source: josm/trunk/src/org/openstreetmap/josm/tools/I18n.java@ 8227

Last change on this file since 8227 was 8227, checked in by stoecker, 9 years ago

fix #11148 - add Valencian language

  • Property svn:eol-style set to native
File size: 29.6 KB
Line 
1package org.openstreetmap.josm.tools;
2
3import java.io.BufferedInputStream;
4import java.io.File;
5import java.io.FileInputStream;
6import java.io.IOException;
7import java.io.InputStream;
8import java.net.URL;
9import java.nio.charset.StandardCharsets;
10import java.text.MessageFormat;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Comparator;
15import java.util.HashMap;
16import java.util.Locale;
17import java.util.Map;
18import java.util.jar.JarInputStream;
19import java.util.zip.ZipEntry;
20
21import javax.swing.JColorChooser;
22import javax.swing.JFileChooser;
23import javax.swing.UIManager;
24
25import org.openstreetmap.gui.jmapviewer.FeatureAdapter.TranslationAdapter;
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
28
29/**
30 * Internationalisation support.
31 *
32 * @author Immanuel.Scholz
33 */
34public final class I18n {
35
36 private I18n() {
37 // Hide default constructor for utils classes
38 }
39
40 /**
41 * Enumeration of possible plural modes. It allows us to identify and implement logical conditions of
42 * plural forms defined on <a href="https://help.launchpad.net/Translations/PluralForms">Launchpad</a>.
43 * See <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html">CLDR</a>
44 * for another complete list.
45 * @see #pluralEval
46 */
47 private enum PluralMode {
48 /** Plural = Not 1. This is the default for many languages, including English: 1 day, but 0 days or 2 days. */
49 MODE_NOTONE,
50 /** No plural. Mainly for Asian languages (Indonesian, Chinese, Japanese, ...) */
51 MODE_NONE,
52 /** Plural = Greater than 1. For some latin languages (French, Brazilian Portuguese) */
53 MODE_GREATERONE,
54 /* Special mode for
55 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar">Arabic</a>.*
56 MODE_AR,*/
57 /** Special mode for
58 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#cs">Czech</a>. */
59 MODE_CS,
60 /** Special mode for
61 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#pl">Polish</a>. */
62 MODE_PL,
63 /* Special mode for
64 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ro">Romanian</a>.*
65 MODE_RO,*/
66 /** Special mode for
67 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#lt">Lithuanian</a>. */
68 MODE_LT,
69 /** Special mode for
70 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ru">Russian</a>. */
71 MODE_RU,
72 /** Special mode for
73 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#sk">Slovak</a>. */
74 MODE_SK,
75 /* Special mode for
76 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#sl">Slovenian</a>.*
77 MODE_SL,*/
78 }
79
80 private static volatile PluralMode pluralMode = PluralMode.MODE_NOTONE; /* english default */
81 private static volatile String loadedCode = "en";
82
83 /* Localization keys for file chooser (and color chooser). */
84 private static final String[] javaInternalMessageKeys = new String[] {
85 /* JFileChooser windows laf */
86 "FileChooser.detailsViewActionLabelText",
87 "FileChooser.detailsViewButtonAccessibleName",
88 "FileChooser.detailsViewButtonToolTipText",
89 "FileChooser.fileAttrHeaderText",
90 "FileChooser.fileDateHeaderText",
91 "FileChooser.fileNameHeaderText",
92 "FileChooser.fileNameLabelText",
93 "FileChooser.fileSizeHeaderText",
94 "FileChooser.fileTypeHeaderText",
95 "FileChooser.filesOfTypeLabelText",
96 "FileChooser.homeFolderAccessibleName",
97 "FileChooser.homeFolderToolTipText",
98 "FileChooser.listViewActionLabelText",
99 "FileChooser.listViewButtonAccessibleName",
100 "FileChooser.listViewButtonToolTipText",
101 "FileChooser.lookInLabelText",
102 "FileChooser.newFolderAccessibleName",
103 "FileChooser.newFolderActionLabelText",
104 "FileChooser.newFolderToolTipText",
105 "FileChooser.refreshActionLabelText",
106 "FileChooser.saveInLabelText",
107 "FileChooser.upFolderAccessibleName",
108 "FileChooser.upFolderToolTipText",
109 "FileChooser.viewMenuLabelText",
110
111 /* JFileChooser gtk laf */
112 "FileChooser.acceptAllFileFilterText",
113 "FileChooser.cancelButtonText",
114 "FileChooser.cancelButtonToolTipText",
115 "FileChooser.deleteFileButtonText",
116 "FileChooser.filesLabelText",
117 "FileChooser.filterLabelText",
118 "FileChooser.foldersLabelText",
119 "FileChooser.newFolderButtonText",
120 "FileChooser.newFolderDialogText",
121 "FileChooser.openButtonText",
122 "FileChooser.openButtonToolTipText",
123 "FileChooser.openDialogTitleText",
124 "FileChooser.pathLabelText",
125 "FileChooser.renameFileButtonText",
126 "FileChooser.renameFileDialogText",
127 "FileChooser.renameFileErrorText",
128 "FileChooser.renameFileErrorTitle",
129 "FileChooser.saveButtonText",
130 "FileChooser.saveButtonToolTipText",
131 "FileChooser.saveDialogTitleText",
132
133 /* JFileChooser motif laf */
134 //"FileChooser.cancelButtonText",
135 //"FileChooser.cancelButtonToolTipText",
136 "FileChooser.enterFileNameLabelText",
137 //"FileChooser.filesLabelText",
138 //"FileChooser.filterLabelText",
139 //"FileChooser.foldersLabelText",
140 "FileChooser.helpButtonText",
141 "FileChooser.helpButtonToolTipText",
142 //"FileChooser.openButtonText",
143 //"FileChooser.openButtonToolTipText",
144 //"FileChooser.openDialogTitleText",
145 //"FileChooser.pathLabelText",
146 //"FileChooser.saveButtonText",
147 //"FileChooser.saveButtonToolTipText",
148 //"FileChooser.saveDialogTitleText",
149 "FileChooser.updateButtonText",
150 "FileChooser.updateButtonToolTipText",
151
152 /* gtk color chooser */
153 "GTKColorChooserPanel.blueText",
154 "GTKColorChooserPanel.colorNameText",
155 "GTKColorChooserPanel.greenText",
156 "GTKColorChooserPanel.hueText",
157 "GTKColorChooserPanel.nameText",
158 "GTKColorChooserPanel.redText",
159 "GTKColorChooserPanel.saturationText",
160 "GTKColorChooserPanel.valueText",
161
162 /* JOptionPane */
163 "OptionPane.okButtonText",
164 "OptionPane.yesButtonText",
165 "OptionPane.noButtonText",
166 "OptionPane.cancelButtonText"
167 };
168 private static volatile Map<String, String> strings = null;
169 private static volatile Map<String, String[]> pstrings = null;
170 private static Map<String, PluralMode> languages = new HashMap<>();
171
172 /**
173 * Translates some text for the current locale.
174 * These strings are collected by a script that runs on the source code files.
175 * After translation, the localizations are distributed with the main program.
176 * <br>
177 * For example, <code>tr("JOSM''s default value is ''{0}''.", val)</code>.
178 * <br>
179 * Use {@link #trn} for distinguishing singular from plural text, i.e.,
180 * do not use {@code tr(size == 1 ? "singular" : "plural")} nor
181 * {@code size == 1 ? tr("singular") : tr("plural")}
182 *
183 * @param text the text to translate.
184 * Must be a string literal. (No constants or local vars.)
185 * Can be broken over multiple lines.
186 * An apostrophe ' must be quoted by another apostrophe.
187 * @param objects the parameters for the string.
188 * Mark occurrences in {@code text} with <code>{0}</code>, <code>{1}</code>, ...
189 * @return the translated string.
190 * @see #trn
191 * @see #trc
192 * @see #trnc
193 */
194 public static final String tr(String text, Object... objects) {
195 if (text == null) return null;
196 return MessageFormat.format(gettext(text, null), objects);
197 }
198
199 /**
200 * Translates some text in a context for the current locale.
201 * There can be different translations for the same text within different contexts.
202 *
203 * @param context string that helps translators to find an appropriate
204 * translation for {@code text}.
205 * @param text the text to translate.
206 * @return the translated string.
207 * @see #tr
208 * @see #trn
209 * @see #trnc
210 */
211 public static final String trc(String context, String text) {
212 if (context == null)
213 return tr(text);
214 if (text == null)
215 return null;
216 return MessageFormat.format(gettext(text, context), (Object)null);
217 }
218
219 public static final String trc_lazy(String context, String text) {
220 if (context == null)
221 return tr(text);
222 if (text == null)
223 return null;
224 return MessageFormat.format(gettext_lazy(text, context), (Object)null);
225 }
226
227 /**
228 * Marks a string for translation (such that a script can harvest
229 * the translatable strings from the source files).
230 *
231 * For example, <code>
232 * String[] options = new String[] {marktr("up"), marktr("down")};
233 * lbl.setText(tr(options[0]));</code>
234 * @param text the string to be marked for translation.
235 * @return {@code text} unmodified.
236 */
237 public static final String marktr(String text) {
238 return text;
239 }
240
241 public static final String marktrc(String context, String text) {
242 return text;
243 }
244
245 /**
246 * Translates some text for the current locale and distinguishes between
247 * {@code singularText} and {@code pluralText} depending on {@code n}.
248 * <br>
249 * For instance, {@code trn("There was an error!", "There were errors!", i)} or
250 * <code>trn("Found {0} error in {1}!", "Found {0} errors in {1}!", i, Integer.toString(i), url)</code>.
251 *
252 * @param singularText the singular text to translate.
253 * Must be a string literal. (No constants or local vars.)
254 * Can be broken over multiple lines.
255 * An apostrophe ' must be quoted by another apostrophe.
256 * @param pluralText the plural text to translate.
257 * Must be a string literal. (No constants or local vars.)
258 * Can be broken over multiple lines.
259 * An apostrophe ' must be quoted by another apostrophe.
260 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used.
261 * @param objects the parameters for the string.
262 * Mark occurrences in {@code singularText} and {@code pluralText} with <code>{0}</code>, <code>{1}</code>, ...
263 * @return the translated string.
264 * @see #tr
265 * @see #trc
266 * @see #trnc
267 */
268 public static final String trn(String singularText, String pluralText, long n, Object... objects) {
269 return MessageFormat.format(gettextn(singularText, pluralText, null, n), objects);
270 }
271
272 /**
273 * Translates some text in a context for the current locale and distinguishes between
274 * {@code singularText} and {@code pluralText} depending on {@code n}.
275 * There can be different translations for the same text within different contexts.
276 *
277 * @param context string that helps translators to find an appropriate
278 * translation for {@code text}.
279 * @param singularText the singular text to translate.
280 * Must be a string literal. (No constants or local vars.)
281 * Can be broken over multiple lines.
282 * An apostrophe ' must be quoted by another apostrophe.
283 * @param pluralText the plural text to translate.
284 * Must be a string literal. (No constants or local vars.)
285 * Can be broken over multiple lines.
286 * An apostrophe ' must be quoted by another apostrophe.
287 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used.
288 * @param objects the parameters for the string.
289 * Mark occurrences in {@code singularText} and {@code pluralText} with <code>{0}</code>, <code>{1}</code>, ...
290 * @return the translated string.
291 * @see #tr
292 * @see #trc
293 * @see #trn
294 */
295 public static final String trnc(String context, String singularText, String pluralText, long n, Object... objects) {
296 return MessageFormat.format(gettextn(singularText, pluralText, context, n), objects);
297 }
298
299 private static final String gettext(String text, String ctx, boolean lazy) {
300 int i;
301 if(ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) {
302 ctx = text.substring(2,i-1);
303 text = text.substring(i+1);
304 }
305 if(strings != null) {
306 String trans = strings.get(ctx == null ? text : "_:"+ctx+"\n"+text);
307 if(trans != null)
308 return trans;
309 }
310 if(pstrings != null) {
311 i = pluralEval(1);
312 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text);
313 if(trans != null && trans.length > i)
314 return trans[i];
315 }
316 return lazy ? gettext(text, null) : text;
317 }
318
319 private static final String gettext(String text, String ctx) {
320 return gettext(text, ctx, false);
321 }
322
323 /* try without context, when context try fails */
324 private static final String gettext_lazy(String text, String ctx) {
325 return gettext(text, ctx, true);
326 }
327
328 private static final String gettextn(String text, String plural, String ctx, long num) {
329 int i;
330 if(ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) {
331 ctx = text.substring(2,i-1);
332 text = text.substring(i+1);
333 }
334 if(pstrings != null) {
335 i = pluralEval(num);
336 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text);
337 if(trans != null && trans.length > i)
338 return trans[i];
339 }
340
341 return num == 1 ? text : plural;
342 }
343
344 public static String escape(String msg) {
345 if (msg == null) return null;
346 return msg.replace("\'", "\'\'").replace("{", "\'{\'").replace("}", "\'}\'");
347 }
348
349 private static URL getTranslationFile(String lang) {
350 return Main.class.getResource("/data/"+lang.replace("@","-")+".lang");
351 }
352
353 /**
354 * Get a list of all available JOSM Translations.
355 * @return an array of locale objects.
356 */
357 public static final Locale[] getAvailableTranslations() {
358 Collection<Locale> v = new ArrayList<>(languages.size());
359 if(getTranslationFile("en") != null) {
360 for (String loc : languages.keySet()) {
361 if(getTranslationFile(loc) != null) {
362 v.add(LanguageInfo.getLocale(loc));
363 }
364 }
365 }
366 v.add(Locale.ENGLISH);
367 Locale[] l = new Locale[v.size()];
368 l = v.toArray(l);
369 Arrays.sort(l, new Comparator<Locale>() {
370 @Override
371 public int compare(Locale o1, Locale o2) {
372 return o1.toString().compareTo(o2.toString());
373 }
374 });
375 return l;
376 }
377
378 /**
379 * Determines if a language exists for the given code.
380 * @param code The language code
381 * @return {@code true} if a language exists, {@code false} otherwise
382 */
383 public static boolean hasCode(String code) {
384 return languages.containsKey(code);
385 }
386
387 /**
388 * I18n initialization.
389 */
390 public static void init() {
391 // Enable CLDR locale provider on Java 8 to get additional languages, such as Khmer.
392 // http://docs.oracle.com/javase/8/docs/technotes/guides/intl/enhancements.8.html#cldr
393 // FIXME: This can be removed after we switch to a minimal version of Java that enables CLDR by default
394 // or includes all languages we need in the JRE. See http://openjdk.java.net/jeps/8043554 for Java 9
395 Utils.updateSystemProperty("java.locale.providers", "JRE,CLDR");
396
397 //languages.put("ar", PluralMode.MODE_AR);
398 languages.put("ast", PluralMode.MODE_NOTONE);
399 languages.put("bg", PluralMode.MODE_NOTONE);
400 languages.put("ca", PluralMode.MODE_NOTONE);
401 languages.put("ca@valencia", PluralMode.MODE_NOTONE);
402 languages.put("cs", PluralMode.MODE_CS);
403 languages.put("da", PluralMode.MODE_NOTONE);
404 languages.put("de", PluralMode.MODE_NOTONE);
405 languages.put("el", PluralMode.MODE_NOTONE);
406 languages.put("en_AU", PluralMode.MODE_NOTONE);
407 languages.put("en_GB", PluralMode.MODE_NOTONE);
408 languages.put("es", PluralMode.MODE_NOTONE);
409 languages.put("et", PluralMode.MODE_NOTONE);
410 //languages.put("eu", PluralMode.MODE_NOTONE);
411 languages.put("fi", PluralMode.MODE_NOTONE);
412 languages.put("fr", PluralMode.MODE_GREATERONE);
413 languages.put("gl", PluralMode.MODE_NOTONE);
414 //languages.put("he", PluralMode.MODE_NOTONE);
415 languages.put("hu", PluralMode.MODE_NOTONE);
416 languages.put("id", PluralMode.MODE_NONE);
417 //languages.put("is", PluralMode.MODE_NOTONE);
418 languages.put("it", PluralMode.MODE_NOTONE);
419 languages.put("ja", PluralMode.MODE_NONE);
420 // fully supported only with Java 8 and later (needs CLDR)
421 languages.put("km", PluralMode.MODE_NONE);
422 languages.put("lt", PluralMode.MODE_LT);
423 //languages.put("nb", PluralMode.MODE_NOTONE);
424 languages.put("nl", PluralMode.MODE_NOTONE);
425 languages.put("pl", PluralMode.MODE_PL);
426 languages.put("pt", PluralMode.MODE_NOTONE);
427 languages.put("pt_BR", PluralMode.MODE_GREATERONE);
428 //languages.put("ro", PluralMode.MODE_RO);
429 languages.put("ru", PluralMode.MODE_RU);
430 languages.put("sk", PluralMode.MODE_SK);
431 //languages.put("sl", PluralMode.MODE_SL);
432 languages.put("sv", PluralMode.MODE_NOTONE);
433 //languages.put("tr", PluralMode.MODE_NONE);
434 languages.put("uk", PluralMode.MODE_RU);
435 languages.put("zh_CN", PluralMode.MODE_NONE);
436 languages.put("zh_TW", PluralMode.MODE_NONE);
437
438 /* try initial language settings, may be changed later again */
439 if(!load(Locale.getDefault().toString())) {
440 Locale.setDefault(Locale.ENGLISH);
441 }
442 }
443
444 public static void addTexts(File source) {
445 if ("en".equals(loadedCode))
446 return;
447 String enfile = "data/en.lang";
448 String langfile = "data/"+loadedCode+".lang";
449 try (
450 FileInputStream fis = new FileInputStream(source);
451 JarInputStream jar = new JarInputStream(fis)
452 ) {
453 ZipEntry e;
454 boolean found = false;
455 while (!found && (e = jar.getNextEntry()) != null) {
456 String name = e.getName();
457 if(name.equals(enfile))
458 found = true;
459 }
460 if (found) {
461 try (
462 FileInputStream fisTrans = new FileInputStream(source);
463 JarInputStream jarTrans = new JarInputStream(fisTrans)
464 ) {
465 found = false;
466 while(!found && (e = jarTrans.getNextEntry()) != null) {
467 String name = e.getName();
468 if (name.equals(langfile))
469 found = true;
470 }
471 if (found)
472 load(jar, jarTrans, true);
473 }
474 }
475 } catch (IOException e) {
476 // Ignore
477 }
478 }
479
480 private static boolean load(String l) {
481 if ("en".equals(l) || "en_US".equals(l)) {
482 strings = null;
483 pstrings = null;
484 loadedCode = "en";
485 pluralMode = PluralMode.MODE_NOTONE;
486 return true;
487 }
488 URL en = getTranslationFile("en");
489 if (en == null)
490 return false;
491 URL tr = getTranslationFile(l);
492 if (tr == null || !languages.containsKey(l)) {
493 int i = l.indexOf('_');
494 if (i > 0) {
495 l = l.substring(0, i);
496 }
497 tr = getTranslationFile(l);
498 if (tr == null || !languages.containsKey(l))
499 return false;
500 }
501 try (
502 InputStream enStream = en.openStream();
503 InputStream trStream = tr.openStream()
504 ) {
505 if (load(enStream, trStream, false)) {
506 pluralMode = languages.get(l);
507 loadedCode = l;
508 return true;
509 }
510 } catch (IOException e) {
511 // Ignore exception
512 }
513 return false;
514 }
515
516 private static boolean load(InputStream en, InputStream tr, boolean add) {
517 Map<String, String> s;
518 Map<String, String[]> p;
519 if (add) {
520 s = strings;
521 p = pstrings;
522 } else {
523 s = new HashMap<>();
524 p = new HashMap<>();
525 }
526 /* file format:
527 Files are always a group. English file and translated file must provide identical datasets.
528
529 for all single strings:
530 {
531 unsigned short (2 byte) stringlength
532 - length 0 indicates missing translation
533 - length 0xFFFE indicates translation equal to original, but otherwise is equal to length 0
534 string
535 }
536 unsigned short (2 byte) 0xFFFF (marks end of single strings)
537 for all multi strings:
538 {
539 unsigned char (1 byte) stringcount
540 - count 0 indicates missing translations
541 - count 0xFE indicates translations equal to original, but otherwise is equal to length 0
542 for stringcount
543 unsigned short (2 byte) stringlength
544 string
545 }
546 */
547 try {
548 InputStream ens = new BufferedInputStream(en);
549 InputStream trs = new BufferedInputStream(tr);
550 byte[] enlen = new byte[2];
551 byte[] trlen = new byte[2];
552 boolean multimode = false;
553 byte[] str = new byte[4096];
554 for(;;) {
555 if(multimode) {
556 int ennum = ens.read();
557 int trnum = trs.read();
558 if(trnum == 0xFE) /* marks identical string, handle equally to non-translated */
559 trnum = 0;
560 if((ennum == -1 && trnum != -1) || (ennum != -1 && trnum == -1)) /* files do not match */
561 return false;
562 if(ennum == -1) {
563 break;
564 }
565 String[] enstrings = new String[ennum];
566 String[] trstrings = new String[trnum];
567 for(int i = 0; i < ennum; ++i) {
568 int val = ens.read(enlen);
569 if(val != 2) /* file corrupt */
570 return false;
571 val = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]);
572 if(val > str.length) {
573 str = new byte[val];
574 }
575 int rval = ens.read(str, 0, val);
576 if(rval != val) /* file corrupt */
577 return false;
578 enstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8);
579 }
580 for(int i = 0; i < trnum; ++i) {
581 int val = trs.read(trlen);
582 if(val != 2) /* file corrupt */
583 return false;
584 val = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]);
585 if(val > str.length) {
586 str = new byte[val];
587 }
588 int rval = trs.read(str, 0, val);
589 if(rval != val) /* file corrupt */
590 return false;
591 trstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8);
592 }
593 if(trnum > 0 && !p.containsKey(enstrings[0])) {
594 p.put(enstrings[0], trstrings);
595 }
596 } else {
597 int enval = ens.read(enlen);
598 int trval = trs.read(trlen);
599 if(enval != trval) /* files do not match */
600 return false;
601 if(enval == -1) {
602 break;
603 }
604 if(enval != 2) /* files corrupt */
605 return false;
606 enval = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]);
607 trval = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]);
608 if(trval == 0xFFFE) /* marks identical string, handle equally to non-translated */
609 trval = 0;
610 if(enval == 0xFFFF) {
611 multimode = true;
612 if(trval != 0xFFFF) /* files do not match */
613 return false;
614 } else {
615 if (enval > str.length) {
616 str = new byte[enval];
617 }
618 if (trval > str.length) {
619 str = new byte[trval];
620 }
621 int val = ens.read(str, 0, enval);
622 if(val != enval) /* file corrupt */
623 return false;
624 String enstr = new String(str, 0, enval, StandardCharsets.UTF_8);
625 if (trval != 0) {
626 val = trs.read(str, 0, trval);
627 if(val != trval) /* file corrupt */
628 return false;
629 String trstr = new String(str, 0, trval, StandardCharsets.UTF_8);
630 if(!s.containsKey(enstr))
631 s.put(enstr, trstr);
632 }
633 }
634 }
635 }
636 } catch (IOException e) {
637 return false;
638 }
639 if (!s.isEmpty()) {
640 strings = s;
641 pstrings = p;
642 return true;
643 }
644 return false;
645 }
646
647 /**
648 * Sets the default locale (see {@link Locale#setDefault(Locale)} to the local
649 * given by <code>localName</code>.
650 *
651 * Ignored if localeName is null. If the locale with name <code>localName</code>
652 * isn't found the default local is set to <tt>en</tt> (english).
653 *
654 * @param localeName the locale name. Ignored if null.
655 */
656 public static void set(String localeName){
657 if (localeName != null) {
658 Locale l = LanguageInfo.getLocale(localeName);
659 if (load(LanguageInfo.getJOSMLocaleCode(l))) {
660 Locale.setDefault(l);
661 } else {
662 if (!"en".equals(l.getLanguage())) {
663 Main.info(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
664 l.getDisplayName(), Locale.getDefault().getDisplayName()));
665 } else {
666 strings = null;
667 pstrings = null;
668 }
669 }
670 }
671 }
672
673 /**
674 * Localizations for file chooser dialog.
675 * For some locales (e.g. de, fr) translations are provided
676 * by Java, but not for others (e.g. ru, uk).
677 */
678 public static void translateJavaInternalMessages() {
679 Locale l = Locale.getDefault();
680
681 AbstractFileChooser.setDefaultLocale(l);
682 JFileChooser.setDefaultLocale(l);
683 JColorChooser.setDefaultLocale(l);
684 for (String key : javaInternalMessageKeys) {
685 String us = UIManager.getString(key, Locale.US);
686 String loc = UIManager.getString(key, l);
687 // only provide custom translation if it is not already localized by Java
688 if (us != null && us.equals(loc)) {
689 UIManager.put(key, tr(us));
690 }
691 }
692 }
693
694 private static int pluralEval(long n) {
695 switch(pluralMode) {
696 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, eu, fi, gl, is, it, iw_IL, nb, nl, sv */
697 return ((n != 1) ? 1 : 0);
698 case MODE_NONE: /* id, ja, km, tr, zh_CN, zh_TW */
699 return 0;
700 case MODE_GREATERONE: /* fr, pt_BR */
701 return ((n > 1) ? 1 : 0);
702 case MODE_CS:
703 return ((n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2));
704 //case MODE_AR:
705 // return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3)
706 // && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5)))));
707 case MODE_PL:
708 return ((n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4))
709 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2));
710 //case MODE_RO:
711 // return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1));
712 case MODE_LT:
713 return (((n % 10) == 1) && ((n % 100) != 11) ? 0 : (((n % 10) >= 2)
714 && (((n % 100) < 10) || ((n % 100) >= 20)) ? 1 : 2));
715 case MODE_RU:
716 return ((((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2)
717 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2));
718 case MODE_SK:
719 return ((n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0));
720 //case MODE_SL:
721 // return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3)
722 // || ((n % 100) == 4)) ? 3 : 0)));
723 }
724 return 0;
725 }
726
727 public static TranslationAdapter getTranslationAdapter() {
728 return new TranslationAdapter() {
729 @Override
730 public String tr(String text, Object... objects) {
731 return I18n.tr(text, objects);
732 }
733 };
734 }
735}
Note: See TracBrowser for help on using the repository browser.