New i18n interface for KDE 4, second try

Nicolas Goutte nicolasg at snafu.de
Fri Oct 28 01:29:54 BST 2005


On Tuesday 25 October 2005 16:25, Chusslove Illich wrote:
> After some more toying with the code, here is the summary.

As nobody has commented yet, I think that it is my turn. :-)

>
> Just a reminder: the new interface is needed if we want to capture
> arguments which replace the %NN placeholders in an i18n call, and that is
> needed because we want to employ translator-side scripting of some
> grammar-wise nasty messages.
>
> Basic idea was to make a new class derived from QString, which has
> overridden arg() methods. This was discussed a lot, and while it would
> make least impact on existing i18n practices, almost preserving source
> compatibility, it had a need for some hacks (because QString was not
> designed for inheritance). However, due to its non-intrusivity, I feel
> this still to be an option, so I mention it here.
>
> The other feasible idea was to make a new class, say named KTrString, but
> not derived from QString and without any implicit conversions to QString,
> and therefore with a special method to finalize translation, i.e. like:
>
> i18n("Blah, blah: %1 %2").arg(foo).arg(bar).toString();
>
> This is a very clean solution, and it also allows to check at runtime
> whether all arguments have been supplied before call to toString(), so
> that programmer can be warned of possible problem if he does something
> like this:
>
> QString msg = i18n("Blah, blah: %1 %2").toString();
> msg = msg.arg(foo).arg(bar);
>
> However, since the mandatory extra toString(), even when no arguments are
> present, is kind of stupid, there can also exist a set of simple templates
> which mimick variable argument list, so that this can be used:
>
> i18n("Blah, blah: %1 %2", foo, bar); // ordinary
> i18nc("This is for...", "Blah, blah: %1 %2", foo, bar); // context
> i18np("One thingy with %1", "%n thingies with %1", n, whatnot); // plural

I would be careful such a solution.

First it is going back into printf and friends. Probably you will force the 
parameters to be strings to avoid printf-like problems but then it makes the 
solution less flexible than QString::arg which can have multiple kinds of 
parameters.

If we choose this way, I suppose that clear names are needed, like 
i18n_context and i18n_plural

The similarity between these 3 functions' names makes that a missed letter or 
a typo, makes the result in the PO(T) file quite different. That could lead 
to a nightmare for translators if there are many of such entries to be 
corrected (even more if it is at message-freeze time).

>

Note if it is QString::arg which makes problems, so that you do not want a 
KI18N::arg, then perhaps give it another name like KI18n::param:

i18n("Foo %1").param("Bar");
i18n_plural("A Foo %1", "%n Foos %1", count).param("Bar");
i18n_context("FooContext", "Foo %1").param("Bar");

This would have the advantage to give compile errors if the name does not 
match the number of parameters. Or it allows to keep the i18n() for all three 
names.

> where i18n[cp]() templates would look like:
>
> class KTrString {
>   ...
>   template <typename A1, ..., typename An>
>   friend i18n (const char *text, const A1 &a1, ..., const An &an) {
>     KTrString(text).arg(a1).....arg(an).toString();

Note: I would avoid Tr in the name, as we are using Gettext and not Qt's 
translation system.

>   }
>   ...
>   template <typename A1, ..., typename An>
>   friend i18nc (const char *context, const char *text,
>                 const A1 &a1, ..., const An &an) {
>     KTrString(context, text).arg(a1).....arg(an).toString();
>   }
>   ...
>   template <typename A1, ..., typename An>
>   friend i18np (const char *singular, const char *plural, int n,
>                 const A1 &a1, ..., const An &an) {
>     KTrString(singular, plural, n).arg(a1).....arg(an).toString();
>   }
>   ...
> }
>
> Templates would be declared up to some limited number of arguments, so over
> that, one would have to use ...arg()...toString() syntax, with special
> ki18n() calls which would really return a KTrString. But these are very,
> very rare - I found seven messages with more than %8, two with more than
> %10.

But what is the drawback (code length, time) to always handle 10 QString 
objects? 

(Qt has defined at QString::arg for at most 4 QStrings, so probably there is a 
reason.)

>
> The other case when one would want to use ki18n[pc]() is if split is really
> convenient, like:
>
> KTrString msg = ki18n("Blah, blah: %1");
> ...
> wiggy->doSomething(msg.arg(foo).toString());
>
> (But this practice should anyway be discouraged.)
>

> Additional nice thing with the templates is that extra .arg(...) sugar can
> be dropped.

That is what I do not like, especially as it limits the flexiblity of the 
parameters.

>
> I just don't know if some obscure compilers would choke with required
> template instantiations.
>

> I have patched together a perl script to convert the current KDE code to
> template formulation. It seems to be doing its job correctly, albeit
> embarrassingly slow :) As a test, in branch 3.5, I've converted and
> compiled libs, base, pim, network, graphics, multimedia and sdk, without
> any (argh, *almost* any) problems.

Good to hear that conversion is rather easy, as if it works with one solution, 
it will work probably with other solutions too.

>

> There was one case when compiler didn't want to instantaniate a template
> when one argument was an unnamed enumeration (I've checked, this is
> according to standard; how many dark corners are there in this language?),

...not counting the dark corners in the compilers...

> and one pathological case in Kontact which had 25 arguments to a
> message :) Yet, this makes even less problems than the inherited solution,
> which requires explicit casts in several places (mostly connected with
> ternary ops)...
>

> As for xgettext support, since recently both approaches are fully supported
> for extraction by xgettext from Gettext CVS, so this element can be
> factored out of consideration.

Yes, sure, as what you are proposing is similar to the printf-handling from 
the interface point of view.

Have a nice day!





More information about the kde-core-devel mailing list