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

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

fix #11381 - default language setting failed

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