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

Last change on this file since 10638 was 10627, checked in by Don-vip, 8 years ago

sonar - squid:S1166 - Exception handlers should preserve the original exceptions

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