Merge lp:~widelands-dev/widelands/test-ngettext into lp:widelands
- test-ngettext
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 7963 |
Proposed branch: | lp:~widelands-dev/widelands/test-ngettext |
Merge into: | lp:widelands |
Diff against target: |
184 lines (+46/-10) 12 files modified
data/tribes/buildings/productionsites/barbarians/coalmine/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/coalmine_deep/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/coalmine_deeper/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/goldmine_deep/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/goldmine_deeper/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/ironmine_deep/helptexts.lua (+1/-1) data/tribes/buildings/productionsites/barbarians/ironmine_deeper/helptexts.lua (+1/-1) data/tribes/scripting/help/global_helptexts.lua (+4/-0) src/scripting/lua_globals.cc (+4/-2) test/maps/lua_testsuite.wmf/scripting/gettext.lua (+29/-0) test/maps/lua_testsuite.wmf/scripting/init.lua (+1/-0) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/test-ngettext |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Klaus Halfmann | compile / regression / review / check f1-help | Approve | |
Miroslav Remák | Approve | ||
GunChleoc | Needs Resubmitting | ||
Review via email: mp+291587@code.launchpad.net |
Commit message
Revised Lua ngettext to allow only unsigned integers. Created test for lua gettext functions.
Description of the change
We missed fixing ngettext when updating eris, so I've created a test.
Miroslav Remák (miroslavr256) wrote : | # |
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1000. State: passed. Details: https:/
Appveyor build 833. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Having more tests is always good and the code looks OK for me.
test-ngettext klaus$ ./regression_
OTOH Miroslav question is valid, where or when do we need to show someting like
"You have 3.145972 Item(plurals) in you Inventory"
?
GunChleoc (gunchleoc) wrote : | # |
An example:
"If the food supply is steady, this mine can produce iron ore in %s on average."
%s is replaced by "%d second(s)", and %d can become floating point.
The floor is what we have always had implicitly (and it also happens to be what my particular language does), so there is no change in functionality. What we really want is a floating-point version of ngettext, but that will be some work.
Miroslav Remák (miroslavr256) wrote : | # |
According to the gettext manual, "a simple gettext call with a form suitable for all values will do"[1] -- something like http://
Flooring produces incorrect results for Slovak and I bet a lot of other languages too, including English. Actually, assuming the information in the CLDR table is correct (and that my understanding of it is correct), Scottish Gaelic suffers as well.
I haven't really looked into it, but I think a fully-featured floating-point version of ngettext wouldn't be possible without writing our own i18n system/forking gettext. We'd probably need to extend the plural expression format and who knows if/how that would work with Transifex.
I'd really like to know how other projects deal with this issue, that is if they care at all. Perhaps we'd be better off using an abbreviation or a universal unit symbol, like "%d s", for the time being.
[1] http://
[2] http://
GunChleoc (gunchleoc) wrote : | # |
The actual rule for Scottish Gaelic is to discard everything after the ., because we say "x seconds point y" for x.y seconds.
Not having things correct for Slovak doesn't please me though.
Maybe we could write a wrapper that will map floating point numbers to the appropriate integer numbers for the languages in our list, and then use those integers to call ngettext?
The alternative would be not to use any placeholders in these strings - that would increase the translation effort though. For example, we have 6 different types of Iron Mine, which currently can use the same string for their productivity texts. So, without the placeholders we get 6x the translation effort, plus if the calculation should change in the future, the strings would need to be retranslated.
Miroslav Remák (miroslavr256) wrote : | # |
> Maybe we could write a wrapper that will map floating point numbers to the
> appropriate integer numbers for the languages in our list, and then use those
> integers to call ngettext?
That wouldn't work for Slovak, because floats need their own, different form. There is nothing to map to.
> The alternative would be not to use any placeholders in these strings - that
> would increase the translation effort though. For example, we have 6 different
> types of Iron Mine, which currently can use the same string for their
> productivity texts. So, without the placeholders we get 6x the translation
> effort, plus if the calculation should change in the future, the strings would
> need to be retranslated.
That doesn't sound like it's worth it, IMO, especially when we can simply use the symbol of the second. I'm not completely opposed to it, though -- it does get the job done.
GunChleoc (gunchleoc) wrote : | # |
Floating point numbers can also become integers - how about we extend the %s second(s) translations to the actual numbers? On the other hand, we also have a translation memory with fuzzy matches now, so pulling similar strings from the Suggestions tab in Transifex shouldn't take that long...
Miroslav Remák (miroslavr256) wrote : | # |
> Floating point numbers can also become integers
I don't see how that is relevant, what's it in response to?
Klaus Halfmann (klaus-halfmann) wrote : | # |
Natural Languages normally have no idea about floating point numbers. But they can use fractions (will be ready in half a minute, can be done in a quarte of an hour). Some progress messages use phrases like "less then 5 seconds remaining" or "estimating 20 min for the Installation".
So we should provide a well defined and _known_ way to handle floats but encourage the programmers to use some of the above mentioned, or better, ideas.
So we might use floor _or_ round but only one of these and then stick with it.
So we should make this a tranlators hint on the homepage and merge this branch :-).
GunChleoc (gunchleoc) wrote : | # |
> > Floating point numbers can also become integers
> I don't see how that is relevant, what's it in response to?
Let's say the value is currently 25.5 - then you would need to translate "%d seconds" with the floating point translation in Slovak. If the value changes to 25, you would need to change your translation - but how would you know? How would a programmer who is not aware of the issue know to give you a hint?
> Natural Languages normally have no idea about floating point numbers. But they
> can use fractions (will be ready in half a minute, can be done in a quarte of
> an hour). Some progress messages use phrases like "less then 5 seconds
> remaining" or "estimating 20 min for the Installation".
Not true. I can say "twenty-five point six seconds" just fine in any natural language on the planet. And Miroslav just stated that his language has special plural forms for floating point numbers ;)
To give you some examples. English and German will simply use the plural, Gaelic will say "five over twenty second point six" (the number 20 takes the singular, so it's "second", not "seconds")
Or maybe I'm just using too much linguist-speak here - "natural language" it meant as opposed to artificial language - i.e. Russian, not C++. It does not mean "what sounds more natural to you, how would you say it naturally?"
> So we should provide a well defined and _known_ way to handle floats but
> encourage the programmers to use some of the above mentioned, or better,
> ideas.
>
> So we might use floor _or_ round but only one of these and then stick with it.
The problem here is that it can give incorrect translations for some languages. There is nothing more annoying for translator than having grammar errors forced on them by a piece of software they're translating. It's the translator who will get blamed for it by the user, and not being able to fix it is very frustrating. I have been down that road many times.
> So we should make this a tranlators hint on the homepage and merge this branch
> :-).
I'd rather have consensus on what we want to do first - this is "just" a bunch of tests, so there is no hurry merging this.
Miroslav Remák (miroslavr256) wrote : | # |
I fully agree with GunChleoc regarding Klaus' post. In Slovak we would say "twenty-five whole (ones) (and) six tenths of a second" and write "25,6 sekundy", where "sekundy" is the genitive case form of the word second. That's why a special plural form is needed.
As for the merge request, I will sum up my stance:
- Ngettext should not accept a float parameter. Scripters are falsely led to believe that it will be handled correctly for every language.
- To fix existing usages, instead of ngettext we should use a simple gettext (_) call with a symbol/abbreviation for the unit, or not use formatting placeholders at all, as long as the number is static. I am leaning towards the first option because it is easier to maintain.
> Let's say the value is currently 25.5 - then you would need to translate "%d
> seconds" with the floating point translation in Slovak. If the value changes
> to 25, you would need to change your translation - but how would you know? How
> would a programmer who is not aware of the issue know to give you a hint?
I am aware of that. This is the issue we're trying to solve/work around here. I thought you were replying to my previous post...
GunChleoc (gunchleoc) wrote : | # |
Because the units appear in a longer sentence, I am in favour of having long unit names rather than symbols. Let's disallow the float and remove the placeholders. I can do that in his branch.
Miroslav Remák (miroslavr256) wrote : | # |
Ok, sounds good to me.
GunChleoc (gunchleoc) wrote : | # |
Done :)
Klaus Halfmann (klaus-halfmann) wrote : | # |
Fine for me.
GunChleoc (gunchleoc) wrote : | # |
Thanks!
@bunnybot merge
Preview Diff
1 | === modified file 'data/tribes/buildings/productionsites/barbarians/coalmine/helptexts.lua' |
2 | --- data/tribes/buildings/productionsites/barbarians/coalmine/helptexts.lua 2015-12-02 04:01:09 +0000 |
3 | +++ data/tribes/buildings/productionsites/barbarians/coalmine/helptexts.lua 2016-04-15 17:35:34 +0000 |
4 | @@ -21,5 +21,5 @@ |
5 | |
6 | function building_helptext_performance() |
7 | -- TRANSLATORS: Performance helptext for a building |
8 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in %s on average."):bformat(ngettext("%d second", "%d seconds", 32.5):bformat(32.5)) |
9 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in 32.5 seconds on average.") |
10 | end |
11 | |
12 | === modified file 'data/tribes/buildings/productionsites/barbarians/coalmine_deep/helptexts.lua' |
13 | --- data/tribes/buildings/productionsites/barbarians/coalmine_deep/helptexts.lua 2015-12-02 04:01:09 +0000 |
14 | +++ data/tribes/buildings/productionsites/barbarians/coalmine_deep/helptexts.lua 2016-04-15 17:35:34 +0000 |
15 | @@ -21,5 +21,5 @@ |
16 | |
17 | function building_helptext_performance() |
18 | -- TRANSLATORS: Performance helptext for a building |
19 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in %s on average."):bformat(ngettext("%d second", "%d seconds", 19.5):bformat(19.5)) |
20 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in 19.5 seconds on average.") |
21 | end |
22 | |
23 | === modified file 'data/tribes/buildings/productionsites/barbarians/coalmine_deeper/helptexts.lua' |
24 | --- data/tribes/buildings/productionsites/barbarians/coalmine_deeper/helptexts.lua 2015-12-02 04:01:09 +0000 |
25 | +++ data/tribes/buildings/productionsites/barbarians/coalmine_deeper/helptexts.lua 2016-04-15 17:35:34 +0000 |
26 | @@ -21,5 +21,5 @@ |
27 | |
28 | function building_helptext_performance() |
29 | -- TRANSLATORS: Performance helptext for a building |
30 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in %s on average."):bformat(ngettext("%d second", "%d seconds", 14.4):bformat(14.4)) |
31 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce coal in 14.4 seconds on average.") |
32 | end |
33 | |
34 | === modified file 'data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/helptexts.lua' |
35 | --- data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/helptexts.lua 2015-12-02 04:01:09 +0000 |
36 | +++ data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/helptexts.lua 2016-04-15 17:35:34 +0000 |
37 | @@ -21,5 +21,5 @@ |
38 | |
39 | function building_helptext_performance() |
40 | -- TRANSLATORS: Performance helptext for a building |
41 | - return pgettext("barbarians_building", "The gamekeeper pauses %s before going to work again."):bformat(ngettext("%d second", "%d seconds", 52.5):bformat(52.5)) |
42 | + return pgettext("barbarians_building", "The gamekeeper pauses 52.5 seconds before going to work again.") |
43 | end |
44 | |
45 | === modified file 'data/tribes/buildings/productionsites/barbarians/goldmine_deep/helptexts.lua' |
46 | --- data/tribes/buildings/productionsites/barbarians/goldmine_deep/helptexts.lua 2015-11-17 09:15:18 +0000 |
47 | +++ data/tribes/buildings/productionsites/barbarians/goldmine_deep/helptexts.lua 2016-04-15 17:35:34 +0000 |
48 | @@ -20,5 +20,5 @@ |
49 | |
50 | function building_helptext_performance() |
51 | -- TRANSLATORS: Performance helptext for a building |
52 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce gold ore in %s on average."):bformat(ngettext("%d second", "%d seconds", 19.5):bformat(19.5)) |
53 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce gold ore in 19.5 seconds on average.") |
54 | end |
55 | |
56 | === modified file 'data/tribes/buildings/productionsites/barbarians/goldmine_deeper/helptexts.lua' |
57 | --- data/tribes/buildings/productionsites/barbarians/goldmine_deeper/helptexts.lua 2015-11-17 09:15:18 +0000 |
58 | +++ data/tribes/buildings/productionsites/barbarians/goldmine_deeper/helptexts.lua 2016-04-15 17:35:34 +0000 |
59 | @@ -20,5 +20,5 @@ |
60 | |
61 | function building_helptext_performance() |
62 | -- TRANSLATORS: Performance helptext for a building |
63 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce gold ore in %s on average."):bformat(ngettext("%d second", "%d seconds", 18.5):bformat(18.5)) |
64 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce gold ore in 18.5 seconds on average.") |
65 | end |
66 | |
67 | === modified file 'data/tribes/buildings/productionsites/barbarians/ironmine_deep/helptexts.lua' |
68 | --- data/tribes/buildings/productionsites/barbarians/ironmine_deep/helptexts.lua 2015-11-08 11:49:22 +0000 |
69 | +++ data/tribes/buildings/productionsites/barbarians/ironmine_deep/helptexts.lua 2016-04-15 17:35:34 +0000 |
70 | @@ -20,5 +20,5 @@ |
71 | |
72 | function building_helptext_performance() |
73 | -- TRANSLATORS: Performance helptext for a building |
74 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce iron ore in %s on average."):bformat(ngettext("%d second", "%d seconds", 39.5):bformat(39.5)) |
75 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce iron ore in 39.5 seconds on average.") |
76 | end |
77 | |
78 | === modified file 'data/tribes/buildings/productionsites/barbarians/ironmine_deeper/helptexts.lua' |
79 | --- data/tribes/buildings/productionsites/barbarians/ironmine_deeper/helptexts.lua 2015-11-08 11:49:22 +0000 |
80 | +++ data/tribes/buildings/productionsites/barbarians/ironmine_deeper/helptexts.lua 2016-04-15 17:35:34 +0000 |
81 | @@ -20,5 +20,5 @@ |
82 | |
83 | function building_helptext_performance() |
84 | -- TRANSLATORS: Performance helptext for a building |
85 | - return pgettext("barbarians_building", "If the food supply is steady, this mine can produce iron ore in %s on average."):bformat(ngettext("%d second", "%d seconds", 17.6):bformat(17.6)) |
86 | + return pgettext("barbarians_building", "If the food supply is steady, this mine can produce iron ore in 17.6 seconds on average.") |
87 | end |
88 | |
89 | === modified file 'data/tribes/scripting/help/global_helptexts.lua' |
90 | --- data/tribes/scripting/help/global_helptexts.lua 2015-11-20 16:39:38 +0000 |
91 | +++ data/tribes/scripting/help/global_helptexts.lua 2016-04-15 17:35:34 +0000 |
92 | @@ -50,6 +50,7 @@ |
93 | -- .. function:: format_seconds(seconds) |
94 | -- |
95 | -- :arg seconds: number of seconds |
96 | +-- :type seconds: An unsigned integer |
97 | -- |
98 | -- Returns a localized string to tell the time in seconds with the proper plural form. |
99 | -- :returns: "1 second", or "20 seconds" etc. |
100 | @@ -62,6 +63,7 @@ |
101 | -- .. function:: format_minutes(minutes) |
102 | -- |
103 | -- :arg minutes: number of minutes |
104 | +-- :type minutes: An unsigned integer |
105 | -- |
106 | -- Returns a localized string to tell the time in minutes with the proper plural form. |
107 | -- :returns: "1 minute", or "20 minutes" etc. |
108 | @@ -74,7 +76,9 @@ |
109 | -- .. function:: format_minutes_seconds(minutes, seconds) |
110 | -- |
111 | -- :arg minutes: number of minutes |
112 | +-- :type minutes: An unsigned integer |
113 | -- :arg seconds: number of seconds |
114 | +-- :type seconds: An unsigned integer |
115 | -- |
116 | -- Returns a localized string to tell the time in minutes and seconds with the proper plural form. |
117 | -- :returns: "1 minute and 20 seconds" etc. |
118 | |
119 | === modified file 'src/scripting/lua_globals.cc' |
120 | --- src/scripting/lua_globals.cc 2016-04-11 08:00:46 +0000 |
121 | +++ src/scripting/lua_globals.cc 2016-04-15 17:35:34 +0000 |
122 | @@ -176,12 +176,14 @@ |
123 | |
124 | :returns: The translated string. |
125 | */ |
126 | -// UNTESTED |
127 | static int L_ngettext(lua_State * L) { |
128 | // S: msgid msgid_plural n |
129 | const char* msgid = luaL_checkstring(L, 1); |
130 | const char* msgid_plural = luaL_checkstring(L, 2); |
131 | - const uint32_t n = floor(luaL_checkdouble(L, 3)); |
132 | + const int32_t n = luaL_checkint32(L, 3); |
133 | + if (n < 0) { |
134 | + report_error(L, "Call to ngettext with negative number %d", n); |
135 | + } |
136 | |
137 | lua_getglobal(L, "__TEXTDOMAIN"); |
138 | if (!lua_isnil(L, -1)) { |
139 | |
140 | === added file 'test/maps/lua_testsuite.wmf/scripting/gettext.lua' |
141 | --- test/maps/lua_testsuite.wmf/scripting/gettext.lua 1970-01-01 00:00:00 +0000 |
142 | +++ test/maps/lua_testsuite.wmf/scripting/gettext.lua 2016-04-15 17:35:34 +0000 |
143 | @@ -0,0 +1,29 @@ |
144 | +test_descr = lunit.TestCase("Gettext Test") |
145 | + |
146 | +-- Since we run the test suite in English, this will only test |
147 | +-- whether gettext functions crash or not |
148 | + |
149 | +function test_descr:test_gettext() |
150 | + set_textdomain("tribes") |
151 | + assert_equal("foo", _"foo") |
152 | + assert_equal("foo", _("foo")) |
153 | + assert_equal(_"Carrier", _("Carrier")) |
154 | +end |
155 | + |
156 | +function test_descr:test_ngettext() |
157 | + assert_equal("foo", ngettext("foo", "bar", 1)) |
158 | + assert_equal("bar", ngettext("foo", "bar", 1000)) |
159 | + assert_equal("1 foo", ngettext("%1% foo", "bar %1%", 1):bformat(1)) |
160 | + assert_equal("bar 2", ngettext("%1% foo", "bar %1%", 2):bformat(2)) |
161 | + -- ngettext is undefined for floating point or negative numbers. |
162 | + assert_error("Call to ngettext with floating point", function() |
163 | + ngettext("foo", "bar", 1.5) |
164 | + end) |
165 | + assert_error("Call to ngettext with negative number", function() |
166 | + ngettext("foo", "bar", -1) |
167 | + end) |
168 | +end |
169 | + |
170 | +function test_descr:test_pgettext() |
171 | + assert_equal("bar", pgettext("foo", "bar")) |
172 | +end |
173 | |
174 | === modified file 'test/maps/lua_testsuite.wmf/scripting/init.lua' |
175 | --- test/maps/lua_testsuite.wmf/scripting/init.lua 2016-01-25 18:50:54 +0000 |
176 | +++ test/maps/lua_testsuite.wmf/scripting/init.lua 2016-04-15 17:35:34 +0000 |
177 | @@ -23,6 +23,7 @@ |
178 | -- ================================= |
179 | include "map:scripting/egbase.lua" |
180 | |
181 | +include "map:scripting/gettext.lua" |
182 | include "map:scripting/math_random.lua" |
183 | include "map:scripting/string_bformat.lua" |
184 | include "map:scripting/path.lua" |
I'm not a fan of flooring the number in our C++ code. Passing a non-integer value to ngettext from Lua should result in an error (no idea what crashes you're talking about), IMO.
Where do we need to pass floats?