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

Last change on this file since 8934 was 8846, checked in by Don-vip, 9 years ago

sonar - fb-contrib - minor performance improvements:

  • Method passes constant String of length 1 to character overridden method
  • Method needlessly boxes a boolean constant
  • Method uses iterator().next() on a List to get the first item
  • Method converts String to boxed primitive using excessive boxing
  • Method converts String to primitive using excessive boxing
  • Method creates array using constants
  • Class defines List based fields but uses them like Sets
  • Property svn:eol-style set to native
File size: 30.7 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;
174 private static volatile Map<String, String[]> pstrings;
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 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 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 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 String marktr(String text) {
243 return text;
244 }
245
246 public static 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 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 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 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 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 String gettext_lazy(String text, String ctx) {
330 return gettext(text, ctx, true);
331 }
332
333 private static 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 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("be", PluralMode.MODE_RU);
406 languages.put("ca", PluralMode.MODE_NOTONE);
407 languages.put("ca@valencia", PluralMode.MODE_NOTONE);
408 languages.put("cs", PluralMode.MODE_CS);
409 languages.put("da", PluralMode.MODE_NOTONE);
410 languages.put("de", PluralMode.MODE_NOTONE);
411 languages.put("el", PluralMode.MODE_NOTONE);
412 languages.put("en_AU", PluralMode.MODE_NOTONE);
413 languages.put("en_GB", PluralMode.MODE_NOTONE);
414 languages.put("es", PluralMode.MODE_NOTONE);
415 languages.put("et", PluralMode.MODE_NOTONE);
416 //languages.put("eu", PluralMode.MODE_NOTONE);
417 languages.put("fi", PluralMode.MODE_NOTONE);
418 languages.put("fr", PluralMode.MODE_GREATERONE);
419 languages.put("gl", PluralMode.MODE_NOTONE);
420 //languages.put("he", PluralMode.MODE_NOTONE);
421 languages.put("hu", PluralMode.MODE_NOTONE);
422 languages.put("id", PluralMode.MODE_NONE);
423 //languages.put("is", PluralMode.MODE_NOTONE);
424 languages.put("it", PluralMode.MODE_NOTONE);
425 languages.put("ja", PluralMode.MODE_NONE);
426 // fully supported only with Java 8 and later (needs CLDR)
427 languages.put("km", PluralMode.MODE_NONE);
428 languages.put("lt", PluralMode.MODE_LT);
429 //languages.put("nb", PluralMode.MODE_NOTONE);
430 languages.put("nl", PluralMode.MODE_NOTONE);
431 languages.put("pl", PluralMode.MODE_PL);
432 languages.put("pt", PluralMode.MODE_NOTONE);
433 languages.put("pt_BR", PluralMode.MODE_GREATERONE);
434 //languages.put("ro", PluralMode.MODE_RO);
435 languages.put("ru", PluralMode.MODE_RU);
436 languages.put("sk", PluralMode.MODE_SK);
437 //languages.put("sl", PluralMode.MODE_SL);
438 languages.put("sv", PluralMode.MODE_NOTONE);
439 //languages.put("tr", PluralMode.MODE_NONE);
440 languages.put("uk", PluralMode.MODE_RU);
441 languages.put("vi", PluralMode.MODE_NONE);
442 languages.put("zh_CN", PluralMode.MODE_NONE);
443 languages.put("zh_TW", PluralMode.MODE_NONE);
444
445 /* try initial language settings, may be changed later again */
446 if (!load(LanguageInfo.getJOSMLocaleCode())) {
447 Locale.setDefault(Locale.ENGLISH);
448 }
449 }
450
451 public static void addTexts(File source) {
452 if ("en".equals(loadedCode))
453 return;
454 final String enfile = "data/en.lang";
455 final String langfile = "data/"+loadedCode+".lang";
456 try (
457 FileInputStream fis = new FileInputStream(source);
458 JarInputStream jar = new JarInputStream(fis)
459 ) {
460 ZipEntry e;
461 boolean found = false;
462 while (!found && (e = jar.getNextEntry()) != null) {
463 String name = e.getName();
464 if (enfile.equals(name))
465 found = true;
466 }
467 if (found) {
468 try (
469 FileInputStream fisTrans = new FileInputStream(source);
470 JarInputStream jarTrans = new JarInputStream(fisTrans)
471 ) {
472 found = false;
473 while (!found && (e = jarTrans.getNextEntry()) != null) {
474 String name = e.getName();
475 if (name.equals(langfile))
476 found = true;
477 }
478 if (found)
479 load(jar, jarTrans, true);
480 }
481 }
482 } catch (IOException e) {
483 // Ignore
484 if (Main.isTraceEnabled()) {
485 Main.trace(e.getMessage());
486 }
487 }
488 }
489
490 private static boolean load(String l) {
491 if ("en".equals(l) || "en_US".equals(l)) {
492 strings = null;
493 pstrings = null;
494 loadedCode = "en";
495 pluralMode = PluralMode.MODE_NOTONE;
496 return true;
497 }
498 URL en = getTranslationFile("en");
499 if (en == null)
500 return false;
501 URL tr = getTranslationFile(l);
502 if (tr == null || !languages.containsKey(l)) {
503 return false;
504 }
505 try (
506 InputStream enStream = en.openStream();
507 InputStream trStream = tr.openStream()
508 ) {
509 if (load(enStream, trStream, false)) {
510 pluralMode = languages.get(l);
511 loadedCode = l;
512 return true;
513 }
514 } catch (IOException e) {
515 // Ignore exception
516 if (Main.isTraceEnabled()) {
517 Main.trace(e.getMessage());
518 }
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 String[] trstrings = new String[trnum];
574 for (int i = 0; i < ennum; ++i) {
575 int val = ens.read(enlen);
576 if (val != 2) /* file corrupt */
577 return false;
578 val = (enlen[0] < 0 ? 256+enlen[0] : enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1] : enlen[1]);
579 if (val > str.length) {
580 str = new byte[val];
581 }
582 int rval = ens.read(str, 0, val);
583 if (rval != val) /* file corrupt */
584 return false;
585 enstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8);
586 }
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 return false;
645 }
646 if (!s.isEmpty()) {
647 strings = s;
648 pstrings = p;
649 return true;
650 }
651 return false;
652 }
653
654 /**
655 * Sets the default locale (see {@link Locale#setDefault(Locale)} to the local
656 * given by <code>localName</code>.
657 *
658 * Ignored if localeName is null. If the locale with name <code>localName</code>
659 * isn't found the default local is set to <tt>en</tt> (english).
660 *
661 * @param localeName the locale name. Ignored if null.
662 */
663 public static void set(String localeName) {
664 if (localeName != null) {
665 Locale l = LanguageInfo.getLocale(localeName);
666 if (load(LanguageInfo.getJOSMLocaleCode(l))) {
667 Locale.setDefault(l);
668 } else {
669 if (!"en".equals(l.getLanguage())) {
670 Main.info(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
671 LanguageInfo.getDisplayName(l), LanguageInfo.getDisplayName(Locale.getDefault())));
672 } else {
673 strings = null;
674 pstrings = null;
675 }
676 }
677 }
678 }
679
680 /**
681 * Localizations for file chooser dialog.
682 * For some locales (e.g. de, fr) translations are provided
683 * by Java, but not for others (e.g. ru, uk).
684 */
685 public static void translateJavaInternalMessages() {
686 Locale l = Locale.getDefault();
687
688 AbstractFileChooser.setDefaultLocale(l);
689 JFileChooser.setDefaultLocale(l);
690 JColorChooser.setDefaultLocale(l);
691 for (String key : javaInternalMessageKeys) {
692 String us = UIManager.getString(key, Locale.US);
693 String loc = UIManager.getString(key, l);
694 // only provide custom translation if it is not already localized by Java
695 if (us != null && us.equals(loc)) {
696 UIManager.put(key, tr(us));
697 }
698 }
699 }
700
701 private static int pluralEval(long n) {
702 switch(pluralMode) {
703 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, eu, fi, gl, is, it, iw_IL, nb, nl, sv */
704 return (n != 1) ? 1 : 0;
705 case MODE_NONE: /* id, vi, ja, km, tr, zh_CN, zh_TW */
706 return 0;
707 case MODE_GREATERONE: /* fr, pt_BR */
708 return (n > 1) ? 1 : 0;
709 case MODE_CS:
710 return (n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2);
711 //case MODE_AR:
712 // return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3)
713 // && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5)))));
714 case MODE_PL:
715 return (n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4))
716 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2);
717 //case MODE_RO:
718 // return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1));
719 case MODE_LT:
720 return ((n % 10) == 1) && ((n % 100) != 11) ? 0 : (((n % 10) >= 2)
721 && (((n % 100) < 10) || ((n % 100) >= 20)) ? 1 : 2);
722 case MODE_RU:
723 return (((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2)
724 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2);
725 case MODE_SK:
726 return (n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0);
727 //case MODE_SL:
728 // return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3)
729 // || ((n % 100) == 4)) ? 3 : 0)));
730 }
731 return 0;
732 }
733
734 public static TranslationAdapter getTranslationAdapter() {
735 return new TranslationAdapter() {
736 @Override
737 public String tr(String text, Object... objects) {
738 return I18n.tr(text, objects);
739 }
740 };
741 }
742
743 /**
744 * Setup special font for Khmer script, as the default Java fonts do not display these characters.
745 *
746 * @since 8282
747 */
748 public static void setupLanguageFonts() {
749 // Use special font for Khmer script, as the default Java font do not display these characters
750 if ("km".equals(LanguageInfo.getJOSMLocaleCode())) {
751 Collection<String> fonts = Arrays.asList(
752 GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
753 for (String f : new String[]{"Khmer UI", "DaunPenh", "MoolBoran"}) {
754 if (fonts.contains(f)) {
755 GuiHelper.setUIFont(f);
756 break;
757 }
758 }
759 }
760 }
761}
Note: See TracBrowser for help on using the repository browser.