package com.limegroup.gnutella.i18n; import java.io.PrintStream; import java.text.DateFormat; import java.text.NumberFormat; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.TreeMap; /** * Writes language info out in HTML format. */ class HTMLOutput { /** @see LanguageInfo#getLink() */ static final String PRE_LINK = "http://www.limewire.org/fisheye/viewrep/~raw,r=MAIN/limecvs/lib/messagebundles/"; private static final String DEFAULT_LINK = PRE_LINK + LanguageLoader.BUNDLE_NAME + LanguageLoader.PROPS_EXT; /** constant link to the translate mailing list. */ private static final String HTML_TRANSLATE_EMAIL_ADDRESS = "<script type=\"text/javascript\" language=\"JavaScript\"><!--\n" + "// Protected email script by Joe Maller JavaScripts available at http://www.joemaller.com\n" + "// This script is free to use and distribute but please credit me and/or link to my site.\n" + "e_mA_iL_E = ('limewire' + \".\" + 'org'); e_mA_iL_E = ('translate' + \"@\" + e_mA_iL_E);\n" + "document.write('<a href=\"mai' + \"lto:\" + e_mA_iL_E + '\">' + e_mA_iL_E + '</a>');\n" + "//--></script><noscript><a href=\"#\">[Email address protected by JavaScript:\n" + "please enable JavaScript to contact me]</a></noscript>"; /** minimum completion levels for the status HTML page */ private static final double MIN_PERCENTAGE_COMPLETED = 0.75; private static final double MIN_PERCENTAGE_NEED_REVISION = 0.65; private static final double MIN_PERCENTAGE_MIDWAY = 0.45; private static final int MIN_COUNT_STARTED = 20; private final StringBuffer page; private final DateFormat df; private final NumberFormat pc; private final Map/* <String code, LanguageInfo li> */langs; private final int basicTotal; /** * Constructs a new HTML output. * * @param df * @param pc * @param langs * @param basicTotal */ HTMLOutput(DateFormat df, NumberFormat pc, Map langs, int basicTotal) { this.df = df; this.pc = pc; this.langs = langs; this.basicTotal = basicTotal; this.page = buildHTML(); } /** * Creates the HTML. * * @return the HTML page in a StringBuffer. */ StringBuffer buildHTML() { List langsCompleted = new LinkedList(); List langsNeedRevision = new LinkedList(); List langsMidway = new LinkedList(); List langsStarted = new LinkedList(); List langsEmbryonic = new LinkedList(); Map charsets = new TreeMap(new CharsetNameComparator()); for (Iterator i = langs.entrySet().iterator(); i.hasNext();) { final Map.Entry entry = (Map.Entry) i.next(); // final String code = (String)entry.getKey(); final LanguageInfo li = (LanguageInfo) entry.getValue(); final Properties props = li.getProperties(); final int count = props.size(); final double percentage = (double) count / (double) basicTotal; li.setPercentage(percentage); if (percentage >= MIN_PERCENTAGE_COMPLETED) langsCompleted.add(li); else if (percentage >= MIN_PERCENTAGE_NEED_REVISION) langsNeedRevision.add(li); else if (percentage >= MIN_PERCENTAGE_MIDWAY) langsMidway.add(li); else if (count >= MIN_COUNT_STARTED) langsStarted.add(li); else langsEmbryonic.add(li); String script = li.getScript(); List inScript = (List) charsets.get(script); if (inScript == null) { inScript = new LinkedList(); charsets.put(script, inScript); } inScript.add(li); } StringBuffer newpage = new StringBuffer(); buildStartOfPage(newpage); buildStatus(newpage, langsCompleted, langsNeedRevision, langsMidway, langsStarted, langsEmbryonic); buildAfterStatus(newpage); buildProgress(newpage, charsets); buildEndOfPage(newpage); return newpage; } /** * Prints the HTML to 'out'. * * @param out */ void printHTML(PrintStream out) { /* * Make sure printed page contains only ASCII, converting all other code * points to decimal NCRs. This will work whatever charset will be * selected by the user's browser. */ int pageLength = page.length(); for (int index = 0; index < pageLength;) { int c = page.charAt(index++); // char's are always positive if (c < 160) { /* C0 or Basic Latin or C1 */ if (c >= 32 && c < 127 || c == '\t') /* Basic Latin or TAB */ out.print((char) c); else if (c == '\n') /* LF */ out.println(); /* platform's newline sequence */ /* ignore all other C0 and C1 controls */ } else { /* Use NCRs */ /* special check for surrogate pairs */ if (c >= 0xD800 && c <= 0xDBFF && index < pageLength) { char c2 = page.charAt(index); if (c2 >= 0xDC00 && c2 <= 0xDFFF) { c = 0x10000 + ((c - 0xD800) << 10) + (c2 - 0xDC00); index++; } } out.print("&#"); out.print(c);// decimal NCR notation out.print(';'); } } } /** * Builds the start of the page. */ private void buildStartOfPage(StringBuffer newpage) { newpage.append( " <div id=\"bod1\">\n" + " <h1>Help Internationalize LimeWire!</h1>\n" + " <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n" + " <tr>\n" + /* Three columns */ " <td valign=\"top\" style=\"line-height: 16px;\">\n" + /* Start column 1 (main content) */ " The LimeWire Open Source Project has embarked on an effort to\n" + " internationalize LimeWire.  If you are an avid English-speaking user\n" + " fluent in another language, we need your help!  Helping requires no\n" + " programming knowledge and little computer savviness beyond using a word\n" + " processor.<br />\n" + " <br />\n" + " <b>HOW LIMEWIRE SUPPORTS MULTIPLE LANGUAGES</b><br />\n" + " <br />\n" + " First, view this <a\n" + " href=\"http://www.limewire.com/img/screenshots/search.jpg\"\n" + " target=\"_blank\">LimeWire screen-shot</a>.  Notice how the tabs\n" + " (<b>Search</b>, <b>Monitor</b>, <b>Library</b>, etc.) and the buttons\n" + " (<b>Download</b>, <b>Cancel</b>, etc.) have text on them.  All\n" + " elements of the LimeWire interface can be translated to any language very\n" + " easily.<br />\n" + " <br />\n" + " This translation is accomplished by packaging all the words of the\n" + " program into a "message bundle".  A message bundle is more\n" + " or less a list, with phrases corresponding to certain parts of the\n" + " software.  There are message bundles for different languages, so\n" + " there is an English message bundle, a French message bundle, a Japanese\n" + " message bundle, etc.  In English, the text for the download button\n" + " is "Download", whereas in French the text is "Charger"\n" + " (which is French for "download").<br />\n" + " <br />\n" + " When you start LimeWire, the program loads the appropriate message bundle\n" + " and uses its contents for any interface element that has text on it. \n" + " For instance, this is the <a\n" + "href=\"" + PRE_LINK + "MessagesBundle.properties\">English\n" + " message bundle</a>.  Note the line:<br />\n" + " <blockquote>\n" + " <table border=\"0\" cellspacing=\"1\" cellpadding=\"4\"" + " bgcolor=\"#b1b1b1\">\n" + " <tr bgcolor=\"#EFEFEF\">\n" + " <td><code>\n" + "SEARCH_DOWNLOAD_BUTTON_LABEL=Download</code></td>\n" + " </tr>\n" + " </table>\n" + " </blockquote>\n" + " This line indicates that the label used on the download button on\n" + " the search tab should read "Download".  Contrast this\n" + " with the same line in the <a\n" + "href=\"" + PRE_LINK + "MessagesBundle_fr.properties\">French\n" + " message bundle</a>:<br />\n" + " <blockquote>\n" + " <table border=\"0\" cellspacing=\"1\" cellpadding=\"4\"\n" + " bgcolor=\"#b1b1b1\">\n" + " <tr bgcolor=\"#EFEFEF\">\n" + " <td><code>\n" + "#### SEARCH_DOWNLOAD_BUTTON_LABEL=Download<br />\n" + "SEARCH_DOWNLOAD_BUTTON_LABEL=Charger</code></td>\n" + " </tr>\n" + " </table>\n" + " </blockquote>\n" + " Note that the line starting with a "#" is a comment line,\n" + " meaning it is not used by LimeWire.  The English translation will\n" + " always be present as a reference.  A label that is not yet\n" + " translated in a bundle will look like the following:<br />\n" + " <blockquote>\n" + " <table border=\"0\" cellspacing=\"1\" cellpadding=\"4\"\n" + " bgcolor=\"#b1b1b1\">\n" + " <tr bgcolor=\"#EFEFEF\">\n" + " <td><code>\n" + "#### SOME_NEW_LABEL=New!<br />\n" + "#? SOME_NEW_LABEL=</code></td>\n" + " </tr>\n" + " </table>\n" + " </blockquote>\n" + " To provide a translation, one just needs to append the translated text\n" + " after the equal sign, and remove the leading comment mark and\n" + " space.  The French translation would look like the following:<br />\n" + " <blockquote>\n" + " <table border=\"0\" cellspacing=\"1\" cellpadding=\"4\"\n" + " bgcolor=\"#b1b1b1\">\n" + " <tr bgcolor=\"#EFEFEF\">\n" + " <td><code>\n" + "#### SOME_NEW_LABEL=New!<br />\n" + "SOME_NEW_LABEL=Nouveau!</code></td>\n" + " </tr>\n" + " </table>\n" + " </blockquote>\n" + " <br />\n"); } /** * Builds the status portion of the page. */ private void buildStatus(StringBuffer newpage, List langsCompleted, List langsNeedRevision, List langsMidway, List langsStarted, List langsEmbryonic) { newpage.append( " <b>TRANSLATION STATUS</b>\n" + " <br />\n" + " <ol>\n"); // ### Need subject-verb agreement here; need to check if // ### size of list == 1 or singular versus plural buildStatus(newpage, langsCompleted, " are complete and will require only small revisions during the project\n" + " evolution."); buildStatus(newpage, langsNeedRevision, " are mostly complete and can still be used reliably, but may need some\n" + " revisions and a few missing translations to work best with newest\n" + " versions."); buildStatus(newpage, langsMidway, " have been completed for an old version, but may now require some work,\n" + " tests and revisions plus additional missing translations to reach a\n" + " reliable status."); buildStatus(newpage, langsStarted, " are partly translated but still unfinished, and their use in LimeWire\n" + " may be difficult for native language users.  Providing a more\n" + " complete translation would be very much appreciated."); buildStatus(newpage, langsEmbryonic, " are only embryonic and need a complete translation. \n" + " The current files are largely untranslated."); newpage.append( " </ol><br />\n"); } /** * Builds an individual bullet point in the status portion of the page. */ private void buildStatus(StringBuffer newpage, List langsList, String status) { boolean first = true; for (Iterator i = langsList.iterator(); i.hasNext();) { LanguageInfo l = (LanguageInfo) i.next(); if (first) newpage.append( " <li>\n"); else if (!i.hasNext()) newpage.append(" and\n"); else newpage.append(",\n"); newpage.append( " " + l.getLink()); first = false; } if (!first) newpage.append("\n" + status + "</li>\n"); } /** * Builds the info after the status portion. */ private void buildAfterStatus(StringBuffer newpage) { newpage.append( " <b>GENERAL CONSIDERATIONS FOR TRANSLATORS</b><br />\n" + " <br />\n" + " Do not start with the existing message bundle installed with your current\n" + " version of LimeWire.  Make sure you <b>work on the latest version of\n" + " a message bundle</b>.  You can get the latest bundle by clicking on\n" + " a language in the list on the right side of this page.<br />\n" + " <br />\n" + " When translating, adopt the common terminology used in your localized\n" + " operating system.  In some cases, some terms were imported from\n" + " English, despite other terms already existing in your own language. \n" + " If a local term can be used unambiguously, please use it in preference to\n" + " the English term, even if you have seen many uses of this English term on\n" + " web sites.  A good translation must be understood by people who are\n" + " not entirely savvy with Internet and computer jargon.  Pay\n" + " particularly attention to the non-technical translation of common terms:\n" + " download, upload, host, byte, firewall, address, file, directory, #\n" + " (number of), leaf (terminal node), etc.<br />\n" + " <br />\n" + " Avoid translating word for word and do not use blindly automatic\n" + " translators.  Be imaginative but make a clear and concise\n" + " translation.  For button labels and column names, do not translate\n" + " them with long sentences, as they need to be short.  Suppress some\n" + " articles, or use abbreviations if necessary.<br />\n" + " <br />\n" + " During the translation process, you may <a\n" + " href=\"http://www.limewire.org/mailinglist.shtml\">subscribe to the\n" + " translate mailing list</a> where you may benefit from other translators'\n" + " questions and knowledge as well as receive assistance in English or\n" + " French.<br />\n" + " <br />\n" + " <b>HOW TO SUBMIT CORRECTIONS OR ENHANCEMENTS FOR YOUR LANGUAGE</b><br />\n" + " <br />\n" + " If your corrections are significant, you may send your complete message\n" + " bundle to\n" + HTML_TRANSLATE_EMAIL_ADDRESS + ". \n" + " Please be sure to include all resource strings defined in the latest\n" + " version of the existing message bundle before sending us your\n" + " revision.<br />\n" + " <br />\n" + " For simple few corrections or additions, just send the corrected lines in\n" + " the content body of an email (making sure to select the correct character\n" + " encoding in your email tool before sending it so that non-ASCII\n" + " characters are not lost or replaced), with your comments.<br />\n" + " <br />\n" + " <i>We will review submitted translations and integrate all valuable\n" + " contributions as quickly as possible.</i><br />\n" + " <br />\n" + " <b>WHICH TOOL OR EDITOR TO USE FOR TRANSLATIONS</b><br />\n" + " <br />\n" + " For <b>Basic Latin or Western European Latin-based languages</b>, which\n" + " can use the US-ASCII or ISO-8859-1 character set, any text editor (such\n" + " as Notepad on Windows) can be used on Windows and Linux.  Once a\n" + " file is completed, it can be sent as a simple text file to\n" + HTML_TRANSLATE_EMAIL_ADDRESS + ".<br />\n" + " <br />\n" + " For <b>Central European languages</b>, the preferred format is a simple\n" + " text file encoded with the ISO-8859-2 character set, or a UTF-8 encoded\n" + " simple text file (which can be edited with Notepad on Windows 2000/XP),\n" + " or a correctly marked-up HTML document such as HTML email, or a Word\n" + " document.<br />\n" + " <br />\n" + " For <b>other European languages</b>, the preferred format is a plain-text\n" + " file, encoded preferably with UTF-8 or a ISO-8859-* character set that\n" + " you should explicitly specify, or a correctly marked-up HTML document, or\n" + " a Word document.  Please specify your working operating system and\n" + " editor you used to create plain-text files (we may support incoming\n" + " Windows codepages or Mac charsets, but we will convert them to Unicode\n" + " UTF-8 in our repository).<br />\n" + " <br />\n" + " For <b>Semitic languages</b> (Arabic, Hebrew...), the preferred format is\n" + " a plain-text file edited with a editor that supports the right-to-left\n" + " layout, encoded preferably with UTF-8 or a ISO-8859-* character set, in\n" + " logical order.  Be careful with the relative order of keys and\n" + " values, and with the appearance of ASCII punctuations around\n" + " right-to-left words: make sure that your editor uses the RTL layout with\n" + " the edited text aligned on the right; please do not insert BiDi control\n" + " overrides; but you may need to place LRE/PDF marks (U+202B/U+202C)\n" + " locally around non-Semitic words inserted within Semitic sentences. \n" + " Also the "<code>\\n</code>" sequences that encode a newline\n" + " will be displayed within semitic text as "<code>n\\</code>": do\n" + " not use BiDi override controls for such special sequence whose appearance\n" + " in your editor is not important, but that MUST be entered with a leading\n" + " backslash before the "n" character.<br />\n" + " <br />\n" + " For <b>Asian Languages</b>, the preferred submission format is a Unicode\n" + " text file encoded with UTF-8.  Users of Windows 2000/XP can use\n" + " Notepad but you must explicitly select the UTF-8 encoding when saving\n" + " your file.  Users of localized versions of Windows 95/98/ME can only\n" + " save their file in the native local "ANSI" encoding, and should\n" + " then send us their translation by copying and pasting it in the content\n" + " body of the email.<br />\n" + " <br />\n" + " <b>Mac users</b> should use a word processor and send us their\n" + " translations in an unambiguous format.  On Mac OSX, the best tool is\n" + " "TextEdit", from the Jaguar accessories, with which you can\n" + " directly edit and save plain-text files encoded with UTF-8.<br />\n" + " <br />\n" + " <b>Linux users</b> can also participate if they have a correct\n" + " environment for their locale.  Files can be edited with\n" + " "vi", "emacs", or other editors.<br />\n" + " <br />\n" + " For further information about internationalization standards, language\n" + " and country codes, character sets and encodings, the following web pages\n" + " may be helpful:<br />\n" + " <ul>\n" + " <li>Language codes: <a\n" + " href=\"http://www.loc.gov/standards/iso639-2/englangn.html\"\n" + "target=\"_blank\">http://www.loc.gov/standards/iso639-2/englangn.html</a></li>\n" + " <li>Country codes: <a\n" + " href=\"http://www.iso.org/iso/en/prods-services/iso3166ma/index.html\"\n" + "target=\"_blank\">http://www.iso.org/iso/en/prods-services/iso3166ma/index.html</a></li>\n" + " <li>Character sets: <a\n" + " href=\"http://www.w3.org/International/O-charset.html\"\n" + "target=\"_blank\">http://www.w3.org/International/O-charset.html</a></li>\n" + " <li>Letter database (languages and character sets): <a\n" + " href=\"http://www.eki.ee/letter/\"\n" + " target=\"_blank\">http://www.eki.ee/letter/</a></li>\n" + " <li>Other internationalization data: <a\n" + " href=\"http://www.unicode.org/unicode/onlinedat/resources.html\"\n" + "target=\"_blank\">http://www.unicode.org/unicode/onlinedat/resources.html</a></li>\n" + " </ul>\n" + " An excellent tutorial on various character sets, including the ASCII\n" + " variants, the ISO-8859 family, the Windows "ANSI code pages",\n" + " Macintosh character codes, and Unicode (or its ISO/IEC 10646 repertoire)\n" + " can be found at <a href=\"http://www.cs.tut.fi/~jkorpela/chars.html\"\n" + " target=\"_blank\">http://www.cs.tut.fi/~jkorpela/chars.html</a>.<br />\n" + " <br />\n" + " Users that do not have the correct tools to edit a message bundle can\n" + " send us an email in English or in French that explains their needs.<br />\n" + " <br />\n" + " <b>HOW TO TEST A NEW TRANSLATION</b><br />\n" + " <br />\n" + " Only Windows and Unix simple text editors can create a plain-text file\n" + " which will work in LimeWire, and only for languages using the Western\n" + " European Latin character set.  Do not use "SimpleText" on\n" + " Mac OS to edit properties files as SimpleText does not create plain-text\n" + " files.  Other translations need to be converted into regular\n" + " properties files, encoded using the ISO-8859-1 Latin character set and\n" + " Unicode escape sequences, with a tool "native2ascii" found in\n" + " the Java Development Kit.<br />\n" + " <br />\n" + " You do not need to rename your translated and converted bundle, which can\n" + " co-exist with the English version.  LimeWire will load the\n" + " appropriate resource file according to the\n" + " "<code>LANGUAGE=</code>", and "<code>COUNTRY=</code>"\n" + " settings stored in your "<code>limewire.props</code>"\n" + " preferences file.  The list on the right can help you to find the\n" + " correct language code to use.<br />\n" + " <br />\n" + " Bundles are stored in a single compressed archive named\n" + " "<code>MessagesBundles.jar</code>" installed with\n" + " LimeWire.  All bundles are named\n" + " "MessagesBundle_xx.properties", where "xx" is\n" + " replaced by the language code.  Note that bundles for languages\n" + " using non-Western European Latin characters will be converted from UTF-8\n" + " to ASCII using a special format with hexadecimal Unicode escape\n" + " sequences, prior to their inclusion in this archive.  This can be\n" + " performed using the <code>native2ascii</code> tool from the Java\n" + " Development Kit.  If you do not know how to proceed to test the\n" + " translation yourself, ask us for assistance at\n" + HTML_TRANSLATE_EMAIL_ADDRESS + ".<br />\n" + " <br />\n" + " <b>HOW TO CREATE A NEW TRANSLATION</b><br />\n" + " <br />\n" + " Users that wish to contribute with a new translation must be fluent in\n" + " the target language, preferably native of a country where this language\n" + " is official.  Before starting your work, please contact us at\n" + HTML_TRANSLATE_EMAIL_ADDRESS + ".<br />\n" + " <br />\n" + " </td>\n" + /* End of column 1 (spacing) */ " <td>   </td>\n" + /* Column 2 (spacing) */ " <td valign=\"top\">\n" + /* Start of column 3 (status) */ /* Start shaded right rectangle */ " <table border=\"0\" cellspacing=\"1\" cellpadding=\"4\"\n" + " bgcolor=\"#b1b1b1\" width=\"270\">\n" + " <tr bgcolor=\"#EFEFEF\">\n" + " <td valign=\"top\"><br />\n" + " <b>LAST UPDATED: <font color=\"#FF0000\">" + df.format(new Date()) + "</font><br />\n" + " <br />\n" + " To get the most recent version of a message bundle, <b>click on the\n" + " corresponding language</b> in the following list.<br />\n" + " <br />\n" + " LATEST TRANSLATIONS STATUS:</b><br />\n"); } /** * Builds the progress table. */ private void buildProgress(StringBuffer newpage, Map charsets) { newpage.append( " <table width=\"250\" border=\"0\" cellpadding=\"0\" cellspacing=\"4\">"); List latin = (List) charsets.remove("Latin"); newpage.append( " <tr>\n" + " <td colspan=\"3\" valign=\"top\">" + " <hr noshade size=\"1\">\n" + " Languages written with Latin (Basic) characters:</td>\n" + " </tr>\n" + " <tr>\n" + " <td valign=\"top\"><a\n" + " href=\"" + DEFAULT_LINK + "\"\n" + " target=\"_blank\"><b>English</b> (US)</a></td>\n" + " <td align=\"right\">(default)</td>\n" + " <td>en</td>\n" + " </tr>\n"); for (Iterator i = latin.iterator(); i.hasNext();) { LanguageInfo l = (LanguageInfo) i.next(); newpage.append( " <tr>\n" + " <td><b>" + l.getLink() + "</b></td>\n" + " <td align=\"right\">(" + pc.format(l.getPercentage()) + ")</td>\n" + " <td>" + l.getCode() + "</td>\n" + " </tr>\n"); } for (Iterator i = charsets.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String charset = (String) entry.getKey(); List l = (List) entry.getValue(); newpage.append( " <tr>\n" + " <td colspan=\"3\" valign=\"top\">\n" + " <hr noshade size=\"1\">\n" + " Languages written with " + charset + " characters:</td>\n" + " </tr>\n"); for (Iterator j = l.iterator(); j.hasNext();) { LanguageInfo li = (LanguageInfo) j.next(); newpage.append( " <tr>\n" + " <td><b>" + li.getLink() + "</b></td>\n" + " <td align=\"right\">(" + pc.format(li.getPercentage()) + ")</td>\n" + " <td>" + li.getCode() + "</td>\n" + " </tr>\n"); } } newpage.append( " </table>\n"); } /** * Builds the closing footers of the page. */ private void buildEndOfPage(StringBuffer newpage) { newpage.append( " </td>\n" + " </tr>\n" + " </table>\n" + /* End of shaded right rectangle */ " </td>\n" + /* End of column 3 (status) */ " </tr>\n" + " </table>\n" + /* End of the 3 columns table below the title */ " </div>\n"); /* (div id="bod1") */ } /** * This Comparator sorts charset names in a prefered order. It is consistent * with equals, so can be used to sort keys in a Map. */ private static class CharsetNameComparator implements Comparator { /** * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(Object arg0, Object arg1) { if (arg0 == arg1 || arg0.equals(arg1)) return 0; final String charsetName0 = (String) arg0; final String charsetName1 = (String) arg1; // Make Latin charsets sorted before others. boolean has0 = charsetName0.contains("Latin"); boolean has1 = charsetName1.contains("Latin"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; if (has0 && has1) { has0 = charsetName0.contains("Basic"); has1 = charsetName1.contains("Basic"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; has0 = charsetName0.contains("Western"); has1 = charsetName1.contains("Western"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; } // Then sort alphabetic charsets. has0 = charsetName0.contains("European"); has1 = charsetName1.contains("European"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; if (has0 & has1) return charsetName0.compareTo(charsetName1); has0 = charsetName0.contains("Cyrillic"); has1 = charsetName1.contains("Cyrillic"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; if (has0 & has1) return charsetName0.compareTo(charsetName1); // Then sort abjads charsets. has0 = charsetName0.contains("Semitic"); has1 = charsetName1.contains("Semitic"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; if (has0 & has1) return charsetName0.compareTo(charsetName1); has0 = charsetName0.contains("Brahmic"); has1 = charsetName1.contains("Brahmic"); if (has0 && !has1) return -1; if (!has0 && has1) return 1; if (has0 & has1) return charsetName0.compareTo(charsetName1); // Sort the remaining complex charsets. return charsetName0.compareTo(charsetName1); } } }