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
=== added file 'data/i18n/translation_stats.conf'
--- data/i18n/translation_stats.conf 1970-01-01 00:00:00 +0000
+++ data/i18n/translation_stats.conf 2017-11-04 09:41:54 +0000
@@ -0,0 +1,182 @@
1[global]
2total=44699
3
4[ar]
5translated=8657
6
7[ast]
8translated=469
9
10[bg]
11translated=44699
12
13[br]
14translated=422
15
16[ca]
17translated=44689
18
19[cs]
20translated=43115
21
22[da]
23translated=42285
24
25[de]
26translated=44689
27
28[el]
29translated=491
30
31[en_CA]
32translated=546
33
34[en_GB]
35translated=41937
36
37[en_US]
38translated=455
39
40[eo]
41translated=4713
42
43[es]
44translated=44288
45
46[et]
47translated=575
48
49[eu]
50translated=1338
51
52[fa]
53translated=115
54
55[fi]
56translated=44689
57
58[fr]
59translated=44699
60
61[ga]
62translated=0
63
64[gd]
65translated=42984
66
67[gl]
68translated=10382
69
70[he]
71translated=420
72
73[hi]
74translated=26
75
76[hr]
77translated=3172
78
79[hu]
80translated=33179
81
82[ia]
83translated=126
84
85[id]
86translated=31
87
88[it]
89translated=36868
90
91[ja]
92translated=6944
93
94[jv]
95translated=22
96
97[ka]
98translated=4
99
100[ko]
101translated=24920
102
103[krl]
104translated=103
105
106[la]
107translated=3994
108
109[lt]
110translated=202
111
112[mr]
113translated=7
114
115[ms]
116translated=10192
117
118[my]
119translated=30
120
121[nb]
122translated=9055
123
124[nds]
125translated=874
126
127[nl]
128translated=33794
129
130[nn]
131translated=2845
132
133[oc]
134translated=193
135
136[pl]
137translated=41731
138
139[pt]
140translated=32884
141
142[pt_BR]
143translated=14496
144
145[ro]
146translated=1105
147
148[ru]
149translated=43920
150
151[rw]
152translated=48
153
154[si]
155translated=283
156
157[sk]
158translated=39387
159
160[sl]
161translated=1010
162
163[sr]
164translated=177
165
166[sv]
167translated=41420
168
169[tr]
170translated=2030
171
172[uk]
173translated=4161
174
175[vi]
176translated=1197
177
178[zh_CN]
179translated=2202
180
181[zh_TW]
182translated=641
0183
=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt 2017-05-14 20:06:48 +0000
+++ src/ui_fsmenu/CMakeLists.txt 2017-11-04 09:41:54 +0000
@@ -10,6 +10,7 @@
10 graphic_fonthandler10 graphic_fonthandler
11 graphic_text11 graphic_text
12 graphic_text_constants12 graphic_text_constants
13 graphic_text_layout
13 helper14 helper
14 io_filesystem15 io_filesystem
15 logic_constants16 logic_constants
1617
=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc 2017-09-11 16:59:41 +0000
+++ src/ui_fsmenu/options.cc 2017-11-04 09:41:54 +0000
@@ -36,6 +36,7 @@
36#include "graphic/text/bidi.h"36#include "graphic/text/bidi.h"
37#include "graphic/text/font_set.h"37#include "graphic/text/font_set.h"
38#include "graphic/text_constants.h"38#include "graphic/text_constants.h"
39#include "graphic/text_layout.h"
39#include "helper.h"40#include "helper.h"
40#include "io/filesystem/layered_filesystem.h"41#include "io/filesystem/layered_filesystem.h"
41#include "logic/constants.h"42#include "logic/constants.h"
@@ -47,23 +48,6 @@
4748
48namespace {49namespace {
4950
50// Data model for the entries in the language selection list.
51struct LanguageEntry {
52 LanguageEntry(const std::string& init_localename,
53 const std::string& init_descname,
54 const std::string& init_sortname)
55 : localename(init_localename), descname(init_descname), sortname(init_sortname) {
56 }
57
58 bool operator<(const LanguageEntry& other) const {
59 return sortname < other.sortname;
60 }
61
62 std::string localename; // ISO code for the locale
63 std::string descname; // Native language name
64 std::string sortname; // ASCII Language name used for sorting
65};
66
67// Locale identifiers can look like this: ca_ES@valencia.UTF-851// Locale identifiers can look like this: ca_ES@valencia.UTF-8
68// The contents of 'selected_locale' will be changed to match the 'current_locale'52// The contents of 'selected_locale' will be changed to match the 'current_locale'
69void find_selected_locale(std::string* selected_locale, const std::string& current_locale) {53void find_selected_locale(std::string* selected_locale, const std::string& current_locale) {
@@ -120,21 +104,22 @@
120 // Tabs104 // Tabs
121 tabs_(this, g_gr->images().get("images/ui_basic/but1.png"), UI::TabPanel::Type::kBorder),105 tabs_(this, g_gr->images().get("images/ui_basic/but1.png"), UI::TabPanel::Type::kBorder),
122106
123 box_interface_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),107 box_interface_(&tabs_, 0, 0, UI::Box::Horizontal, 0, 0, padding_),
108 box_interface_left_(&box_interface_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
124 box_windows_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),109 box_windows_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
125 box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),110 box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
126 box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),111 box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
127 box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),112 box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
128113
129 // Interface options114 // Interface options
130 language_dropdown_(&box_interface_,115 language_dropdown_(&box_interface_left_,
131 0,116 0,
132 0,117 0,
133 100, // 100 is arbitrary, will be resized in layout().118 100, // 100 is arbitrary, will be resized in layout().
134 100, // 100 is arbitrary, will be resized in layout().119 100, // 100 is arbitrary, will be resized in layout().
135 24,120 24,
136 _("Language")),121 _("Language")),
137 resolution_dropdown_(&box_interface_,122 resolution_dropdown_(&box_interface_left_,
138 0,123 0,
139 0,124 0,
140 100, // 100 is arbitrary, will be resized in layout().125 100, // 100 is arbitrary, will be resized in layout().
@@ -142,10 +127,10 @@
142 24,127 24,
143 _("In-game resolution")),128 _("In-game resolution")),
144129
145 fullscreen_(&box_interface_, Vector2i::zero(), _("Fullscreen"), "", 0),130 fullscreen_(&box_interface_left_, Vector2i::zero(), _("Fullscreen"), "", 0),
146 inputgrab_(&box_interface_, Vector2i::zero(), _("Grab Input"), "", 0),131 inputgrab_(&box_interface_left_, Vector2i::zero(), _("Grab Input"), "", 0),
147132 sb_maxfps_(&box_interface_left_, 0, 0, 0, 0, opt.maxfps, 0, 99, _("Maximum FPS:")),
148 sb_maxfps_(&box_interface_, 0, 0, 0, 0, opt.maxfps, 0, 99, _("Maximum FPS:")),133 translation_info_(&box_interface_, 0, 0, 100, 100),
149134
150 // Windows options135 // Windows options
151 snap_win_overlap_only_(136 snap_win_overlap_only_(
@@ -232,6 +217,7 @@
232 os_(opt) {217 os_(opt) {
233 // Set up UI Elements218 // Set up UI Elements
234 title_.set_fontsize(UI_FONT_SIZE_BIG);219 title_.set_fontsize(UI_FONT_SIZE_BIG);
220 translation_info_.force_new_renderer();
235221
236 // Buttons222 // Buttons
237 button_box_.add(UI::g_fh1->fontset()->is_rtl() ? &ok_ : &cancel_);223 button_box_.add(UI::g_fh1->fontset()->is_rtl() ? &ok_ : &cancel_);
@@ -252,18 +238,14 @@
252 tabs_.activate(os_.active_tab);238 tabs_.activate(os_.active_tab);
253 }239 }
254240
255 box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
256 box_windows_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
257 box_sound_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
258 box_saving_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
259 box_game_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
260
261 // Interface241 // Interface
262 box_interface_.add(&language_dropdown_);242 box_interface_.add(&box_interface_left_);
263 box_interface_.add(&resolution_dropdown_);243 box_interface_.add(&translation_info_, UI::Box::Resizing::kExpandBoth);
264 box_interface_.add(&fullscreen_);244 box_interface_left_.add(&language_dropdown_);
265 box_interface_.add(&inputgrab_);245 box_interface_left_.add(&resolution_dropdown_);
266 box_interface_.add(&sb_maxfps_);246 box_interface_left_.add(&fullscreen_);
247 box_interface_left_.add(&inputgrab_);
248 box_interface_left_.add(&sb_maxfps_);
267249
268 // Windows250 // Windows
269 box_windows_.add(&snap_win_overlap_only_);251 box_windows_.add(&snap_win_overlap_only_);
@@ -290,6 +272,8 @@
290 box_game_.add(&single_watchwin_);272 box_game_.add(&single_watchwin_);
291273
292 // Bind actions274 // Bind actions
275 language_dropdown_.selected.connect(
276 boost::bind(&FullscreenMenuOptions::update_language_stats, this, false));
293 cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));277 cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));
294 apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this));278 apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this));
295 ok_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_ok, this));279 ok_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_ok, this));
@@ -360,6 +344,7 @@
360344
361 // Language options345 // Language options
362 add_languages_to_list(opt.language);346 add_languages_to_list(opt.language);
347 update_language_stats(true);
363 layout();348 layout();
364}349}
365350
@@ -369,8 +354,7 @@
369 butw_ = get_w() / 5;354 butw_ = get_w() / 5;
370 buth_ = get_h() * 9 / 200;355 buth_ = get_h() * 9 / 200;
371 hmargin_ = get_w() * 19 / 200;356 hmargin_ = get_w() * 19 / 200;
372 tab_panel_width_ = get_inner_w() - 2 * hmargin_;357 int tab_panel_width = get_inner_w() - 2 * hmargin_;
373 column_width_ = tab_panel_width_ - padding_;
374 tab_panel_y_ = get_h() * 14 / 100;358 tab_panel_y_ = get_h() * 14 / 100;
375359
376 // Title360 // Title
@@ -382,48 +366,53 @@
382 apply_.set_desired_size(butw_, buth_);366 apply_.set_desired_size(butw_, buth_);
383 ok_.set_desired_size(butw_, buth_);367 ok_.set_desired_size(butw_, buth_);
384 button_box_.set_pos(Vector2i(hmargin_ + butw_ / 3, get_inner_h() - hmargin_));368 button_box_.set_pos(Vector2i(hmargin_ + butw_ / 3, get_inner_h() - hmargin_));
385 button_box_.set_size(tab_panel_width_ - 2 * butw_ / 3, buth_);369 button_box_.set_size(tab_panel_width - 2 * butw_ / 3, buth_);
386370
387 // Tabs371 // Tabs
388 tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));372 tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));
389 tabs_.set_size(tab_panel_width_, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);373 tabs_.set_size(tab_panel_width, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);
374
375 tab_panel_width -= padding_;
376 const int column_width = tab_panel_width / 2;
390377
391 // Interface378 // Interface
392 language_dropdown_.set_desired_size(column_width_ / 2, language_dropdown_.get_h());379 box_interface_left_.set_desired_size(column_width + padding_, tabs_.get_inner_h());
380 box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
381 language_dropdown_.set_desired_size(column_width, language_dropdown_.get_h());
393 language_dropdown_.set_height(tabs_.get_h() - language_dropdown_.get_y() - buth_ - 3 * padding_);382 language_dropdown_.set_height(tabs_.get_h() - language_dropdown_.get_y() - buth_ - 3 * padding_);
394 resolution_dropdown_.set_desired_size(column_width_ / 2, resolution_dropdown_.get_h());383 resolution_dropdown_.set_desired_size(column_width, resolution_dropdown_.get_h());
395 resolution_dropdown_.set_height(tabs_.get_h() - resolution_dropdown_.get_y() - buth_ -384 resolution_dropdown_.set_height(tabs_.get_h() - resolution_dropdown_.get_y() - buth_ -
396 3 * padding_);385 3 * padding_);
397386
398 fullscreen_.set_desired_size(column_width_, fullscreen_.get_h());387 fullscreen_.set_desired_size(column_width, fullscreen_.get_h());
399 inputgrab_.set_desired_size(column_width_, inputgrab_.get_h());388 inputgrab_.set_desired_size(column_width, inputgrab_.get_h());
400 sb_maxfps_.set_unit_width(column_width_ / 4);389 sb_maxfps_.set_unit_width(column_width / 2);
401 sb_maxfps_.set_desired_size(column_width_ / 2, sb_maxfps_.get_h());390 sb_maxfps_.set_desired_size(column_width, sb_maxfps_.get_h());
402391
403 // Windows options392 // Windows options
404 snap_win_overlap_only_.set_desired_size(column_width_, snap_win_overlap_only_.get_h());393 snap_win_overlap_only_.set_desired_size(tab_panel_width, snap_win_overlap_only_.get_h());
405 dock_windows_to_edges_.set_desired_size(column_width_, dock_windows_to_edges_.get_h());394 dock_windows_to_edges_.set_desired_size(tab_panel_width, dock_windows_to_edges_.get_h());
406 animate_map_panning_.set_desired_size(column_width_, animate_map_panning_.get_h());395 animate_map_panning_.set_desired_size(tab_panel_width, animate_map_panning_.get_h());
407 sb_dis_panel_.set_unit_width(200);396 sb_dis_panel_.set_unit_width(200);
408 sb_dis_panel_.set_desired_size(column_width_, sb_dis_panel_.get_h());397 sb_dis_panel_.set_desired_size(tab_panel_width, sb_dis_panel_.get_h());
409 sb_dis_border_.set_unit_width(200);398 sb_dis_border_.set_unit_width(200);
410 sb_dis_border_.set_desired_size(column_width_, sb_dis_border_.get_h());399 sb_dis_border_.set_desired_size(tab_panel_width, sb_dis_border_.get_h());
411400
412 // Sound options401 // Sound options
413 music_.set_desired_size(column_width_, music_.get_h());402 music_.set_desired_size(tab_panel_width, music_.get_h());
414 fx_.set_desired_size(column_width_, fx_.get_h());403 fx_.set_desired_size(tab_panel_width, fx_.get_h());
415 message_sound_.set_desired_size(column_width_, message_sound_.get_h());404 message_sound_.set_desired_size(tab_panel_width, message_sound_.get_h());
416405
417 // Saving options406 // Saving options
418 sb_autosave_.set_unit_width(250);407 sb_autosave_.set_unit_width(250);
419 sb_autosave_.set_desired_size(column_width_, sb_autosave_.get_h());408 sb_autosave_.set_desired_size(tab_panel_width, sb_autosave_.get_h());
420 sb_rolling_autosave_.set_unit_width(250);409 sb_rolling_autosave_.set_unit_width(250);
421 sb_rolling_autosave_.set_desired_size(column_width_, sb_rolling_autosave_.get_h());410 sb_rolling_autosave_.set_desired_size(tab_panel_width, sb_rolling_autosave_.get_h());
422 zip_.set_desired_size(column_width_, zip_.get_h());411 zip_.set_desired_size(tab_panel_width, zip_.get_h());
423 write_syncstreams_.set_desired_size(column_width_, write_syncstreams_.get_h());412 write_syncstreams_.set_desired_size(tab_panel_width, write_syncstreams_.get_h());
424413
425 // Game options414 // Game options
426 transparent_chat_.set_desired_size(column_width_, transparent_chat_.get_h());415 transparent_chat_.set_desired_size(tab_panel_width, transparent_chat_.get_h());
427}416}
428417
429void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {418void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {
@@ -432,8 +421,8 @@
432 language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == "");421 language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == "");
433 language_dropdown_.add("English", "en", nullptr, current_locale == "en");422 language_dropdown_.add("English", "en", nullptr, current_locale == "en");
434423
435 // Add translation directories to the list424 // Add translation directories to the list. Using the LanguageEntries' sortnames as a key for getting a sorted result.
436 std::vector<LanguageEntry> entries;425 std::map<std::string, LanguageEntry> entries;
437 std::string selected_locale;426 std::string selected_locale;
438427
439 try { // Begin read locales table428 try { // Begin read locales table
@@ -460,7 +449,9 @@
460449
461 std::string name = i18n::make_ligatures(table->get_string("name").c_str());450 std::string name = i18n::make_ligatures(table->get_string("name").c_str());
462 const std::string sortname = table->get_string("sort_name");451 const std::string sortname = table->get_string("sort_name");
463 entries.push_back(LanguageEntry(localename, name, sortname));452 LanguageEntry* entry = new LanguageEntry(localename, name);
453 entries.insert(std::make_pair(sortname, *entry));
454 language_entries_.insert(std::make_pair(localename, *entry));
464455
465 if (localename == current_locale) {456 if (localename == current_locale) {
466 selected_locale = current_locale;457 selected_locale = current_locale;
@@ -468,7 +459,7 @@
468459
469 } catch (const WException&) {460 } catch (const WException&) {
470 log("Could not read locale for: %s\n", localename.c_str());461 log("Could not read locale for: %s\n", localename.c_str());
471 entries.push_back(LanguageEntry(localename, localename, localename));462 entries.insert(std::make_pair(localename, LanguageEntry(localename, localename)));
472 } // End read locale from table463 } // End read locale from table
473 } // End scan locales directory464 } // End scan locales directory
474 } catch (const LuaError& err) {465 } catch (const LuaError& err) {
@@ -477,11 +468,76 @@
477 } // End read locales table468 } // End read locales table
478469
479 find_selected_locale(&selected_locale, current_locale);470 find_selected_locale(&selected_locale, current_locale);
480 std::sort(entries.begin(), entries.end());471 for (const auto& entry : entries) {
481 for (const LanguageEntry& entry : entries) {472 const LanguageEntry& language_entry = entry.second;
482 language_dropdown_.add(entry.descname.c_str(), entry.localename, nullptr,473 language_dropdown_.add(language_entry.descname.c_str(), language_entry.localename, nullptr,
483 entry.localename == selected_locale, "");474 language_entry.localename == selected_locale, "");
484 }475 }
476}
477
478/**
479 * Updates the language statistics message according to the currently selected locale.
480 * @param include_system_lang We only want to include the system lang if it matches the Widelands
481 * locale.
482 */
483void FullscreenMenuOptions::update_language_stats(bool include_system_lang) {
484 int percent = 100;
485 std::string message = "";
486 if (language_dropdown_.has_selection()) {
487 std::string locale = language_dropdown_.get_selected();
488 // Empty locale means try system locale
489 if (locale.empty() && include_system_lang) {
490 std::vector<std::string> parts;
491 boost::split(parts, i18n::get_locale(), boost::is_any_of("."));
492 if (language_entries_.count(parts[0]) == 1) {
493 locale = parts[0];
494 } else {
495 boost::split(parts, parts[0], boost::is_any_of("@"));
496 if (language_entries_.count(parts[0]) == 1) {
497 locale = parts[0];
498 } else {
499 boost::split(parts, parts[0], boost::is_any_of("_"));
500 if (language_entries_.count(parts[0]) == 1) {
501 locale = parts[0];
502 }
503 }
504 }
505 }
506
507 // If we have the locale, grab the stats and set the message
508 if (language_entries_.count(locale) == 1) {
509 try {
510 const LanguageEntry& entry = language_entries_[locale];
511 Profile prof("i18n/translation_stats.conf");
512 Section& s = prof.get_safe_section("global");
513 const int total = s.get_int("total");
514 s = prof.get_safe_section(locale);
515 percent = floor(100.f * s.get_int("translated") / total);
516 if (percent == 100) {
517 message = (boost::format(_("The translation into %s is complete.")) %
518 entry.descname)
519 .str();
520 } else {
521 message = (boost::format(_("The translation into %s is %d%% complete.")) %
522 entry.descname % percent)
523 .str();
524 }
525 } catch (...) {
526 }
527 }
528 }
529
530 // We will want some help with incomplete translations. We set this lower than 100%,
531 // because some translators let things drop a bit sometimes because they're busy and
532 // will catch up with the work later.
533 if (percent <= 90) {
534 message = message + " " +
535 (boost::format(_("If you wish to help us translate, please visit %s")) %
536 "<font underline=1>widelands.org/wiki/TranslatingWidelands</font>")
537 .str();
538 }
539 // Make font a bit smaller so the link will fit at 800x600 resolution.
540 translation_info_.set_text(as_uifont(message, 12));
485}541}
486542
487void FullscreenMenuOptions::clicked_apply() {543void FullscreenMenuOptions::clicked_apply() {
488544
=== modified file 'src/ui_fsmenu/options.h'
--- src/ui_fsmenu/options.h 2017-09-11 08:09:07 +0000
+++ src/ui_fsmenu/options.h 2017-11-04 09:41:54 +0000
@@ -104,6 +104,7 @@
104104
105 // Fills the language selection list105 // Fills the language selection list
106 void add_languages_to_list(const std::string& current_locale);106 void add_languages_to_list(const std::string& current_locale);
107 void update_language_stats(bool include_system_lang);
107108
108 // Saves the options and reloads the active tab109 // Saves the options and reloads the active tab
109 void clicked_apply();110 void clicked_apply();
@@ -112,8 +113,6 @@
112 uint32_t butw_;113 uint32_t butw_;
113 uint32_t buth_;114 uint32_t buth_;
114 uint32_t hmargin_;115 uint32_t hmargin_;
115 uint32_t tab_panel_width_;
116 uint32_t column_width_;
117 uint32_t tab_panel_y_;116 uint32_t tab_panel_y_;
118117
119 UI::Textarea title_;118 UI::Textarea title_;
@@ -123,6 +122,7 @@
123 // UI elements122 // UI elements
124 UI::TabPanel tabs_;123 UI::TabPanel tabs_;
125 UI::Box box_interface_;124 UI::Box box_interface_;
125 UI::Box box_interface_left_;
126 UI::Box box_windows_;126 UI::Box box_windows_;
127 UI::Box box_sound_;127 UI::Box box_sound_;
128 UI::Box box_saving_;128 UI::Box box_saving_;
@@ -134,6 +134,7 @@
134 UI::Checkbox fullscreen_;134 UI::Checkbox fullscreen_;
135 UI::Checkbox inputgrab_;135 UI::Checkbox inputgrab_;
136 UI::SpinBox sb_maxfps_;136 UI::SpinBox sb_maxfps_;
137 UI::MultilineTextarea translation_info_;
137138
138 // Windows options139 // Windows options
139 UI::Checkbox snap_win_overlap_only_;140 UI::Checkbox snap_win_overlap_only_;
@@ -170,6 +171,19 @@
170171
171 /// All supported screen resolutions.172 /// All supported screen resolutions.
172 std::vector<ScreenResolution> resolutions_;173 std::vector<ScreenResolution> resolutions_;
174
175 // Data model for the entries in the language selection list.
176 struct LanguageEntry {
177 LanguageEntry(const std::string& init_localename,
178 const std::string& init_descname)
179 : localename(init_localename), descname(init_descname) {
180 }
181 LanguageEntry() : LanguageEntry("", "") {
182 }
183 std::string localename; // ISO code for the locale
184 std::string descname; // Native language name
185 };
186 std::map<std::string, LanguageEntry> language_entries_;
173};187};
174188
175#endif // end of include guard: WL_UI_FSMENU_OPTIONS_H189#endif // end of include guard: WL_UI_FSMENU_OPTIONS_H
176190
=== modified file 'utils/merge_and_push_translations.sh'
--- utils/merge_and_push_translations.sh 2017-05-19 07:41:26 +0000
+++ utils/merge_and_push_translations.sh 2017-11-04 09:41:54 +0000
@@ -67,6 +67,11 @@
6767
68# Update catalogues.68# Update catalogues.
69utils/buildcat.py69utils/buildcat.py
70
71# Update statistics.
72utils/update_translation_stats.py
73
74# Commit and push.
70bzr commit -m "Fetched translations and updated catalogues."75bzr commit -m "Fetched translations and updated catalogues."
71bzr push lp:widelands76bzr push lp:widelands
7277
7378
=== added file 'utils/update_translation_stats.py'
--- utils/update_translation_stats.py 1970-01-01 00:00:00 +0000
+++ utils/update_translation_stats.py 2017-11-04 09:41:54 +0000
@@ -0,0 +1,147 @@
1#!/usr/bin/python2
2# encoding: utf-8
3
4
5"""Uses pocount from the Translate Toolkit to write translation statistics to
6data/i18n/translation_stats.conf.
7
8You will need to have the Translate Toolkit installed:
9http://toolkit.translatehouse.org/
10
11For Debian-based Linux: sudo apt-get install translate-toolkit
12
13"""
14
15from collections import defaultdict
16from subprocess import call, check_output, CalledProcessError
17import os.path
18import re
19import subprocess
20import sys
21import traceback
22
23#############################################################################
24# Data Containers #
25#############################################################################
26
27
28class TranslationStats:
29 """Total source words and translated source words."""
30
31 def __init__(self):
32 # We need the total only once, but since the entries come in per
33 # directory rather than per locale, we just store it here to keep the
34 # algorithm simpler
35 self.total = 0
36 self.translated = 0
37
38
39#############################################################################
40# Main Loop #
41#############################################################################
42
43def generate_translation_stats(po_dir, output_file):
44 locale_stats = defaultdict(TranslationStats)
45
46 sys.stdout.write('Fetching translation stats ')
47
48 # Regex to extract the locale from the po filenames.
49 regex_po = re.compile(r"/\S+/(\w+)\.po")
50
51 # We get errors for non-po files in the base po dir, so we have to walk
52 # the subdirs.
53 for subdir in sorted(os.listdir(po_dir), key=str.lower):
54 subdir = os.path.join(po_dir, subdir)
55 if not os.path.isdir(subdir):
56 continue
57
58 sys.stdout.write('.')
59 sys.stdout.flush()
60
61 try:
62 # We need shell=True, otherwise we get "No such file or directory".
63 stats_output = check_output(
64 ['pocount ' + subdir + ' --csv'], stderr=subprocess.STDOUT, shell=True)
65 if 'ERROR' in stats_output:
66 print('\nError running pocount:\n' + stats_output.split('\n', 0)
67 [0]) + '\nAborted creating translation statistics.'
68 return False
69
70 except CalledProcessError:
71 print('Failed to run pocount:\n FILE: ' + po_dir +
72 '\n ' + stats_output.split('\n', 1)[1])
73 return False
74
75 result = stats_output.split('\n')
76
77 # pocount distributes its header over multiple lines, so we have to do some
78 # collecting here before we parse it
79 header = ""
80 for line in result:
81 # Non-header rows will have a file path in them
82 if "/" in line or "\\" in line:
83 break
84 header = header + " " + line
85 header_entries = header.split(",")
86 column_counter = 0
87 total_column = 0
88 translated_column = 0
89 while column_counter < len(header_entries):
90 if header_entries[column_counter].strip() == "Total Source Words":
91 total_column = column_counter
92 elif header_entries[column_counter].strip() == "Translated Source Words":
93 translated_column = column_counter
94 column_counter = column_counter + 1
95
96 # Now do the actual counting for the current textdomain
97 for line in result:
98 cells = line.split(",")
99 po_filename = cells[0]
100 if po_filename.endswith(".po"):
101 entry = TranslationStats()
102 locale = regex_po.match(po_filename).group(1)
103 if locale in locale_stats:
104 entry = locale_stats[locale]
105 entry.total = entry.total + int(cells[total_column])
106 entry.translated = entry.translated + int(cells[translated_column])
107 if entry.translated > entry.total:
108 print("Error! Translated " + str(entry.translated) + " (+"+(cells[translated_column])+") is bigger than the total of " + str(entry.total) + "("+(cells[total_column])+")\n" + line)
109 sys.exit(1)
110 locale_stats[locale] = entry
111
112 print('\n\nLocale\tTotal\tTranslated')
113 print('------\t-----\t----------')
114
115 # The total goes in a [global] section and is identical for all locales
116 result = '[global]\n'
117 result = result + 'total=' + str(locale_stats[locale_stats.keys()[0]].total) + '\n\n'
118
119 # Write translation stats for all locales
120 for locale in sorted(locale_stats.keys(), key=str.lower):
121 entry = locale_stats[locale]
122 print(locale + '\t' + str(entry.total) + '\t' + str(entry.translated))
123 result = result + '[' + locale + ']\n'
124 result = result + 'translated=' + str(entry.translated) + '\n\n'
125
126 with open(output_file, 'w+') as destination:
127 destination.write(result[:-1]) # Strip the final \n
128 print('\nResult written to ' + output_file)
129 return True
130
131
132def main():
133 try:
134 po_dir = os.path.abspath(os.path.join(
135 os.path.dirname(__file__), '../po'))
136 output_file = os.path.abspath(os.path.join(
137 os.path.dirname(__file__), '../data/i18n/translation_stats.conf'))
138 result = generate_translation_stats(po_dir, output_file)
139 return result
140
141 except Exception:
142 print('Something went wrong:')
143 traceback.print_exc()
144 return 1
145
146if __name__ == '__main__':
147 sys.exit(main())

Subscribers

People subscribed via source and target branches

to status/vote changes: