All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.i18n.server.GwtLocaleImpl Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2009 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.i18n.server;

import com.google.gwt.i18n.shared.GwtLocale;
import com.google.gwt.i18n.shared.GwtLocaleFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Class representing GWT locales and conversion to/from other formats.
 * 
 * These locales correspond to BCP47.
 */
public class GwtLocaleImpl implements GwtLocale {
  // TODO(jat): implement a version of this suitable for client-side use,
  // probably using a generator to include only the information relevant to
  // the set of locales supported, and then figure out a way to get that into
  // the property provider to handle inheritance there.

  /**
   * Maps deprecated language codes to the canonical code.  Strings are always
   * in pairs, with the first being the canonical code and the second being
   * a deprecated code which maps to it.
   * 

* Source: http://www.loc.gov/standards/iso639-2/php/code_changes.php *

* TODO: consider building maps if this list grows much. */ private static final String[] deprecatedLanguages = new String[] { "he", "iw", // Hebrew "id", "in", // Indonesian "jv", "jw", // Javanese, typo in original publication "ro", "mo", // Moldovian "yi", "ji", // Yiddish }; /** * Maps deprecated region codes to the canonical code. Strings are always * in pairs, with the first being the canonical code and the second being * a deprecated code which maps to it. * * Note that any mappings which split an old code into multiple new codes * cannot be done automatically (such as cs -> rs/me) -- perhaps we could * have a way of flagging region codes which are no longer valid and allow * an appropriate warning message. *

* Source: http://en.wikipedia.org/wiki/ISO_3166-1 *

* TODO: consider building maps if this list grows much. */ private static final String[] deprecatedRegions = new String[] { // East Timor - http://www.iso.org/iso/newsletter_v-5_east_timor.pdf "TL", "TP", }; /** * Add in the missing locale of a deprecated pair. * * @param factory GwtLocaleFactory to create new instances with * @param locale locale to add deprecated pair for * @param aliases where to store the alias if present */ private static void addDeprecatedPairs(GwtLocaleFactory factory, GwtLocale locale, List aliases) { int n = deprecatedLanguages.length; for (int i = 0; i < n; i += 2) { if (deprecatedLanguages[i].equals(locale.getLanguage())) { aliases.add(factory.fromComponents(deprecatedLanguages[i + 1], locale.getScript(), locale.getRegion(), locale.getVariant())); break; } if (deprecatedLanguages[i + 1].equals(locale.getLanguage())) { aliases.add(factory.fromComponents(deprecatedLanguages[i], locale.getScript(), locale.getRegion(), locale.getVariant())); break; } } } private static void addImmediateParentRegions(GwtLocaleFactory factory, GwtLocale locale, Collection work) { String language = locale.getLanguage(); String script = locale.getScript(); String region = locale.getRegion(); String variant = locale.getVariant(); if (variant != null) { work.add(factory.fromComponents(language, script, region, null)); } Set immediateParents = RegionInheritance.getImmediateParents(region); for (String parent : immediateParents) { work.add(factory.fromComponents(language, script, parent, variant)); if (variant != null) { work.add(factory.fromComponents(language, script, parent, null)); } } if (immediateParents.isEmpty()) { work.add(factory.fromComponents(language, script, null, variant)); if (variant != null) { work.add(factory.fromComponents(language, script, null, null)); } } if (script != null) { work.add(factory.fromComponents(language, null, region, variant)); if (variant != null) { work.add(factory.fromComponents(language, null, region, null)); } } } /** * Add inherited regions for a given locale. * * @param factory * @param inherits * @param language * @param script * @param region * @param variant */ private static void addParentRegionLocales(GwtLocaleFactory factory, List inherits, String language, String script, String region, String variant) { for (String parent : RegionInheritance.getAllAncestors(region)) { inherits.add(factory.fromComponents(language, script, parent, variant)); } } /** * Add special aliases for a given locale. * * This includes things like choosing the default region for Chinese based on * the script, handling Norwegian language changes, and treating pt_BR as the * default pt type. * * @param factory GwtLocaleFactory to create new instances with * @param locale locale to add deprecated pair for * @param aliases where to store the alias if present */ private static void addSpecialAliases(GwtLocaleFactory factory, GwtLocale locale, String initialScript, List aliases) { String language = locale.getLanguage(); String script = locale.getScript(); String region = locale.getRegion(); String variant = locale.getVariant(); if ("zh".equals(language) && region == null && (initialScript == null || initialScript.equals(script))) { // Add aliases for main users of particular scripts, but only if the // script matches what was initially supplied. This is to avoid cases // like zh_TW => zh => zh_CN, while still allowing zh => zh_CN. aliases.add(factory.fromComponents("zh", script, "Hant".equals(script) ? "TW" : "CN", variant)); } else if ("no".equals(language)) { if (variant == null || "BOKMAL".equals(variant)) { aliases.add(factory.fromComponents("nb", script, region, null)); aliases.add(factory.fromComponents("no-bok", script, region, null)); } else if ("NYNORSK".equals(variant)) { aliases.add(factory.fromComponents("nn", script, region, null)); aliases.add(factory.fromComponents("no-nyn", script, region, null)); } } else if ("nb".equals(language)) { aliases.add(factory.fromComponents("no", script, region, "BOKMAL")); } else if ("nn".equals(language)) { aliases.add(factory.fromComponents("no", script, region, "NYNORSK")); } else if ("pt".equals(language)) { if (region == null) { aliases.add(factory.fromComponents("pt", script, "BR", variant)); } else if ("BR".equals(region)) { aliases.add(factory.fromComponents("pt", script, null, variant)); } } } private static boolean equalsNullCheck(String str1, String str2) { if (str1 == null) { return str2 == null; } return str1.equals(str2); } /** * Compare strings, accounting for nulls (which are treated as before any * other value). * * @param a first string * @param b second string * * @return positive if a>b, negative if a cachedSearchList; // protected by cacheLock private List cachedAliases; /** * Must only be called from a factory to preserve instance caching. */ GwtLocaleImpl(GwtLocaleFactory factory, String language, String region, String script, String variant) { this.factory = factory; this.language = language; this.region = region; this.script = script; this.variant = variant; } public int compareTo(GwtLocale o) { int c = stringCompare(language, o.getLanguage()); if (c == 0) { c = stringCompare(script, o.getScript()); } if (c == 0) { c = stringCompare(region, o.getRegion()); } if (c == 0) { c = stringCompare(variant, o.getVariant()); } return c; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof GwtLocale)) { return false; } GwtLocale other = (GwtLocale) obj; return equalsNullCheck(language, other.getLanguage()) && equalsNullCheck(region, other.getRegion()) && equalsNullCheck(script, other.getScript()) && equalsNullCheck(variant, other.getVariant()); } public List getAliases() { // TODO(jat): more locale aliases? better way to encode them? synchronized (cacheLock) { if (cachedAliases == null) { cachedAliases = new ArrayList(); GwtLocale canonicalForm = getCanonicalForm(); Set seen = new HashSet(); cachedAliases.add(canonicalForm); ArrayList nextGroup = new ArrayList(); nextGroup.add(this); // Account for default script String defaultScript = DefaultLanguageScripts.getDefaultScript(language, region); if (defaultScript != null) { if (script == null) { nextGroup.add(factory.fromComponents(language, defaultScript, region, variant)); } else if (script.equals(defaultScript)) { nextGroup.add(factory.fromComponents(language, null, region, variant)); } } String initialScript = script == null ? defaultScript : script; while (!nextGroup.isEmpty()) { List thisGroup = nextGroup; nextGroup = new ArrayList(); for (GwtLocale locale : thisGroup) { if (seen.contains(locale)) { continue; } seen.add(locale); if (!locale.equals(canonicalForm)) { cachedAliases.add(locale); } addDeprecatedPairs(factory, locale, nextGroup); addSpecialAliases(factory, locale, initialScript, nextGroup); } } cachedAliases = Collections.unmodifiableList(cachedAliases); } return cachedAliases; } } public String getAsString() { StringBuilder buf = new StringBuilder(); if (language != null) { buf.append(language); } if (script != null) { buf.append('_'); buf.append(script); } if (region != null) { buf.append('_'); buf.append(region); } if (variant != null) { buf.append('_'); buf.append(variant); } return buf.toString(); } /** * Returns this locale in canonical form. Changes for canonical form are: *

    *
  • Deprecated language/region tags are replaced with official versions *
* * @return GwtLocale instance */ public GwtLocale getCanonicalForm() { String canonLanguage = language; String canonScript = script; String canonRegion = region; String canonVariant = variant; // Handle deprecated language codes int n = deprecatedLanguages.length; for (int i = 0; i < n; i += 2) { if (deprecatedLanguages[i + 1].equals(canonLanguage)) { canonLanguage = deprecatedLanguages[i]; break; } } // Handle deprecated region codes n = deprecatedRegions.length; for (int i = 0; i < n; i += 2) { if (deprecatedRegions[i + 1].equals(canonRegion)) { canonRegion = deprecatedRegions[i]; break; } } // Special-case Chinese default scripts if ("zh".equals(canonLanguage)) { if (canonRegion != null) { if ("CN".equals(canonRegion) && "Hans".equals(canonScript)) { canonScript = null; } else if ("TW".equals(canonRegion) && "Hant".equals(canonScript)) { canonScript = null; } } else if ("Hans".equals(canonScript)) { canonRegion = "CN"; canonScript = null; } else if ("Hant".equals(canonScript)) { canonRegion = "TW"; canonScript = null; } } // Special-case no->nb/nn split if ("no-bok".equals(canonLanguage)) { canonLanguage = "nb"; canonVariant = null; } else if ("no-nyn".equals(canonLanguage)) { canonLanguage = "nn"; canonVariant = null; } else if ("no".equals(canonLanguage)) { if (canonVariant == null || "BOKMAL".equals(canonVariant)) { canonLanguage = "nb"; canonVariant = null; } else if ("NYNORSK".equals(canonVariant)) { canonLanguage = "nn"; canonVariant = null; } } // Remove any default script for the language if (canonScript != null && canonScript.equals( DefaultLanguageScripts.getDefaultScript(canonLanguage, canonRegion))) { canonScript = null; } return factory.fromComponents(canonLanguage, canonScript, canonRegion, canonVariant); } public List getCompleteSearchList() { // TODO(jat): base order on distance from the initial locale, such as the // draft proposal at: // http://cldr.unicode.org/development/design-proposals/languagedistance // This will ensure that zh_Hant will come before zh in the search list for // zh_TW, and pa_Arab to come before pa in the search list for pa_PK. synchronized (cacheLock) { if (cachedSearchList == null) { cachedSearchList = new ArrayList(); Set seen = new HashSet(); String initialScript = script; if (initialScript == null) { initialScript = DefaultLanguageScripts.getDefaultScript(language, region); } List thisGroup = new ArrayList(); if (initialScript != null) { // Make sure the default script is listed first in the search list, // which ensures that zh_Hant appears before zh in the search list for // zh_TW. thisGroup.add(factory.fromComponents(language, initialScript, region, variant)); } thisGroup.add(this); seen.addAll(thisGroup); GwtLocale defLocale = factory.getDefault(); seen.add(defLocale); while (!thisGroup.isEmpty()) { cachedSearchList.addAll(thisGroup); List nextGroup = new ArrayList(); for (GwtLocale locale : thisGroup) { List aliases = locale.getAliases(); aliases = filterBadScripts(aliases, initialScript); List work = new ArrayList(aliases); work.removeAll(seen); nextGroup.addAll(work); seen.addAll(work); work.clear(); if (locale.getRegion() != null) { addImmediateParentRegions(factory, locale, work); } else if (locale.getVariant() != null) { work.add(factory.fromComponents(locale.getLanguage(), locale.getScript(), null, null)); } else if (locale.getScript() != null) { work.add(factory.fromComponents(locale.getLanguage(), null, null, null)); } work.removeAll(seen); nextGroup.addAll(work); seen.addAll(work); } thisGroup = nextGroup; } if (!isDefault()) { cachedSearchList.add(defLocale); } cachedSearchList = Collections.unmodifiableList(cachedSearchList); } return cachedSearchList; } } /** * Return a list of locales to search for, in order of preference. The * current locale is always first on the list. Aliases are not included * in the list -- use {@link #getAliases} to expand those. * * @return inheritance list */ public List getInheritanceChain() { List inherits = new ArrayList(); inherits.add(this); if (variant != null) { inherits.add(factory.fromComponents(language, script, region, null)); } if (region != null) { addParentRegionLocales(factory, inherits, language, script, region, null); inherits.add(factory.fromComponents(language, script, null, null)); } if (script != null) { inherits.add(factory.fromComponents(language, null, region, null)); addParentRegionLocales(factory, inherits, language, null, region, null); if (region != null) { inherits.add(factory.fromComponents(language, null, null, null)); } } if (language != null) { inherits.add(factory.fromComponents(null, null, null, null)); } return inherits; } public String getLanguage() { return language; } public String getLanguageNotNull() { return language == null ? "" : language; } public String getRegion() { return region; } public String getRegionNotNull() { return region == null ? "" : region; } public String getScript() { return script; } public String getScriptNotNull() { return script == null ? "" : script; } public String getVariant() { return variant; } public String getVariantNotNull() { return variant == null ? "" : variant; } @Override public int hashCode() { int result = ((language == null) ? 0 : language.hashCode()); result = 37 * result + ((region == null) ? 0 : region.hashCode()); result = 43 * result + ((script == null) ? 0 : script.hashCode()); result = 53 * result + ((variant == null) ? 0 : variant.hashCode()); return result; } /** * Return true if this locale inherits from the specified locale. Note that * locale.inheritsFrom(locale) is false -- if you want that to be true, you * should just use locale.getInheritanceChain().contains(x). * * @param parent locale to test against * @return true if parent is an ancestor of this locale */ public boolean inheritsFrom(GwtLocale parent) { if (equals(parent)) { return false; } return getInheritanceChain().contains(parent); } public boolean isDefault() { return language == null; } @Override public String toString() { if (language == null) { return DEFAULT_LOCALE; } return getAsString(); } /** * Checks if this locale uses the same script as another locale, taking into * account default scripts. * * @param other * @return true if the scripts are the same */ public boolean usesSameScript(GwtLocale other) { // The number of aliases is very small, so n^2 isn't a problem here. List myAliases = getAliases(); List otherAliases = other.getAliases(); for (GwtLocale alias : myAliases) { for (GwtLocale otherAlias : otherAliases) { if (equalsNullCheck(alias.getScript(), otherAlias.getScript())) { return true; } } } return false; } /** * Remove aliases which are incompatible with the initial script provided or * defaulted in a locale. * * @param aliases * @param initialScript * @return copy of aliases with incompatible scripts removed */ private List filterBadScripts(List aliases, String initialScript) { if (initialScript == null) { return aliases; } List result = new ArrayList(); for (GwtLocale alias : aliases) { String aliasScript = alias.getScript(); if (aliasScript == null || aliasScript.equals(initialScript)) { result.add(alias); } } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy