001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.io.BufferedInputStream; 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.IOException; 008import java.io.InputStream; 009import java.net.URL; 010import java.nio.charset.StandardCharsets; 011import java.text.MessageFormat; 012import java.util.ArrayList; 013import java.util.Arrays; 014import java.util.Collection; 015import java.util.Comparator; 016import java.util.HashMap; 017import java.util.Locale; 018import java.util.Map; 019import java.util.jar.JarInputStream; 020import java.util.zip.ZipEntry; 021 022import javax.swing.JColorChooser; 023import javax.swing.JFileChooser; 024import javax.swing.UIManager; 025 026import org.openstreetmap.gui.jmapviewer.FeatureAdapter.TranslationAdapter; 027import org.openstreetmap.josm.Main; 028import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; 029 030/** 031 * Internationalisation support. 032 * 033 * @author Immanuel.Scholz 034 */ 035public final class I18n { 036 037 private I18n() { 038 // Hide default constructor for utils classes 039 } 040 041 private enum PluralMode { MODE_NOTONE, MODE_NONE, MODE_GREATERONE, 042 MODE_CS/*, MODE_AR*/, MODE_PL/*, MODE_RO*/, MODE_RU, MODE_SK/*, MODE_SL*/} 043 private static PluralMode pluralMode = PluralMode.MODE_NOTONE; /* english default */ 044 private static String loadedCode = "en"; 045 046 /* Localization keys for file chooser (and color chooser). */ 047 private static final String[] javaInternalMessageKeys = new String[] { 048 /* JFileChooser windows laf */ 049 "FileChooser.detailsViewActionLabelText", 050 "FileChooser.detailsViewButtonAccessibleName", 051 "FileChooser.detailsViewButtonToolTipText", 052 "FileChooser.fileAttrHeaderText", 053 "FileChooser.fileDateHeaderText", 054 "FileChooser.fileNameHeaderText", 055 "FileChooser.fileNameLabelText", 056 "FileChooser.fileSizeHeaderText", 057 "FileChooser.fileTypeHeaderText", 058 "FileChooser.filesOfTypeLabelText", 059 "FileChooser.homeFolderAccessibleName", 060 "FileChooser.homeFolderToolTipText", 061 "FileChooser.listViewActionLabelText", 062 "FileChooser.listViewButtonAccessibleName", 063 "FileChooser.listViewButtonToolTipText", 064 "FileChooser.lookInLabelText", 065 "FileChooser.newFolderAccessibleName", 066 "FileChooser.newFolderActionLabelText", 067 "FileChooser.newFolderToolTipText", 068 "FileChooser.refreshActionLabelText", 069 "FileChooser.saveInLabelText", 070 "FileChooser.upFolderAccessibleName", 071 "FileChooser.upFolderToolTipText", 072 "FileChooser.viewMenuLabelText", 073 074 /* JFileChooser gtk laf */ 075 "FileChooser.acceptAllFileFilterText", 076 "FileChooser.cancelButtonText", 077 "FileChooser.cancelButtonToolTipText", 078 "FileChooser.deleteFileButtonText", 079 "FileChooser.filesLabelText", 080 "FileChooser.filterLabelText", 081 "FileChooser.foldersLabelText", 082 "FileChooser.newFolderButtonText", 083 "FileChooser.newFolderDialogText", 084 "FileChooser.openButtonText", 085 "FileChooser.openButtonToolTipText", 086 "FileChooser.openDialogTitleText", 087 "FileChooser.pathLabelText", 088 "FileChooser.renameFileButtonText", 089 "FileChooser.renameFileDialogText", 090 "FileChooser.renameFileErrorText", 091 "FileChooser.renameFileErrorTitle", 092 "FileChooser.saveButtonText", 093 "FileChooser.saveButtonToolTipText", 094 "FileChooser.saveDialogTitleText", 095 096 /* JFileChooser motif laf */ 097 //"FileChooser.cancelButtonText", 098 //"FileChooser.cancelButtonToolTipText", 099 "FileChooser.enterFileNameLabelText", 100 //"FileChooser.filesLabelText", 101 //"FileChooser.filterLabelText", 102 //"FileChooser.foldersLabelText", 103 "FileChooser.helpButtonText", 104 "FileChooser.helpButtonToolTipText", 105 //"FileChooser.openButtonText", 106 //"FileChooser.openButtonToolTipText", 107 //"FileChooser.openDialogTitleText", 108 //"FileChooser.pathLabelText", 109 //"FileChooser.saveButtonText", 110 //"FileChooser.saveButtonToolTipText", 111 //"FileChooser.saveDialogTitleText", 112 "FileChooser.updateButtonText", 113 "FileChooser.updateButtonToolTipText", 114 115 /* gtk color chooser */ 116 "GTKColorChooserPanel.blueText", 117 "GTKColorChooserPanel.colorNameText", 118 "GTKColorChooserPanel.greenText", 119 "GTKColorChooserPanel.hueText", 120 "GTKColorChooserPanel.nameText", 121 "GTKColorChooserPanel.redText", 122 "GTKColorChooserPanel.saturationText", 123 "GTKColorChooserPanel.valueText", 124 125 /* JOptionPane */ 126 "OptionPane.okButtonText", 127 "OptionPane.yesButtonText", 128 "OptionPane.noButtonText", 129 "OptionPane.cancelButtonText" 130 }; 131 private static Map<String, String> strings = null; 132 private static Map<String, String[]> pstrings = null; 133 private static Map<String, PluralMode> languages = new HashMap<>(); 134 135 /** 136 * Translates some text for the current locale. 137 * These strings are collected by a script that runs on the source code files. 138 * After translation, the localizations are distributed with the main program. 139 * <br> 140 * For example, {@code tr("JOSM''s default value is ''{0}''.", val)}. 141 * <br> 142 * Use {@link #trn} for distinguishing singular from plural text, i.e., 143 * do not use {@code tr(size == 1 ? "singular" : "plural")} nor 144 * {@code size == 1 ? tr("singular") : tr("plural")} 145 * 146 * @param text the text to translate. 147 * Must be a string literal. (No constants or local vars.) 148 * Can be broken over multiple lines. 149 * An apostrophe ' must be quoted by another apostrophe. 150 * @param objects the parameters for the string. 151 * Mark occurrences in {@code text} with {@code {0}}, {@code {1}}, ... 152 * @return the translated string. 153 * @see #trn 154 * @see #trc 155 * @see #trnc 156 */ 157 public static final String tr(String text, Object... objects) { 158 if (text == null) return null; 159 return MessageFormat.format(gettext(text, null), objects); 160 } 161 162 /** 163 * Translates some text in a context for the current locale. 164 * There can be different translations for the same text within different contexts. 165 * 166 * @param context string that helps translators to find an appropriate 167 * translation for {@code text}. 168 * @param text the text to translate. 169 * @return the translated string. 170 * @see #tr 171 * @see #trn 172 * @see #trnc 173 */ 174 public static final String trc(String context, String text) { 175 if (context == null) 176 return tr(text); 177 if (text == null) 178 return null; 179 return MessageFormat.format(gettext(text, context), (Object)null); 180 } 181 182 public static final String trc_lazy(String context, String text) { 183 if (context == null) 184 return tr(text); 185 if (text == null) 186 return null; 187 return MessageFormat.format(gettext_lazy(text, context), (Object)null); 188 } 189 190 /** 191 * Marks a string for translation (such that a script can harvest 192 * the translatable strings from the source files). 193 * 194 * For example, {@code 195 * String[] options = new String[] {marktr("up"), marktr("down")}; 196 * lbl.setText(tr(options[0]));} 197 * @param text the string to be marked for translation. 198 * @return {@code text} unmodified. 199 */ 200 public static final String marktr(String text) { 201 return text; 202 } 203 204 public static final String marktrc(String context, String text) { 205 return text; 206 } 207 208 /** 209 * Translates some text for the current locale and distinguishes between 210 * {@code singularText} and {@code pluralText} depending on {@code n}. 211 * <br> 212 * For instance, {@code trn("There was an error!", "There were errors!", i)} or 213 * {@code trn("Found {0} error in {1}!", "Found {0} errors in {1}!", i, Integer.toString(i), url)}. 214 * 215 * @param singularText the singular text to translate. 216 * Must be a string literal. (No constants or local vars.) 217 * Can be broken over multiple lines. 218 * An apostrophe ' must be quoted by another apostrophe. 219 * @param pluralText the plural text to translate. 220 * Must be a string literal. (No constants or local vars.) 221 * Can be broken over multiple lines. 222 * An apostrophe ' must be quoted by another apostrophe. 223 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used. 224 * @param objects the parameters for the string. 225 * Mark occurrences in {@code singularText} and {@code pluralText} with {@code {0}}, {@code {1}}, ... 226 * @return the translated string. 227 * @see #tr 228 * @see #trc 229 * @see #trnc 230 */ 231 public static final String trn(String singularText, String pluralText, long n, Object... objects) { 232 return MessageFormat.format(gettextn(singularText, pluralText, null, n), objects); 233 } 234 235 /** 236 * Translates some text in a context for the current locale and distinguishes between 237 * {@code singularText} and {@code pluralText} depending on {@code n}. 238 * There can be different translations for the same text within different contexts. 239 * 240 * @param context string that helps translators to find an appropriate 241 * translation for {@code text}. 242 * @param singularText the singular text to translate. 243 * Must be a string literal. (No constants or local vars.) 244 * Can be broken over multiple lines. 245 * An apostrophe ' must be quoted by another apostrophe. 246 * @param pluralText the plural text to translate. 247 * Must be a string literal. (No constants or local vars.) 248 * Can be broken over multiple lines. 249 * An apostrophe ' must be quoted by another apostrophe. 250 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used. 251 * @param objects the parameters for the string. 252 * Mark occurrences in {@code singularText} and {@code pluralText} with {@code {0}}, {@code {1}}, ... 253 * @return the translated string. 254 * @see #tr 255 * @see #trc 256 * @see #trn 257 */ 258 public static final String trnc(String context, String singularText, String pluralText, long n, Object... objects) { 259 return MessageFormat.format(gettextn(singularText, pluralText, context, n), objects); 260 } 261 262 private static final String gettext(String text, String ctx, boolean lazy) 263 { 264 int i; 265 if(ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) 266 { 267 ctx = text.substring(2,i-1); 268 text = text.substring(i+1); 269 } 270 if(strings != null) 271 { 272 String trans = strings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 273 if(trans != null) 274 return trans; 275 } 276 if(pstrings != null) { 277 i = pluralEval(1); 278 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 279 if(trans != null && trans.length > i) 280 return trans[i]; 281 } 282 return lazy ? gettext(text, null) : text; 283 } 284 285 private static final String gettext(String text, String ctx) { 286 return gettext(text, ctx, false); 287 } 288 289 290 /* try without context, when context try fails */ 291 private static final String gettext_lazy(String text, String ctx) { 292 return gettext(text, ctx, true); 293 } 294 295 private static final String gettextn(String text, String plural, String ctx, long num) 296 { 297 int i; 298 if(ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) 299 { 300 ctx = text.substring(2,i-1); 301 text = text.substring(i+1); 302 } 303 if(pstrings != null) 304 { 305 i = pluralEval(num); 306 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 307 if(trans != null && trans.length > i) 308 return trans[i]; 309 } 310 311 return num == 1 ? text : plural; 312 } 313 314 public static String escape(String msg) { 315 if (msg == null) return null; 316 return msg.replace("\'", "\'\'").replace("{", "\'{\'").replace("}", "\'}\'"); 317 } 318 319 private static URL getTranslationFile(String lang) { 320 return Main.class.getResource("/data/"+lang+".lang"); 321 } 322 323 /** 324 * Get a list of all available JOSM Translations. 325 * @return an array of locale objects. 326 */ 327 public static final Locale[] getAvailableTranslations() { 328 Collection<Locale> v = new ArrayList<>(languages.size()); 329 if(getTranslationFile("en") != null) 330 { 331 for (String loc : languages.keySet()) { 332 if(getTranslationFile(loc) != null) { 333 v.add(LanguageInfo.getLocale(loc)); 334 } 335 } 336 } 337 v.add(Locale.ENGLISH); 338 Locale[] l = new Locale[v.size()]; 339 l = v.toArray(l); 340 Arrays.sort(l, new Comparator<Locale>() { 341 @Override 342 public int compare(Locale o1, Locale o2) { 343 return o1.toString().compareTo(o2.toString()); 344 } 345 }); 346 return l; 347 } 348 349 public static boolean hasCode(String code) 350 { 351 return languages.containsKey(code); 352 } 353 354 public static void init() 355 { 356 //languages.put("ar", PluralMode.MODE_AR); 357 languages.put("bg", PluralMode.MODE_NOTONE); 358 languages.put("ca", PluralMode.MODE_NOTONE); 359 languages.put("cs", PluralMode.MODE_CS); 360 languages.put("da", PluralMode.MODE_NOTONE); 361 languages.put("de", PluralMode.MODE_NOTONE); 362 languages.put("el", PluralMode.MODE_NOTONE); 363 languages.put("en_AU", PluralMode.MODE_NOTONE); 364 languages.put("en_GB", PluralMode.MODE_NOTONE); 365 languages.put("es", PluralMode.MODE_NOTONE); 366 languages.put("et", PluralMode.MODE_NOTONE); 367 //languages.put("eu", PluralMode.MODE_NOTONE); 368 languages.put("fi", PluralMode.MODE_NOTONE); 369 languages.put("fr", PluralMode.MODE_GREATERONE); 370 languages.put("gl", PluralMode.MODE_NOTONE); 371 //languages.put("he", PluralMode.MODE_NOTONE); 372 languages.put("hu", PluralMode.MODE_NOTONE); 373 languages.put("id", PluralMode.MODE_NONE); 374 //languages.put("is", PluralMode.MODE_NOTONE); 375 languages.put("it", PluralMode.MODE_NOTONE); 376 languages.put("ja", PluralMode.MODE_NONE); 377 //languages.put("nb", PluralMode.MODE_NOTONE); 378 languages.put("nl", PluralMode.MODE_NOTONE); 379 languages.put("pl", PluralMode.MODE_PL); 380 languages.put("pt", PluralMode.MODE_NOTONE); 381 languages.put("pt_BR", PluralMode.MODE_GREATERONE); 382 //languages.put("ro", PluralMode.MODE_RO); 383 languages.put("ru", PluralMode.MODE_RU); 384 languages.put("sk", PluralMode.MODE_SK); 385 //languages.put("sl", PluralMode.MODE_SL); 386 languages.put("sv", PluralMode.MODE_NOTONE); 387 //languages.put("tr", PluralMode.MODE_NONE); 388 languages.put("uk", PluralMode.MODE_RU); 389 languages.put("zh_CN", PluralMode.MODE_NONE); 390 languages.put("zh_TW", PluralMode.MODE_NONE); 391 392 /* try initial language settings, may be changed later again */ 393 if(!load(Locale.getDefault().toString())) { 394 Locale.setDefault(Locale.ENGLISH); 395 } 396 } 397 398 public static void addTexts(File source) { 399 if ("en".equals(loadedCode)) 400 return; 401 String enfile = "data/en.lang"; 402 String langfile = "data/"+loadedCode+".lang"; 403 try ( 404 FileInputStream fis = new FileInputStream(source); 405 JarInputStream jar = new JarInputStream(fis) 406 ) { 407 ZipEntry e; 408 boolean found = false; 409 while (!found && (e = jar.getNextEntry()) != null) { 410 String name = e.getName(); 411 if(name.equals(enfile)) 412 found = true; 413 } 414 if (found) { 415 try ( 416 FileInputStream fisTrans = new FileInputStream(source); 417 JarInputStream jarTrans = new JarInputStream(fisTrans) 418 ) { 419 found = false; 420 while(!found && (e = jarTrans.getNextEntry()) != null) { 421 String name = e.getName(); 422 if (name.equals(langfile)) 423 found = true; 424 } 425 if (found) 426 load(jar, jarTrans, true); 427 } 428 } 429 } catch (IOException e) { 430 // Ignore 431 } 432 } 433 434 private static boolean load(String l) { 435 if ("en".equals(l) || "en_US".equals(l)) { 436 strings = null; 437 pstrings = null; 438 loadedCode = "en"; 439 pluralMode = PluralMode.MODE_NOTONE; 440 return true; 441 } 442 URL en = getTranslationFile("en"); 443 if (en == null) 444 return false; 445 URL tr = getTranslationFile(l); 446 if (tr == null || !languages.containsKey(l)) { 447 int i = l.indexOf('_'); 448 if (i > 0) { 449 l = l.substring(0, i); 450 } 451 tr = getTranslationFile(l); 452 if (tr == null || !languages.containsKey(l)) 453 return false; 454 } 455 try ( 456 InputStream enStream = en.openStream(); 457 InputStream trStream = tr.openStream() 458 ) { 459 if (load(enStream, trStream, false)) { 460 pluralMode = languages.get(l); 461 loadedCode = l; 462 return true; 463 } 464 } catch (IOException e) { 465 // Ignore exception 466 } 467 return false; 468 } 469 470 private static boolean load(InputStream en, InputStream tr, boolean add) { 471 Map<String, String> s; 472 Map<String, String[]> p; 473 if (add) { 474 s = strings; 475 p = pstrings; 476 } else { 477 s = new HashMap<>(); 478 p = new HashMap<>(); 479 } 480 /* file format: 481 Files are always a group. English file and translated file must provide identical datasets. 482 483 for all single strings: 484 { 485 unsigned short (2 byte) stringlength 486 - length 0 indicates missing translation 487 - length 0xFFFE indicates translation equal to original, but otherwise is equal to length 0 488 string 489 } 490 unsigned short (2 byte) 0xFFFF (marks end of single strings) 491 for all multi strings: 492 { 493 unsigned char (1 byte) stringcount 494 - count 0 indicates missing translations 495 - count 0xFE indicates translations equal to original, but otherwise is equal to length 0 496 for stringcount 497 unsigned short (2 byte) stringlength 498 string 499 } 500 */ 501 try 502 { 503 InputStream ens = new BufferedInputStream(en); 504 InputStream trs = new BufferedInputStream(tr); 505 byte[] enlen = new byte[2]; 506 byte[] trlen = new byte[2]; 507 boolean multimode = false; 508 byte[] str = new byte[4096]; 509 for(;;) 510 { 511 if(multimode) 512 { 513 int ennum = ens.read(); 514 int trnum = trs.read(); 515 if(trnum == 0xFE) /* marks identical string, handle equally to non-translated */ 516 trnum = 0; 517 if((ennum == -1 && trnum != -1) || (ennum != -1 && trnum == -1)) /* files do not match */ 518 return false; 519 if(ennum == -1) { 520 break; 521 } 522 String[] enstrings = new String[ennum]; 523 String[] trstrings = new String[trnum]; 524 for(int i = 0; i < ennum; ++i) 525 { 526 int val = ens.read(enlen); 527 if(val != 2) /* file corrupt */ 528 return false; 529 val = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]); 530 if(val > str.length) { 531 str = new byte[val]; 532 } 533 int rval = ens.read(str, 0, val); 534 if(rval != val) /* file corrupt */ 535 return false; 536 enstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8); 537 } 538 for(int i = 0; i < trnum; ++i) 539 { 540 int val = trs.read(trlen); 541 if(val != 2) /* file corrupt */ 542 return false; 543 val = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]); 544 if(val > str.length) { 545 str = new byte[val]; 546 } 547 int rval = trs.read(str, 0, val); 548 if(rval != val) /* file corrupt */ 549 return false; 550 trstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8); 551 } 552 if(trnum > 0 && !p.containsKey(enstrings[0])) { 553 p.put(enstrings[0], trstrings); 554 } 555 } 556 else 557 { 558 int enval = ens.read(enlen); 559 int trval = trs.read(trlen); 560 if(enval != trval) /* files do not match */ 561 return false; 562 if(enval == -1) { 563 break; 564 } 565 if(enval != 2) /* files corrupt */ 566 return false; 567 enval = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]); 568 trval = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]); 569 if(trval == 0xFFFE) /* marks identical string, handle equally to non-translated */ 570 trval = 0; 571 if(enval == 0xFFFF) 572 { 573 multimode = true; 574 if(trval != 0xFFFF) /* files do not match */ 575 return false; 576 } else { 577 if (enval > str.length) { 578 str = new byte[enval]; 579 } 580 if (trval > str.length) { 581 str = new byte[trval]; 582 } 583 int val = ens.read(str, 0, enval); 584 if(val != enval) /* file corrupt */ 585 return false; 586 String enstr = new String(str, 0, enval, StandardCharsets.UTF_8); 587 if (trval != 0) { 588 val = trs.read(str, 0, trval); 589 if(val != trval) /* file corrupt */ 590 return false; 591 String trstr = new String(str, 0, trval, StandardCharsets.UTF_8); 592 if(!s.containsKey(enstr)) 593 s.put(enstr, trstr); 594 } 595 } 596 } 597 } 598 } 599 catch (IOException e) { 600 return false; 601 } 602 if (!s.isEmpty()) { 603 strings = s; 604 pstrings = p; 605 return true; 606 } 607 return false; 608 } 609 610 /** 611 * Sets the default locale (see {@link Locale#setDefault(Locale)} to the local 612 * given by <code>localName</code>. 613 * 614 * Ignored if localeName is null. If the locale with name <code>localName</code> 615 * isn't found the default local is set to <tt>en</tt> (english). 616 * 617 * @param localeName the locale name. Ignored if null. 618 */ 619 public static void set(String localeName){ 620 if (localeName != null) { 621 Locale l = LanguageInfo.getLocale(localeName); 622 if (load(LanguageInfo.getJOSMLocaleCode(l))) { 623 Locale.setDefault(l); 624 } else { 625 if (!"en".equals(l.getLanguage())) { 626 Main.info(tr("Unable to find translation for the locale {0}. Reverting to {1}.", 627 l.getDisplayName(), Locale.getDefault().getDisplayName())); 628 } else { 629 strings = null; 630 pstrings = null; 631 } 632 } 633 } 634 } 635 636 /** 637 * Localizations for file chooser dialog. 638 * For some locales (e.g. de, fr) translations are provided 639 * by Java, but not for others (e.g. ru, uk). 640 */ 641 public static void translateJavaInternalMessages() { 642 Locale l = Locale.getDefault(); 643 644 AbstractFileChooser.setDefaultLocale(l); 645 JFileChooser.setDefaultLocale(l); 646 JColorChooser.setDefaultLocale(l); 647 for (String key : javaInternalMessageKeys) { 648 String us = UIManager.getString(key, Locale.US); 649 String loc = UIManager.getString(key, l); 650 // only provide custom translation if it is not already localized by Java 651 if (us != null && us.equals(loc)) { 652 UIManager.put(key, tr(us)); 653 } 654 } 655 } 656 657 private static int pluralEval(long n) 658 { 659 switch(pluralMode) 660 { 661 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, eu, fi, gl, is, it, iw_IL, nb, nl, sv */ 662 return ((n != 1) ? 1 : 0); 663 case MODE_NONE: /* ja, tr, zh_CN, zh_TW */ 664 return 0; 665 case MODE_GREATERONE: /* fr, pt_BR */ 666 return ((n > 1) ? 1 : 0); 667 case MODE_CS: 668 return ((n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2)); 669 //case MODE_AR: 670 // return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3) 671 // && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5))))); 672 case MODE_PL: 673 return ((n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4)) 674 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2)); 675 //case MODE_RO: 676 // return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1)); 677 case MODE_RU: 678 return ((((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2) 679 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2)); 680 case MODE_SK: 681 return ((n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0)); 682 //case MODE_SL: 683 // return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3) 684 // || ((n % 100) == 4)) ? 3 : 0))); 685 } 686 return 0; 687 } 688 689 public static TranslationAdapter getTranslationAdapter() { 690 return new TranslationAdapter() { 691 @Override 692 public String tr(String text, Object... objects) { 693 return I18n.tr(text, objects); 694 } 695 }; 696 } 697}