Merge lp:~widelands-dev/widelands/translation_stats into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8475
Proposed branch: lp:~widelands-dev/widelands/translation_stats
Merge into: lp:widelands
Diff against target: 749 lines (+474/-69)
6 files modified
data/i18n/translation_stats.conf (+182/-0)
src/ui_fsmenu/CMakeLists.txt (+1/-0)
src/ui_fsmenu/options.cc (+123/-67)
src/ui_fsmenu/options.h (+16/-2)
utils/merge_and_push_translations.sh (+5/-0)
utils/update_translation_stats.py (+147/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/translation_stats
Reviewer Review Type Date Requested Status
kaputtnik (community) Needs Fixing
GunChleoc Needs Resubmitting
SirVer Approve
Review via email: mp+332029@code.launchpad.net

Commit message

Show translation stats next to the language selection menu and invite translators if a translation is incomplete, with the help of the Translate Toolkit.

- Added a new utils script "update_translation_stats.py" that will write translation statistics to data/i18n/translation_stats.conf.
  This script is now called on every translation pull.
- Added translation stats + invitation to translators to Options.

Description of the change

Some translations have very little work accomplished, so I thought we'd better tell the user so. This also gives us an opportunity to invite translators.

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2702. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/285758712.
Appveyor build 2517. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2517.

Revision history for this message
kaputtnik (franku) wrote :

This is good approach to get more translators, i think.

Translation stats: I guess the count of 'total' is ever the same, because English is the base language. Is it needed to store this value for each language?

The text for RTL languages are messed up somehow. I guess this will be fixed if the string is translated:

"is 0% complete. If you whish to help us translate, please visit [language name] The translation intowidelands.org/wiki/TranlatingWidelands"

But if some one is willing to help translate, he should know English. So i would suggest to keep the English sentence here. Anyway adding "https://" to the underlined words would be helpful to show that this is an internet address.

Revision history for this message
SirVer (sirver) wrote :

code lgtm, a few nits.

not tested.

review: Approve
Revision history for this message
GunChleoc (gunchleoc) wrote :

Answers to SirVer's comments in-line.

> Translation stats: I guess the count of 'total' is ever the same, because English is the base language. Is it needed to store this value for each language?

You are right, I should refactor that.

> The text for RTL languages are messed up somehow. I guess this will be fixed if the string is translated: <snip>

This is an issue with the BiDi support in the font renderer, so I'd rather not change this just to hide the bug. And there are actually translators who don't speak English that well - I was recently at a localizer's meeting and having a language other than English in the translation tool's UI was a desired feature for some of them.

Revision history for this message
SirVer (sirver) wrote :

more comments inline.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I just pushed another update, where I write the total only once as suggested by Kaputtnik. I still need to clean up the Python script incl. the regex.

Revision history for this message
SirVer (sirver) :
Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for the comment, I mixed something up there. Will replace the vector with a map and get rid of the std::sort call.

Revision history for this message
GunChleoc (gunchleoc) wrote :

This should be ready now.

I left the data container in Python as it is with the total for each locale, because the entires come in per directory rather than per locale. So, I sacrificed some memory in favour of code readability + performance.

review: Needs Resubmitting
Revision history for this message
kaputtnik (franku) wrote :

I have run the update_translation_stats.py:

Looks like this could be run only with python2. Maybe add a comment?

I got a list out of range error:

$ > python2 utils/update_translation_stats.py
Fetching translation stats .......................

Locale Total Translated
------ ----- ----------
Something went wrong:
Traceback (most recent call last):
  File "utils/update_translation_stats.py", line 118, in main
    result = generate_translation_stats(po_dir, output_file)
  File "utils/update_translation_stats.py", line 97, in generate_translation_stats
    result = result + 'total=' + str(locale_stats[locale_stats.keys()[0]].total) + '\n\n'
IndexError: list index out of range

Do i miss something?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2722. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/295046876.
Appveyor build 2534. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2534.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for pointing that out - there are multiple things in that script that won't work with Python 3 and a fix isn't trivial, so I've changed the Python header.

Revision history for this message
kaputtnik (franku) wrote :

The regex doesn't match here and the match is always 'None', resulting in the List out of range error.

Don't know how to fix the regex... but maybe adding an else clause? See diff comment.

review: Needs Fixing
Revision history for this message
GunChleoc (gunchleoc) wrote :

It fails for me with Python3 too, but not with Python2. Good idea about the error handling though. I've added some :)

Revision history for this message
kaputtnik (franku) wrote :

Just want to mention that i did run the file with python2 :-) This is what i get now:

$:> python2 utils/update_translation_stats.py
Fetching translation stats .
ERROR: Invalid line in pocount output:
/home/kaputtnik/Quellcode/widelands-repo/translation_stats/po/map_the_green_plateau.wmf/la.po source words: total: 438 | 438t 0f 0u | 100.0%t 0.0%f 0.0%u

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2729. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/295908441.
Appveyor build 2541. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2541.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Looks like your version of pocount produces slightly different output. Can you please try again and double-check if the resulting conf looks OK?

Revision history for this message
kaputtnik (franku) wrote :

My version of translate-toolkit is 2.2.5-1.

Pulled your changes but no change:

$:> python2 utils/update_translation_stats.py
Fetching translation stats .
ERROR: Invalid line in pocount output:
/home/kaputtnik/Quellcode/widelands-repo/translation_stats/po/map_the_green_plateau.wmf/la.po source words: total: 438 | 438t 0f 0u | 100.0%t 0.0%f 0.0%u

I have tried to print the line showing escape sequences by adjusting line 106 like "...line.encode('string_escape'))" and the result is:

$:> python2 utils/update_translation_stats.py
Fetching translation stats .
ERROR: Invalid line in pocount output:
\x1b[95m/home/kaputtnik/Quellcode/widelands-repo/translation_stats/po/map_the_green_plateau.wmf/la.po\x1b[0m source words: total: 438\t| \x1b[92m438\x1b[0mt\t\x1b[93m0\x1b[0mf\t\x1b[91m0\x1b[0mu\t| \x1b[92m100.0%\x1b[0mt\t\x1b[93m0.0%\x1b[0mf\t\x1b[91m0.0%\x1b[0mu

So tabs are there plus escape sequences showing colored output.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I am now testing for non-Word characters rather than tabs or spaces. Does that work for you?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2743. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/296669731.
Appveyor build 2555. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2555.

Revision history for this message
kaputtnik (franku) wrote :

No, it doesn't work :(

Whereas the version of pocount used by me has an option '--no-color', it is maybe better to use another pocount output format like --csv in combination with python csv module?

https://docs.python.org/2/library/csv.html?highlight=csv#module-csv

I guess the csv format provided by pocount will not change when switching the version of translate-toolkit. So the script might get more stable without using a fragile regexp.

For comparison her is the output with option --csv on my machine:

$:> pocount --csv po/map_the_green_plateau.wmf/la.po
Filename, Translated Messages, Translated Source Words, Translated Target Words, Fuzzy Messages, Fuzzy Source Words, Untranslated Messages, Untranslated Source Words, Total Message, Total Source Words, Review Messages, Review Source Words
po/map_the_green_plateau.wmf/la.po, 25, 438, 283, 0, 0, 0, 0, 25, 438

Revision history for this message
GunChleoc (gunchleoc) wrote :

Much better! No idea why I didn't use the csv format in the first place.

Revision history for this message
kaputtnik (franku) wrote :

Now its working :-)

Could you please add 'https://' to the underlined link for "...please visit widelands.org/wiki/TranslatinWidelands"

Revision history for this message
GunChleoc (gunchleoc) wrote :

I actually removed that https on purpose, it adds visual clutter and makes the string too long to fir.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2746. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/296921592.
Appveyor build 2558. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2558.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I gave it another try, adding https:// results in a string truncation, so I'm leaving it out.

I found another bug where I was parsing the wrong column, so I've added some error handling and am parsing the header info.

Thanks for all your help in debugging this thing!

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2754. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/297152789.
Appveyor build 2566. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_translation_stats-2566.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/i18n/translation_stats.conf'
2--- data/i18n/translation_stats.conf 1970-01-01 00:00:00 +0000
3+++ data/i18n/translation_stats.conf 2017-11-04 09:41:54 +0000
4@@ -0,0 +1,182 @@
5+[global]
6+total=44699
7+
8+[ar]
9+translated=8657
10+
11+[ast]
12+translated=469
13+
14+[bg]
15+translated=44699
16+
17+[br]
18+translated=422
19+
20+[ca]
21+translated=44689
22+
23+[cs]
24+translated=43115
25+
26+[da]
27+translated=42285
28+
29+[de]
30+translated=44689
31+
32+[el]
33+translated=491
34+
35+[en_CA]
36+translated=546
37+
38+[en_GB]
39+translated=41937
40+
41+[en_US]
42+translated=455
43+
44+[eo]
45+translated=4713
46+
47+[es]
48+translated=44288
49+
50+[et]
51+translated=575
52+
53+[eu]
54+translated=1338
55+
56+[fa]
57+translated=115
58+
59+[fi]
60+translated=44689
61+
62+[fr]
63+translated=44699
64+
65+[ga]
66+translated=0
67+
68+[gd]
69+translated=42984
70+
71+[gl]
72+translated=10382
73+
74+[he]
75+translated=420
76+
77+[hi]
78+translated=26
79+
80+[hr]
81+translated=3172
82+
83+[hu]
84+translated=33179
85+
86+[ia]
87+translated=126
88+
89+[id]
90+translated=31
91+
92+[it]
93+translated=36868
94+
95+[ja]
96+translated=6944
97+
98+[jv]
99+translated=22
100+
101+[ka]
102+translated=4
103+
104+[ko]
105+translated=24920
106+
107+[krl]
108+translated=103
109+
110+[la]
111+translated=3994
112+
113+[lt]
114+translated=202
115+
116+[mr]
117+translated=7
118+
119+[ms]
120+translated=10192
121+
122+[my]
123+translated=30
124+
125+[nb]
126+translated=9055
127+
128+[nds]
129+translated=874
130+
131+[nl]
132+translated=33794
133+
134+[nn]
135+translated=2845
136+
137+[oc]
138+translated=193
139+
140+[pl]
141+translated=41731
142+
143+[pt]
144+translated=32884
145+
146+[pt_BR]
147+translated=14496
148+
149+[ro]
150+translated=1105
151+
152+[ru]
153+translated=43920
154+
155+[rw]
156+translated=48
157+
158+[si]
159+translated=283
160+
161+[sk]
162+translated=39387
163+
164+[sl]
165+translated=1010
166+
167+[sr]
168+translated=177
169+
170+[sv]
171+translated=41420
172+
173+[tr]
174+translated=2030
175+
176+[uk]
177+translated=4161
178+
179+[vi]
180+translated=1197
181+
182+[zh_CN]
183+translated=2202
184+
185+[zh_TW]
186+translated=641
187
188=== modified file 'src/ui_fsmenu/CMakeLists.txt'
189--- src/ui_fsmenu/CMakeLists.txt 2017-05-14 20:06:48 +0000
190+++ src/ui_fsmenu/CMakeLists.txt 2017-11-04 09:41:54 +0000
191@@ -10,6 +10,7 @@
192 graphic_fonthandler
193 graphic_text
194 graphic_text_constants
195+ graphic_text_layout
196 helper
197 io_filesystem
198 logic_constants
199
200=== modified file 'src/ui_fsmenu/options.cc'
201--- src/ui_fsmenu/options.cc 2017-09-11 16:59:41 +0000
202+++ src/ui_fsmenu/options.cc 2017-11-04 09:41:54 +0000
203@@ -36,6 +36,7 @@
204 #include "graphic/text/bidi.h"
205 #include "graphic/text/font_set.h"
206 #include "graphic/text_constants.h"
207+#include "graphic/text_layout.h"
208 #include "helper.h"
209 #include "io/filesystem/layered_filesystem.h"
210 #include "logic/constants.h"
211@@ -47,23 +48,6 @@
212
213 namespace {
214
215-// Data model for the entries in the language selection list.
216-struct LanguageEntry {
217- LanguageEntry(const std::string& init_localename,
218- const std::string& init_descname,
219- const std::string& init_sortname)
220- : localename(init_localename), descname(init_descname), sortname(init_sortname) {
221- }
222-
223- bool operator<(const LanguageEntry& other) const {
224- return sortname < other.sortname;
225- }
226-
227- std::string localename; // ISO code for the locale
228- std::string descname; // Native language name
229- std::string sortname; // ASCII Language name used for sorting
230-};
231-
232 // Locale identifiers can look like this: ca_ES@valencia.UTF-8
233 // The contents of 'selected_locale' will be changed to match the 'current_locale'
234 void find_selected_locale(std::string* selected_locale, const std::string& current_locale) {
235@@ -120,21 +104,22 @@
236 // Tabs
237 tabs_(this, g_gr->images().get("images/ui_basic/but1.png"), UI::TabPanel::Type::kBorder),
238
239- box_interface_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
240+ box_interface_(&tabs_, 0, 0, UI::Box::Horizontal, 0, 0, padding_),
241+ box_interface_left_(&box_interface_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
242 box_windows_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
243 box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
244 box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
245 box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
246
247 // Interface options
248- language_dropdown_(&box_interface_,
249+ language_dropdown_(&box_interface_left_,
250 0,
251 0,
252 100, // 100 is arbitrary, will be resized in layout().
253 100, // 100 is arbitrary, will be resized in layout().
254 24,
255 _("Language")),
256- resolution_dropdown_(&box_interface_,
257+ resolution_dropdown_(&box_interface_left_,
258 0,
259 0,
260 100, // 100 is arbitrary, will be resized in layout().
261@@ -142,10 +127,10 @@
262 24,
263 _("In-game resolution")),
264
265- fullscreen_(&box_interface_, Vector2i::zero(), _("Fullscreen"), "", 0),
266- inputgrab_(&box_interface_, Vector2i::zero(), _("Grab Input"), "", 0),
267-
268- sb_maxfps_(&box_interface_, 0, 0, 0, 0, opt.maxfps, 0, 99, _("Maximum FPS:")),
269+ fullscreen_(&box_interface_left_, Vector2i::zero(), _("Fullscreen"), "", 0),
270+ inputgrab_(&box_interface_left_, Vector2i::zero(), _("Grab Input"), "", 0),
271+ sb_maxfps_(&box_interface_left_, 0, 0, 0, 0, opt.maxfps, 0, 99, _("Maximum FPS:")),
272+ translation_info_(&box_interface_, 0, 0, 100, 100),
273
274 // Windows options
275 snap_win_overlap_only_(
276@@ -232,6 +217,7 @@
277 os_(opt) {
278 // Set up UI Elements
279 title_.set_fontsize(UI_FONT_SIZE_BIG);
280+ translation_info_.force_new_renderer();
281
282 // Buttons
283 button_box_.add(UI::g_fh1->fontset()->is_rtl() ? &ok_ : &cancel_);
284@@ -252,18 +238,14 @@
285 tabs_.activate(os_.active_tab);
286 }
287
288- box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
289- box_windows_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
290- box_sound_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
291- box_saving_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
292- box_game_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
293-
294 // Interface
295- box_interface_.add(&language_dropdown_);
296- box_interface_.add(&resolution_dropdown_);
297- box_interface_.add(&fullscreen_);
298- box_interface_.add(&inputgrab_);
299- box_interface_.add(&sb_maxfps_);
300+ box_interface_.add(&box_interface_left_);
301+ box_interface_.add(&translation_info_, UI::Box::Resizing::kExpandBoth);
302+ box_interface_left_.add(&language_dropdown_);
303+ box_interface_left_.add(&resolution_dropdown_);
304+ box_interface_left_.add(&fullscreen_);
305+ box_interface_left_.add(&inputgrab_);
306+ box_interface_left_.add(&sb_maxfps_);
307
308 // Windows
309 box_windows_.add(&snap_win_overlap_only_);
310@@ -290,6 +272,8 @@
311 box_game_.add(&single_watchwin_);
312
313 // Bind actions
314+ language_dropdown_.selected.connect(
315+ boost::bind(&FullscreenMenuOptions::update_language_stats, this, false));
316 cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));
317 apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this));
318 ok_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_ok, this));
319@@ -360,6 +344,7 @@
320
321 // Language options
322 add_languages_to_list(opt.language);
323+ update_language_stats(true);
324 layout();
325 }
326
327@@ -369,8 +354,7 @@
328 butw_ = get_w() / 5;
329 buth_ = get_h() * 9 / 200;
330 hmargin_ = get_w() * 19 / 200;
331- tab_panel_width_ = get_inner_w() - 2 * hmargin_;
332- column_width_ = tab_panel_width_ - padding_;
333+ int tab_panel_width = get_inner_w() - 2 * hmargin_;
334 tab_panel_y_ = get_h() * 14 / 100;
335
336 // Title
337@@ -382,48 +366,53 @@
338 apply_.set_desired_size(butw_, buth_);
339 ok_.set_desired_size(butw_, buth_);
340 button_box_.set_pos(Vector2i(hmargin_ + butw_ / 3, get_inner_h() - hmargin_));
341- button_box_.set_size(tab_panel_width_ - 2 * butw_ / 3, buth_);
342+ button_box_.set_size(tab_panel_width - 2 * butw_ / 3, buth_);
343
344 // Tabs
345 tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));
346- tabs_.set_size(tab_panel_width_, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);
347+ tabs_.set_size(tab_panel_width, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);
348+
349+ tab_panel_width -= padding_;
350+ const int column_width = tab_panel_width / 2;
351
352 // Interface
353- language_dropdown_.set_desired_size(column_width_ / 2, language_dropdown_.get_h());
354+ box_interface_left_.set_desired_size(column_width + padding_, tabs_.get_inner_h());
355+ box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
356+ language_dropdown_.set_desired_size(column_width, language_dropdown_.get_h());
357 language_dropdown_.set_height(tabs_.get_h() - language_dropdown_.get_y() - buth_ - 3 * padding_);
358- resolution_dropdown_.set_desired_size(column_width_ / 2, resolution_dropdown_.get_h());
359+ resolution_dropdown_.set_desired_size(column_width, resolution_dropdown_.get_h());
360 resolution_dropdown_.set_height(tabs_.get_h() - resolution_dropdown_.get_y() - buth_ -
361 3 * padding_);
362
363- fullscreen_.set_desired_size(column_width_, fullscreen_.get_h());
364- inputgrab_.set_desired_size(column_width_, inputgrab_.get_h());
365- sb_maxfps_.set_unit_width(column_width_ / 4);
366- sb_maxfps_.set_desired_size(column_width_ / 2, sb_maxfps_.get_h());
367+ fullscreen_.set_desired_size(column_width, fullscreen_.get_h());
368+ inputgrab_.set_desired_size(column_width, inputgrab_.get_h());
369+ sb_maxfps_.set_unit_width(column_width / 2);
370+ sb_maxfps_.set_desired_size(column_width, sb_maxfps_.get_h());
371
372 // Windows options
373- snap_win_overlap_only_.set_desired_size(column_width_, snap_win_overlap_only_.get_h());
374- dock_windows_to_edges_.set_desired_size(column_width_, dock_windows_to_edges_.get_h());
375- animate_map_panning_.set_desired_size(column_width_, animate_map_panning_.get_h());
376+ snap_win_overlap_only_.set_desired_size(tab_panel_width, snap_win_overlap_only_.get_h());
377+ dock_windows_to_edges_.set_desired_size(tab_panel_width, dock_windows_to_edges_.get_h());
378+ animate_map_panning_.set_desired_size(tab_panel_width, animate_map_panning_.get_h());
379 sb_dis_panel_.set_unit_width(200);
380- sb_dis_panel_.set_desired_size(column_width_, sb_dis_panel_.get_h());
381+ sb_dis_panel_.set_desired_size(tab_panel_width, sb_dis_panel_.get_h());
382 sb_dis_border_.set_unit_width(200);
383- sb_dis_border_.set_desired_size(column_width_, sb_dis_border_.get_h());
384+ sb_dis_border_.set_desired_size(tab_panel_width, sb_dis_border_.get_h());
385
386 // Sound options
387- music_.set_desired_size(column_width_, music_.get_h());
388- fx_.set_desired_size(column_width_, fx_.get_h());
389- message_sound_.set_desired_size(column_width_, message_sound_.get_h());
390+ music_.set_desired_size(tab_panel_width, music_.get_h());
391+ fx_.set_desired_size(tab_panel_width, fx_.get_h());
392+ message_sound_.set_desired_size(tab_panel_width, message_sound_.get_h());
393
394 // Saving options
395 sb_autosave_.set_unit_width(250);
396- sb_autosave_.set_desired_size(column_width_, sb_autosave_.get_h());
397+ sb_autosave_.set_desired_size(tab_panel_width, sb_autosave_.get_h());
398 sb_rolling_autosave_.set_unit_width(250);
399- sb_rolling_autosave_.set_desired_size(column_width_, sb_rolling_autosave_.get_h());
400- zip_.set_desired_size(column_width_, zip_.get_h());
401- write_syncstreams_.set_desired_size(column_width_, write_syncstreams_.get_h());
402+ sb_rolling_autosave_.set_desired_size(tab_panel_width, sb_rolling_autosave_.get_h());
403+ zip_.set_desired_size(tab_panel_width, zip_.get_h());
404+ write_syncstreams_.set_desired_size(tab_panel_width, write_syncstreams_.get_h());
405
406 // Game options
407- transparent_chat_.set_desired_size(column_width_, transparent_chat_.get_h());
408+ transparent_chat_.set_desired_size(tab_panel_width, transparent_chat_.get_h());
409 }
410
411 void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {
412@@ -432,8 +421,8 @@
413 language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == "");
414 language_dropdown_.add("English", "en", nullptr, current_locale == "en");
415
416- // Add translation directories to the list
417- std::vector<LanguageEntry> entries;
418+ // Add translation directories to the list. Using the LanguageEntries' sortnames as a key for getting a sorted result.
419+ std::map<std::string, LanguageEntry> entries;
420 std::string selected_locale;
421
422 try { // Begin read locales table
423@@ -460,7 +449,9 @@
424
425 std::string name = i18n::make_ligatures(table->get_string("name").c_str());
426 const std::string sortname = table->get_string("sort_name");
427- entries.push_back(LanguageEntry(localename, name, sortname));
428+ LanguageEntry* entry = new LanguageEntry(localename, name);
429+ entries.insert(std::make_pair(sortname, *entry));
430+ language_entries_.insert(std::make_pair(localename, *entry));
431
432 if (localename == current_locale) {
433 selected_locale = current_locale;
434@@ -468,7 +459,7 @@
435
436 } catch (const WException&) {
437 log("Could not read locale for: %s\n", localename.c_str());
438- entries.push_back(LanguageEntry(localename, localename, localename));
439+ entries.insert(std::make_pair(localename, LanguageEntry(localename, localename)));
440 } // End read locale from table
441 } // End scan locales directory
442 } catch (const LuaError& err) {
443@@ -477,11 +468,76 @@
444 } // End read locales table
445
446 find_selected_locale(&selected_locale, current_locale);
447- std::sort(entries.begin(), entries.end());
448- for (const LanguageEntry& entry : entries) {
449- language_dropdown_.add(entry.descname.c_str(), entry.localename, nullptr,
450- entry.localename == selected_locale, "");
451- }
452+ for (const auto& entry : entries) {
453+ const LanguageEntry& language_entry = entry.second;
454+ language_dropdown_.add(language_entry.descname.c_str(), language_entry.localename, nullptr,
455+ language_entry.localename == selected_locale, "");
456+ }
457+}
458+
459+/**
460+ * Updates the language statistics message according to the currently selected locale.
461+ * @param include_system_lang We only want to include the system lang if it matches the Widelands
462+ * locale.
463+ */
464+void FullscreenMenuOptions::update_language_stats(bool include_system_lang) {
465+ int percent = 100;
466+ std::string message = "";
467+ if (language_dropdown_.has_selection()) {
468+ std::string locale = language_dropdown_.get_selected();
469+ // Empty locale means try system locale
470+ if (locale.empty() && include_system_lang) {
471+ std::vector<std::string> parts;
472+ boost::split(parts, i18n::get_locale(), boost::is_any_of("."));
473+ if (language_entries_.count(parts[0]) == 1) {
474+ locale = parts[0];
475+ } else {
476+ boost::split(parts, parts[0], boost::is_any_of("@"));
477+ if (language_entries_.count(parts[0]) == 1) {
478+ locale = parts[0];
479+ } else {
480+ boost::split(parts, parts[0], boost::is_any_of("_"));
481+ if (language_entries_.count(parts[0]) == 1) {
482+ locale = parts[0];
483+ }
484+ }
485+ }
486+ }
487+
488+ // If we have the locale, grab the stats and set the message
489+ if (language_entries_.count(locale) == 1) {
490+ try {
491+ const LanguageEntry& entry = language_entries_[locale];
492+ Profile prof("i18n/translation_stats.conf");
493+ Section& s = prof.get_safe_section("global");
494+ const int total = s.get_int("total");
495+ s = prof.get_safe_section(locale);
496+ percent = floor(100.f * s.get_int("translated") / total);
497+ if (percent == 100) {
498+ message = (boost::format(_("The translation into %s is complete.")) %
499+ entry.descname)
500+ .str();
501+ } else {
502+ message = (boost::format(_("The translation into %s is %d%% complete.")) %
503+ entry.descname % percent)
504+ .str();
505+ }
506+ } catch (...) {
507+ }
508+ }
509+ }
510+
511+ // We will want some help with incomplete translations. We set this lower than 100%,
512+ // because some translators let things drop a bit sometimes because they're busy and
513+ // will catch up with the work later.
514+ if (percent <= 90) {
515+ message = message + " " +
516+ (boost::format(_("If you wish to help us translate, please visit %s")) %
517+ "<font underline=1>widelands.org/wiki/TranslatingWidelands</font>")
518+ .str();
519+ }
520+ // Make font a bit smaller so the link will fit at 800x600 resolution.
521+ translation_info_.set_text(as_uifont(message, 12));
522 }
523
524 void FullscreenMenuOptions::clicked_apply() {
525
526=== modified file 'src/ui_fsmenu/options.h'
527--- src/ui_fsmenu/options.h 2017-09-11 08:09:07 +0000
528+++ src/ui_fsmenu/options.h 2017-11-04 09:41:54 +0000
529@@ -104,6 +104,7 @@
530
531 // Fills the language selection list
532 void add_languages_to_list(const std::string& current_locale);
533+ void update_language_stats(bool include_system_lang);
534
535 // Saves the options and reloads the active tab
536 void clicked_apply();
537@@ -112,8 +113,6 @@
538 uint32_t butw_;
539 uint32_t buth_;
540 uint32_t hmargin_;
541- uint32_t tab_panel_width_;
542- uint32_t column_width_;
543 uint32_t tab_panel_y_;
544
545 UI::Textarea title_;
546@@ -123,6 +122,7 @@
547 // UI elements
548 UI::TabPanel tabs_;
549 UI::Box box_interface_;
550+ UI::Box box_interface_left_;
551 UI::Box box_windows_;
552 UI::Box box_sound_;
553 UI::Box box_saving_;
554@@ -134,6 +134,7 @@
555 UI::Checkbox fullscreen_;
556 UI::Checkbox inputgrab_;
557 UI::SpinBox sb_maxfps_;
558+ UI::MultilineTextarea translation_info_;
559
560 // Windows options
561 UI::Checkbox snap_win_overlap_only_;
562@@ -170,6 +171,19 @@
563
564 /// All supported screen resolutions.
565 std::vector<ScreenResolution> resolutions_;
566+
567+ // Data model for the entries in the language selection list.
568+ struct LanguageEntry {
569+ LanguageEntry(const std::string& init_localename,
570+ const std::string& init_descname)
571+ : localename(init_localename), descname(init_descname) {
572+ }
573+ LanguageEntry() : LanguageEntry("", "") {
574+ }
575+ std::string localename; // ISO code for the locale
576+ std::string descname; // Native language name
577+ };
578+ std::map<std::string, LanguageEntry> language_entries_;
579 };
580
581 #endif // end of include guard: WL_UI_FSMENU_OPTIONS_H
582
583=== modified file 'utils/merge_and_push_translations.sh'
584--- utils/merge_and_push_translations.sh 2017-05-19 07:41:26 +0000
585+++ utils/merge_and_push_translations.sh 2017-11-04 09:41:54 +0000
586@@ -67,6 +67,11 @@
587
588 # Update catalogues.
589 utils/buildcat.py
590+
591+# Update statistics.
592+utils/update_translation_stats.py
593+
594+# Commit and push.
595 bzr commit -m "Fetched translations and updated catalogues."
596 bzr push lp:widelands
597
598
599=== added file 'utils/update_translation_stats.py'
600--- utils/update_translation_stats.py 1970-01-01 00:00:00 +0000
601+++ utils/update_translation_stats.py 2017-11-04 09:41:54 +0000
602@@ -0,0 +1,147 @@
603+#!/usr/bin/python2
604+# encoding: utf-8
605+
606+
607+"""Uses pocount from the Translate Toolkit to write translation statistics to
608+data/i18n/translation_stats.conf.
609+
610+You will need to have the Translate Toolkit installed:
611+http://toolkit.translatehouse.org/
612+
613+For Debian-based Linux: sudo apt-get install translate-toolkit
614+
615+"""
616+
617+from collections import defaultdict
618+from subprocess import call, check_output, CalledProcessError
619+import os.path
620+import re
621+import subprocess
622+import sys
623+import traceback
624+
625+#############################################################################
626+# Data Containers #
627+#############################################################################
628+
629+
630+class TranslationStats:
631+ """Total source words and translated source words."""
632+
633+ def __init__(self):
634+ # We need the total only once, but since the entries come in per
635+ # directory rather than per locale, we just store it here to keep the
636+ # algorithm simpler
637+ self.total = 0
638+ self.translated = 0
639+
640+
641+#############################################################################
642+# Main Loop #
643+#############################################################################
644+
645+def generate_translation_stats(po_dir, output_file):
646+ locale_stats = defaultdict(TranslationStats)
647+
648+ sys.stdout.write('Fetching translation stats ')
649+
650+ # Regex to extract the locale from the po filenames.
651+ regex_po = re.compile(r"/\S+/(\w+)\.po")
652+
653+ # We get errors for non-po files in the base po dir, so we have to walk
654+ # the subdirs.
655+ for subdir in sorted(os.listdir(po_dir), key=str.lower):
656+ subdir = os.path.join(po_dir, subdir)
657+ if not os.path.isdir(subdir):
658+ continue
659+
660+ sys.stdout.write('.')
661+ sys.stdout.flush()
662+
663+ try:
664+ # We need shell=True, otherwise we get "No such file or directory".
665+ stats_output = check_output(
666+ ['pocount ' + subdir + ' --csv'], stderr=subprocess.STDOUT, shell=True)
667+ if 'ERROR' in stats_output:
668+ print('\nError running pocount:\n' + stats_output.split('\n', 0)
669+ [0]) + '\nAborted creating translation statistics.'
670+ return False
671+
672+ except CalledProcessError:
673+ print('Failed to run pocount:\n FILE: ' + po_dir +
674+ '\n ' + stats_output.split('\n', 1)[1])
675+ return False
676+
677+ result = stats_output.split('\n')
678+
679+ # pocount distributes its header over multiple lines, so we have to do some
680+ # collecting here before we parse it
681+ header = ""
682+ for line in result:
683+ # Non-header rows will have a file path in them
684+ if "/" in line or "\\" in line:
685+ break
686+ header = header + " " + line
687+ header_entries = header.split(",")
688+ column_counter = 0
689+ total_column = 0
690+ translated_column = 0
691+ while column_counter < len(header_entries):
692+ if header_entries[column_counter].strip() == "Total Source Words":
693+ total_column = column_counter
694+ elif header_entries[column_counter].strip() == "Translated Source Words":
695+ translated_column = column_counter
696+ column_counter = column_counter + 1
697+
698+ # Now do the actual counting for the current textdomain
699+ for line in result:
700+ cells = line.split(",")
701+ po_filename = cells[0]
702+ if po_filename.endswith(".po"):
703+ entry = TranslationStats()
704+ locale = regex_po.match(po_filename).group(1)
705+ if locale in locale_stats:
706+ entry = locale_stats[locale]
707+ entry.total = entry.total + int(cells[total_column])
708+ entry.translated = entry.translated + int(cells[translated_column])
709+ if entry.translated > entry.total:
710+ print("Error! Translated " + str(entry.translated) + " (+"+(cells[translated_column])+") is bigger than the total of " + str(entry.total) + "("+(cells[total_column])+")\n" + line)
711+ sys.exit(1)
712+ locale_stats[locale] = entry
713+
714+ print('\n\nLocale\tTotal\tTranslated')
715+ print('------\t-----\t----------')
716+
717+ # The total goes in a [global] section and is identical for all locales
718+ result = '[global]\n'
719+ result = result + 'total=' + str(locale_stats[locale_stats.keys()[0]].total) + '\n\n'
720+
721+ # Write translation stats for all locales
722+ for locale in sorted(locale_stats.keys(), key=str.lower):
723+ entry = locale_stats[locale]
724+ print(locale + '\t' + str(entry.total) + '\t' + str(entry.translated))
725+ result = result + '[' + locale + ']\n'
726+ result = result + 'translated=' + str(entry.translated) + '\n\n'
727+
728+ with open(output_file, 'w+') as destination:
729+ destination.write(result[:-1]) # Strip the final \n
730+ print('\nResult written to ' + output_file)
731+ return True
732+
733+
734+def main():
735+ try:
736+ po_dir = os.path.abspath(os.path.join(
737+ os.path.dirname(__file__), '../po'))
738+ output_file = os.path.abspath(os.path.join(
739+ os.path.dirname(__file__), '../data/i18n/translation_stats.conf'))
740+ result = generate_translation_stats(po_dir, output_file)
741+ return result
742+
743+ except Exception:
744+ print('Something went wrong:')
745+ traceback.print_exc()
746+ return 1
747+
748+if __name__ == '__main__':
749+ sys.exit(main())

Subscribers

People subscribed via source and target branches

to status/vote changes: