1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Linguist of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "translator.h" |
30 | |
31 | #include <QtCore/QByteArray> |
32 | #include <QtCore/QDebug> |
33 | #include <QtCore/QDir> |
34 | #include <QtCore/QFile> |
35 | #include <QtCore/QFileInfo> |
36 | #include <QtCore/QMap> |
37 | |
38 | #include <private/qtranslator_p.h> |
39 | |
40 | QT_BEGIN_NAMESPACE |
41 | |
42 | static const uchar englishStyleRules[] = |
43 | { Q_EQ, 1 }; |
44 | static const uchar frenchStyleRules[] = |
45 | { Q_LEQ, 1 }; |
46 | static const uchar latvianRules[] = |
47 | { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE, |
48 | Q_NEQ, 0 }; |
49 | static const uchar icelandicRules[] = |
50 | { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11 }; |
51 | static const uchar irishStyleRules[] = |
52 | { Q_EQ, 1, Q_NEWRULE, |
53 | Q_EQ, 2 }; |
54 | static const uchar gaelicStyleRules[] = |
55 | { Q_EQ, 1, Q_OR, Q_EQ, 11, Q_NEWRULE, |
56 | Q_EQ, 2, Q_OR, Q_EQ, 12, Q_NEWRULE, |
57 | Q_BETWEEN, 3, 19 }; |
58 | static const uchar slovakStyleRules[] = |
59 | { Q_EQ, 1, Q_NEWRULE, |
60 | Q_BETWEEN, 2, 4 }; |
61 | static const uchar macedonianRules[] = |
62 | { Q_MOD_10 | Q_EQ, 1, Q_NEWRULE, |
63 | Q_MOD_10 | Q_EQ, 2 }; |
64 | static const uchar lithuanianRules[] = |
65 | { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE, |
66 | Q_MOD_10 | Q_NEQ, 0, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 }; |
67 | static const uchar russianStyleRules[] = |
68 | { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE, |
69 | Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 }; |
70 | static const uchar polishRules[] = |
71 | { Q_EQ, 1, Q_NEWRULE, |
72 | Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 }; |
73 | static const uchar romanianRules[] = |
74 | { Q_EQ, 1, Q_NEWRULE, |
75 | Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 19 }; |
76 | static const uchar slovenianRules[] = |
77 | { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE, |
78 | Q_MOD_100 | Q_EQ, 2, Q_NEWRULE, |
79 | Q_MOD_100 | Q_BETWEEN, 3, 4 }; |
80 | static const uchar malteseRules[] = |
81 | { Q_EQ, 1, Q_NEWRULE, |
82 | Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 10, Q_NEWRULE, |
83 | Q_MOD_100 | Q_BETWEEN, 11, 19 }; |
84 | static const uchar welshRules[] = |
85 | { Q_EQ, 0, Q_NEWRULE, |
86 | Q_EQ, 1, Q_NEWRULE, |
87 | Q_BETWEEN, 2, 5, Q_NEWRULE, |
88 | Q_EQ, 6 }; |
89 | static const uchar arabicRules[] = |
90 | { Q_EQ, 0, Q_NEWRULE, |
91 | Q_EQ, 1, Q_NEWRULE, |
92 | Q_EQ, 2, Q_NEWRULE, |
93 | Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE, |
94 | Q_MOD_100 | Q_GEQ, 11 }; |
95 | static const uchar tagalogRules[] = |
96 | { Q_LEQ, 1, Q_NEWRULE, |
97 | Q_MOD_10 | Q_EQ, 4, Q_OR, Q_MOD_10 | Q_EQ, 6, Q_OR, Q_MOD_10 | Q_EQ, 9 }; |
98 | |
99 | static const char * const japaneseStyleForms[] = { "Universal Form" , 0 }; |
100 | static const char * const englishStyleForms[] = { "Singular" , "Plural" , 0 }; |
101 | static const char * const frenchStyleForms[] = { "Singular" , "Plural" , 0 }; |
102 | static const char * const icelandicForms[] = { "Singular" , "Plural" , 0 }; |
103 | static const char * const latvianForms[] = { "Singular" , "Plural" , "Nullar" , 0 }; |
104 | static const char * const irishStyleForms[] = { "Singular" , "Dual" , "Plural" , 0 }; |
105 | // Gaelic uses the grammatical Singular for the Plural cardinality, |
106 | // so using the Latin terms is expected to cause confusion. |
107 | static const char * const gaelicStyleForms[] = { "1/11" , "2/12" , "Few" , "Many" , 0 }; |
108 | static const char * const slovakStyleForms[] = { "Singular" , "Paucal" , "Plural" , 0 }; |
109 | static const char * const macedonianForms[] = { "Singular" , "Dual" , "Plural" , 0 }; |
110 | static const char * const lithuanianForms[] = { "Singular" , "Paucal" , "Plural" , 0 }; |
111 | static const char * const russianStyleForms[] = { "Singular" , "Dual" , "Plural" , 0 }; |
112 | static const char * const polishForms[] = { "Singular" , "Paucal" , "Plural" , 0 }; |
113 | static const char * const romanianForms[] = { "Singular" , "Paucal" , "Plural" , 0 }; |
114 | static const char * const slovenianForms[] = { "Singular" , "Dual" , "Trial" , "Plural" , 0 }; |
115 | static const char * const malteseForms[] = |
116 | { "Singular" , "Paucal" , "Greater Paucal" , "Plural" , 0 }; |
117 | static const char * const welshForms[] = |
118 | { "Nullar" , "Singular" , "Dual" , "Sexal" , "Plural" , 0 }; |
119 | static const char * const arabicForms[] = |
120 | { "Nullar" , "Singular" , "Dual" , "Minority Plural" , "Plural" , "Plural (100-102, ...)" , 0 }; |
121 | static const char * const tagalogForms[] = |
122 | { "Singular" , "Plural (consonant-ended)" , "Plural (vowel-ended)" , 0 }; |
123 | |
124 | #define EOL QLocale::C |
125 | |
126 | static const QLocale::Language japaneseStyleLanguages[] = { |
127 | QLocale::Bislama, |
128 | QLocale::Burmese, |
129 | QLocale::Chinese, |
130 | QLocale::Dzongkha, |
131 | QLocale::Fijian, |
132 | QLocale::Guarani, |
133 | QLocale::Hungarian, |
134 | QLocale::Indonesian, |
135 | QLocale::Japanese, |
136 | QLocale::Javanese, |
137 | QLocale::Korean, |
138 | QLocale::Malay, |
139 | QLocale::NauruLanguage, |
140 | QLocale::Oromo, |
141 | QLocale::Persian, |
142 | QLocale::Sundanese, |
143 | QLocale::Tatar, |
144 | QLocale::Thai, |
145 | QLocale::Tibetan, |
146 | QLocale::Turkish, |
147 | QLocale::Vietnamese, |
148 | QLocale::Yoruba, |
149 | QLocale::Zhuang, |
150 | EOL |
151 | }; |
152 | |
153 | static const QLocale::Language englishStyleLanguages[] = { |
154 | QLocale::Abkhazian, |
155 | QLocale::Afar, |
156 | QLocale::Afrikaans, |
157 | QLocale::Albanian, |
158 | QLocale::Amharic, |
159 | QLocale::Assamese, |
160 | QLocale::Aymara, |
161 | QLocale::Azerbaijani, |
162 | QLocale::Bashkir, |
163 | QLocale::Basque, |
164 | QLocale::Bengali, |
165 | QLocale::Bulgarian, |
166 | QLocale::Catalan, |
167 | QLocale::Cornish, |
168 | QLocale::Corsican, |
169 | QLocale::Danish, |
170 | QLocale::Dutch, |
171 | QLocale::English, |
172 | QLocale::Esperanto, |
173 | QLocale::Estonian, |
174 | QLocale::Faroese, |
175 | QLocale::Finnish, |
176 | QLocale::Friulian, |
177 | QLocale::WesternFrisian, |
178 | QLocale::Galician, |
179 | QLocale::Georgian, |
180 | QLocale::German, |
181 | QLocale::Greek, |
182 | QLocale::Greenlandic, |
183 | QLocale::Gujarati, |
184 | QLocale::Hausa, |
185 | QLocale::Hebrew, |
186 | QLocale::Hindi, |
187 | QLocale::Interlingua, |
188 | QLocale::Interlingue, |
189 | QLocale::Italian, |
190 | QLocale::Kannada, |
191 | QLocale::Kashmiri, |
192 | QLocale::Kazakh, |
193 | QLocale::Khmer, |
194 | QLocale::Kinyarwanda, |
195 | QLocale::Kirghiz, |
196 | QLocale::Kurdish, |
197 | QLocale::Lao, |
198 | QLocale::Latin, |
199 | QLocale::Lingala, |
200 | QLocale::Luxembourgish, |
201 | QLocale::Malagasy, |
202 | QLocale::Malayalam, |
203 | QLocale::Marathi, |
204 | QLocale::Mongolian, |
205 | // Missing: Nahuatl, |
206 | QLocale::Nepali, |
207 | QLocale::NorthernSotho, |
208 | QLocale::NorwegianBokmal, |
209 | QLocale::NorwegianNynorsk, |
210 | QLocale::Occitan, |
211 | QLocale::Oriya, |
212 | QLocale::Pashto, |
213 | QLocale::Portuguese, |
214 | QLocale::Punjabi, |
215 | QLocale::Quechua, |
216 | QLocale::Romansh, |
217 | QLocale::Rundi, |
218 | QLocale::Shona, |
219 | QLocale::Sindhi, |
220 | QLocale::Sinhala, |
221 | QLocale::Somali, |
222 | QLocale::SouthernSotho, |
223 | QLocale::Spanish, |
224 | QLocale::Swahili, |
225 | QLocale::Swati, |
226 | QLocale::Swedish, |
227 | QLocale::Tajik, |
228 | QLocale::Tamil, |
229 | QLocale::Telugu, |
230 | QLocale::Tongan, |
231 | QLocale::Tsonga, |
232 | QLocale::Tswana, |
233 | QLocale::Turkmen, |
234 | QLocale::Uigur, |
235 | QLocale::Urdu, |
236 | QLocale::Uzbek, |
237 | QLocale::Volapuk, |
238 | QLocale::Wolof, |
239 | QLocale::Xhosa, |
240 | QLocale::Yiddish, |
241 | QLocale::Zulu, |
242 | EOL |
243 | }; |
244 | static const QLocale::Language frenchStyleLanguages[] = { |
245 | // keep synchronized with frenchStyleCountries |
246 | QLocale::Armenian, |
247 | QLocale::Breton, |
248 | QLocale::French, |
249 | QLocale::Portuguese, |
250 | QLocale::Filipino, |
251 | QLocale::Tigrinya, |
252 | QLocale::Walloon, |
253 | EOL |
254 | }; |
255 | static const QLocale::Language latvianLanguage[] = { QLocale::Latvian, EOL }; |
256 | static const QLocale::Language icelandicLanguage[] = { QLocale::Icelandic, EOL }; |
257 | static const QLocale::Language irishStyleLanguages[] = { |
258 | QLocale::Divehi, |
259 | QLocale::Inuktitut, |
260 | QLocale::Inupiak, |
261 | QLocale::Irish, |
262 | QLocale::Manx, |
263 | QLocale::Maori, |
264 | QLocale::NorthernSami, |
265 | QLocale::Samoan, |
266 | QLocale::Sanskrit, |
267 | EOL |
268 | }; |
269 | static const QLocale::Language gaelicStyleLanguages[] = { QLocale::Gaelic, EOL }; |
270 | static const QLocale::Language slovakStyleLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL }; |
271 | static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL }; |
272 | static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL }; |
273 | static const QLocale::Language russianStyleLanguages[] = { |
274 | QLocale::Bosnian, |
275 | QLocale::Belarusian, |
276 | QLocale::Croatian, |
277 | QLocale::Russian, |
278 | QLocale::Serbian, |
279 | QLocale::Ukrainian, |
280 | EOL |
281 | }; |
282 | static const QLocale::Language polishLanguage[] = { QLocale::Polish, EOL }; |
283 | static const QLocale::Language romanianLanguages[] = { |
284 | QLocale::Romanian, |
285 | EOL |
286 | }; |
287 | static const QLocale::Language slovenianLanguage[] = { QLocale::Slovenian, EOL }; |
288 | static const QLocale::Language malteseLanguage[] = { QLocale::Maltese, EOL }; |
289 | static const QLocale::Language welshLanguage[] = { QLocale::Welsh, EOL }; |
290 | static const QLocale::Language arabicLanguage[] = { QLocale::Arabic, EOL }; |
291 | static const QLocale::Language tagalogLanguage[] = { QLocale::Filipino, EOL }; |
292 | |
293 | static const QLocale::Country frenchStyleCountries[] = { |
294 | // keep synchronized with frenchStyleLanguages |
295 | QLocale::AnyCountry, |
296 | QLocale::AnyCountry, |
297 | QLocale::AnyCountry, |
298 | QLocale::Brazil, |
299 | QLocale::AnyCountry, |
300 | QLocale::AnyCountry, |
301 | QLocale::AnyCountry |
302 | }; |
303 | struct NumerusTableEntry { |
304 | const uchar *rules; |
305 | int rulesSize; |
306 | const char * const *forms; |
307 | const QLocale::Language *languages; |
308 | const QLocale::Country *countries; |
309 | const char * const gettextRules; |
310 | }; |
311 | |
312 | static const NumerusTableEntry numerusTable[] = { |
313 | { .rules: 0, .rulesSize: 0, .forms: japaneseStyleForms, .languages: japaneseStyleLanguages, .countries: 0, .gettextRules: "nplurals=1; plural=0;" }, |
314 | { .rules: englishStyleRules, .rulesSize: sizeof(englishStyleRules), .forms: englishStyleForms, .languages: englishStyleLanguages, .countries: 0, |
315 | .gettextRules: "nplurals=2; plural=(n != 1);" }, |
316 | { .rules: frenchStyleRules, .rulesSize: sizeof(frenchStyleRules), .forms: frenchStyleForms, .languages: frenchStyleLanguages, |
317 | .countries: frenchStyleCountries, .gettextRules: "nplurals=2; plural=(n > 1);" }, |
318 | { .rules: latvianRules, .rulesSize: sizeof(latvianRules), .forms: latvianForms, .languages: latvianLanguage, .countries: 0, |
319 | .gettextRules: "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" }, |
320 | { .rules: icelandicRules, .rulesSize: sizeof(icelandicRules), .forms: icelandicForms, .languages: icelandicLanguage, .countries: 0, |
321 | .gettextRules: "nplurals=2; plural=(n%10==1 && n%100!=11 ? 0 : 1);" }, |
322 | { .rules: irishStyleRules, .rulesSize: sizeof(irishStyleRules), .forms: irishStyleForms, .languages: irishStyleLanguages, .countries: 0, |
323 | .gettextRules: "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" }, |
324 | { .rules: gaelicStyleRules, .rulesSize: sizeof(gaelicStyleRules), .forms: gaelicStyleForms, .languages: gaelicStyleLanguages, .countries: 0, |
325 | .gettextRules: "nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;" }, |
326 | { .rules: slovakStyleRules, .rulesSize: sizeof(slovakStyleRules), .forms: slovakStyleForms, .languages: slovakStyleLanguages, .countries: 0, |
327 | .gettextRules: "nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);" }, |
328 | { .rules: macedonianRules, .rulesSize: sizeof(macedonianRules), .forms: macedonianForms, .languages: macedonianLanguage, .countries: 0, |
329 | .gettextRules: "nplurals=3; plural=(n%100==1 ? 0 : n%100==2 ? 1 : 2);" }, |
330 | { .rules: lithuanianRules, .rulesSize: sizeof(lithuanianRules), .forms: lithuanianForms, .languages: lithuanianLanguage, .countries: 0, |
331 | .gettextRules: "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" }, |
332 | { .rules: russianStyleRules, .rulesSize: sizeof(russianStyleRules), .forms: russianStyleForms, .languages: russianStyleLanguages, .countries: 0, |
333 | .gettextRules: "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, |
334 | { .rules: polishRules, .rulesSize: sizeof(polishRules), .forms: polishForms, .languages: polishLanguage, .countries: 0, |
335 | .gettextRules: "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, |
336 | { .rules: romanianRules, .rulesSize: sizeof(romanianRules), .forms: romanianForms, .languages: romanianLanguages, .countries: 0, |
337 | .gettextRules: "nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);" }, |
338 | { .rules: slovenianRules, .rulesSize: sizeof(slovenianRules), .forms: slovenianForms, .languages: slovenianLanguage, .countries: 0, |
339 | .gettextRules: "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" }, |
340 | { .rules: malteseRules, .rulesSize: sizeof(malteseRules), .forms: malteseForms, .languages: malteseLanguage, .countries: 0, |
341 | .gettextRules: "nplurals=4; plural=(n==1 ? 0 : (n==0 || (n%100>=1 && n%100<=10)) ? 1 : (n%100>=11 && n%100<=19) ? 2 : 3);" }, |
342 | { .rules: welshRules, .rulesSize: sizeof(welshRules), .forms: welshForms, .languages: welshLanguage, .countries: 0, |
343 | .gettextRules: "nplurals=5; plural=(n==0 ? 0 : n==1 ? 1 : (n>=2 && n<=5) ? 2 : n==6 ? 3 : 4);" }, |
344 | { .rules: arabicRules, .rulesSize: sizeof(arabicRules), .forms: arabicForms, .languages: arabicLanguage, .countries: 0, |
345 | .gettextRules: "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : (n%100>=3 && n%100<=10) ? 3 : n%100>=11 ? 4 : 5);" }, |
346 | { .rules: tagalogRules, .rulesSize: sizeof(tagalogRules), .forms: tagalogForms, .languages: tagalogLanguage, .countries: 0, |
347 | .gettextRules: "nplurals=3; plural=(n==1 ? 0 : (n%10==4 || n%10==6 || n%10== 9) ? 1 : 2);" }, |
348 | }; |
349 | |
350 | static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]); |
351 | |
352 | bool getNumerusInfo(QLocale::Language language, QLocale::Country country, |
353 | QByteArray *rules, QStringList *forms, const char **gettextRules) |
354 | { |
355 | while (true) { |
356 | for (int i = 0; i < NumerusTableSize; ++i) { |
357 | const NumerusTableEntry &entry = numerusTable[i]; |
358 | for (int j = 0; entry.languages[j] != EOL; ++j) { |
359 | if (entry.languages[j] == language |
360 | && ((!entry.countries && country == QLocale::AnyCountry) |
361 | || (entry.countries && entry.countries[j] == country))) { |
362 | if (rules) { |
363 | *rules = QByteArray::fromRawData(reinterpret_cast<const char *>(entry.rules), |
364 | size: entry.rulesSize); |
365 | } |
366 | if (gettextRules) |
367 | *gettextRules = entry.gettextRules; |
368 | if (forms) { |
369 | forms->clear(); |
370 | for (int k = 0; entry.forms[k]; ++k) |
371 | forms->append(t: QLatin1String(entry.forms[k])); |
372 | } |
373 | return true; |
374 | } |
375 | } |
376 | } |
377 | |
378 | if (country == QLocale::AnyCountry) |
379 | break; |
380 | country = QLocale::AnyCountry; |
381 | } |
382 | return false; |
383 | } |
384 | |
385 | QString getNumerusInfoString() |
386 | { |
387 | QStringList langs; |
388 | |
389 | for (int i = 0; i < NumerusTableSize; ++i) { |
390 | const NumerusTableEntry &entry = numerusTable[i]; |
391 | for (int j = 0; entry.languages[j] != EOL; ++j) { |
392 | QLocale loc(entry.languages[j], entry.countries ? entry.countries[j] : QLocale::AnyCountry); |
393 | QString lang = QLocale::languageToString(language: entry.languages[j]); |
394 | if (loc.language() == QLocale::C) |
395 | lang += QLatin1String(" (!!!)" ); |
396 | else if (entry.countries && entry.countries[j] != QLocale::AnyCountry) |
397 | lang += QLatin1String(" (" ) + QLocale::countryToString(country: loc.country()) + QLatin1Char(')'); |
398 | else |
399 | lang += QLatin1String(" [" ) + QLocale::countryToString(country: loc.country()) + QLatin1Char(']'); |
400 | langs << QString::fromLatin1(str: "%1 %2 %3\n" ).arg(a: lang, fieldWidth: -40).arg(a: loc.name(), fieldWidth: -8) |
401 | .arg(a: QString::fromLatin1(str: entry.gettextRules)); |
402 | } |
403 | } |
404 | langs.sort(); |
405 | return langs.join(sep: QString()); |
406 | } |
407 | |
408 | QT_END_NAMESPACE |
409 | |