Merge lp:~widelands-dev/widelands/test-ngettext into lp:widelands

Proposed by GunChleoc
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
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.

To post a comment you must log in.
Revision history for this message
Miroslav Remák (miroslavr256) wrote :

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?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 1000. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/122450845.
Appveyor build 833. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_test_ngettext-833.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Having more tests is always good and the code looks OK for me.

test-ngettext klaus$ ./regression_test.py -b ./widelands

OTOH Miroslav question is valid, where or when do we need to show someting like
"You have 3.145972 Item(plurals) in you Inventory"
?

review: Approve (compile, regressiontest.py +/-)
Revision history for this message
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.

Revision history for this message
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://pastebin.com/raw/R2tGW9XH. The CLDR tables[2] clearly disprove this rule being universal, though.

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://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html (at the end of the page)
[2] http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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...

Revision history for this message
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?

Revision history for this message
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 :-).

Revision history for this message
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.

Revision history for this message
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...

Revision history for this message
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.

Revision history for this message
Miroslav Remák (miroslavr256) wrote :

Ok, sounds good to me.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Done :)

review: Needs Resubmitting
Revision history for this message
Miroslav Remák (miroslavr256) wrote :

LGTM.

review: Approve
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Fine for me.

review: Approve (compile / regression / review / check f1-help)
Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks!

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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"

Subscribers

People subscribed via source and target branches

to status/vote changes: