GNOME Bugzilla – Bug 149586
String composition is bad in battstat applet
Last modified: 2004-12-22 21:47:04 UTC
String composition like below is VERY WRONG. One simply cannot translate this appropriately. #. Translation Note: as in "No time remaining" or "No time till charged" #: battstat/battstat_applet.c:365 msgid "No time" #. Translation Note: as in "Unknown time remaining" or "Unknown time till charged" #. Translation Note: as in "Unknown time remaining" #: battstat/battstat_applet.c:368 battstat/battstat_applet.c:1145 msgid "Unknown time" This seems to be used in places like: #: battstat/battstat_applet.c:818 battstat/battstat_applet.c:1197 #, c-format msgid "System is running on AC power. %s (%d%%) till charged." You should probably have these in separate messages: "Unknown time (%d%%) till charged." "No time (%d%%) till charged." "%s (%d%%) till charged." "Unknown time (%d%%) remaining." "No time (%d%%) remaining." "%s (%d%%) remaining." Otherwise, there's no chance to translate this in some languages without getting obscure sentenses.
Mmm, I appreciate why this sucks. I want to try and fix it, at the moment I feel there are too many almost identical strings in the applet anyway. The problem is, this is probably going to require some structural changes, I'll try and get it done before the string freeze.
Created attachment 30329 [details] [review] Fix strings in battery status applets for L10N How about this patch? Untested, and would require a bit of cleaning up the comments. 2004-08-08 Danilo Šegan <dsegan@gmx.net> Fix bug #149586. * battstat_applet.c (get_remaining): Add batstatus and remaining parameters. (pixmap_timeout, change_orient): Adjust calls to get_remaining().
Just a note. Even my solution would cause problems in handling plural forms for some languages. For instance, "remaining" (in Serbian) should have a different form if we have "21 hours" and another form if we have "4 hours". I can get around this using a form which is uncommon for modern-Serbian, but it would still be better if we had something like: /* %s is replaced with something like "%d minutes" */ g_strdup_printf( ngettext("%d hour %s (%d%%) remaining", "%d hours %s remaining", hour), hour, min_string, batstatus ); But, I'm not sure if it would work fine for all languages (i.e. in some languages form of "remaining" may depend on the number of minutes, instead of number of hours as in Serbian). Of course, all of this applies equally to "till charged" constructs.
Another note about the patch: those strings with "\n" at the end should probably be changed slightly: remove \n from them, and add ,"\n" after _() call (inside g_strconcat call), so they would get reused (there're already exactly the same strings elsewhere in the code).
I had a bit of a brain wave about this. Rather then adding things to parse to the get_remaining () call, you may as well pass the entire struct. This way the call can pass back a much more intelligent string. This allows us to deal with a lot of edge cases where we want to say certain things. I've also added global strings to deal with commonly appearing strings meaning you should only need to translate them once. I'm not sure how to deal with your problem of having to use different counters depending on the size of the number. How do others resolve this?
Created attachment 30333 [details] [review] fix for this problem and more Here is the patch I just mentioned. Are there any obvious translation issues here? I'm going to mail this to gnome-i18n to see if they can see any issues.
Ok, so you basically went the route I went above with my patch, just a bit more cleanly ;-) A couple of notes. First of all, I'm not so sure about the following. You're probably avoiding _() here because it returns a pointer to static storage, but you seem to be forgetting to put ac_power_string and dc_power_string inside gettext() call, so these will probably end up untranslated (even though translators translate will them). +char *ac_power_string = N_("System is running on AC power"); +char *dc_power_string = N_("System is running on battery power"); ... + new_label = g_strdup_printf ("%s\n%s", + ac_power_string, + new_string); ... + new_label = g_strdup (dc_power_string); You should put these as gettext(ac_power_string) and gettext(dc_power_string) -- there're more of these. Now, since you're already doing some checks in the code, I guess it would be nice to put entire sentences into ngettext calls: if (hours == 0) { + char *return_string; + return_string = g_strdup_printf (_("%s (%d%%) remaining"), + mins_string, batt_life); This should rather be: if (hours == 0) { + char *return_string; + return_string = g_strdup_printf (ngettext("%d minute (%d%%) remaining", "%d minutes (%d%%) remaining", mins), + mins, batt_life); This is useful because in some languages form of the "remaining" also depends on the number. This also applies to the mins==0 case. Of course, it would be nice if one could easily solve this for hours!=0 and mins!=0 as well, but I don't know if what would work for Serbian would work for other languages. We'd need input from other translators (if it doesn't break it for them, I'd be more than happy to have this as well, since I'd be able to provide perfect Serbian translations ;-). I.e. for Serbian it would be: printf(ngettext("%d hour %s remaining", "%d hours %s remaining", hours), hours, minsstring) while for some other languages it might be printf(ngettext("%s %d minute remaining", "%s %d minutes remaining", mins), hoursstring, mins)
Another comment on: > I've also added global strings to deal with commonly appearing strings meaning > you should only need to translate them once. There's no need to do it this way. Gettext merges all messages which are the same (thus the above [after my patch] talk about separating \n and . from the messages). This means that if you have in the code something like: bla1 = g_strdup(_("This is great")); bla2 = g_strdup_printf("%s\n", _("This is great")); translators would have to translate "This is great" only once (that's gettext feature; or misfeature, when you need to differentiate messages on the context :). No need to hack around with putting this in a variable first, etc.
Created attachment 30353 [details] [review] better fix, perhaps? Ok, I've moved the ngettext calls into the returns to remove the amount of string composition. I wasn't sure how to do it for the double plural sentance, so I've done it the way I think it should be done. I've also moved the N_() calls which you said are wrong to #defines, which I suspect are wrong. Basically, I don't want to have to look at the same string over and over ;) If #defines don't work, I'll use a static inline or something (or just give up and put them back in). Comments appriciated as soon as possible. Thanks.
Created attachment 30359 [details] [review] more cleanups Cleanups, this is what I intend to commit unless someone noticed any more issues before this evening.
Davyd, everything looks fine from my perspective, and I guess everyone can translate it as good as it's possible now. Thanks for investing so much time in this.
Excellent. This is committed then. Thanks for your help.
There is a new problem now. In the string below: #. TRANSLATOR: "%d %s %d %s" are "%d hours %d minutes" #. * Swap order with "%2$s %2$d %1$s %1$d if needed #: battstat/battstat_applet.c:414 #, c-format msgid "%d %s %d %s (%d%%) remaining" the word "remaining", in some languages, may be translated in plural or singular depending on the time remaining. It will be wrong in these languages if there's no support for plurals on it. I don't know how this problem could be properly solved (maybe using ngettext), but I wouldn't like to have reverse word order to translate this string. It would look like "Tempo restante: %d %s %d %s (%d%%)" (or in English "Time remaining: %d %s %d %s (%d%%)") which is not so natural and would break the way the status messages were going up to now.
Raphael, it's the problem I mention above. It cannot be solved without knowing on *what* to pluralize. For Serbian, we'd need to pluralize on hours, but in the message "%d %s %d %s (%d%%) till charged", we'd need it to be pluralized on minutes (that's because word "remaining" would go in front of the time, and "till charged" after it in Serbian translation). So, do you have any magic solution that would work for both cases?
I don't think you can use ngettext for to independantly pluralize two numbers. Would it help if 'remaining' was put into an ngettext of it's own? Making it something like: g_strdup_printf (_("%d %s %d %s (%d%%) %s"), /* strings */ ngettext (remaining, remaining, hours + mins)); Other then that, I'm not sure what to do. This is a hard problem, and a possible limitation of ngettext (although how would you solve it?)
Davyd, that wouldn't help (especially not with it being pluralized on such crap value such as sum of hours and minutes ;-). What might help is doing something like the following. /* Translators: translate this to either "hours" or "mins". "%d %s %d %s remaining" message below will be pluralized on the value you select here. */ char *choice = g_strdup(_("mins")); if (strcmp(choice, "mins")) { remstring = g_strdup(ngettext("%d %s %d %s remaining", "%d %s %d %s remaining", hours)); } else { remstring = g_strdup(ngettext("%d %s %d %s remaining", "%d %s %d %s remaining", mins)); } Then, passing remstring in g_strdup_printf call. This is very hackish, though I believe it will solve the problem for all translators. It's even worse than "hackish", because one would have to provide the same hack for "%d %s %d %s till charged" message, and since "mins" is already used (remember, gettext provides ability to translate this message only once), we'd need to perhaps use "hours" there as a default keyword, and invert the if logic. Of course, you can perhaps use completely different keywords, such as "pluralize-remaining-on: mins" and "pluralize-till-charged-on: mins", remembering to adjust translators' comment and strcmp compare. Because of this foo-for-the-bar nature of the solution (letting translator handle which number should it be pluralized on, since it can be pluralized only on the one), I'm not pushing too hard for it, and I rarely mention it anywhere as a real option to consider. But, it's up to you Davyd, and up to other translators to weight merits and disadvantages of the method.
I haven't had time to look into this bug. It doesn't look like I'll have time to fix it before the release tomorrow. At least it will give me time to think about it ;)
I like Danilo's solution. If it is well commented I think translators will understand. Even I could understand it. I just think it should verify whether the choice string was translated only to "hours" or "mins". If it is other else than those the applet would not pluralize it in hours by default. Backing on my previous post, I asked my mom, who is a Portuguese teacher, and she told me that in Portuguese when the subject is a composed subject as in this case (X hours and Y minutes) it is always pluralized, even if both are singular. Then that problem I exposed is not really a problem in Portuguese, although it still remains in other languages.
If it's not a problem in Portuguese or Serbian, then I don't really want to touch it now. Unless of course, some other translator has a problem. It's something to look at for the next release cycle though.
This bug can probably be closed now.