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