• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.util;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamField;
24 import java.io.Serializable;
25 //import libcore.icu.ICU;
26 
27 
28 /**
29  * {@code Locale} represents a language/country/variant combination. Locales are used to
30  * alter the presentation of information such as numbers or dates to suit the conventions
31  * in the region they describe.
32  *
33  * <p>The language codes are two-letter lowercase ISO language codes (such as "en") as defined by
34  * <a href="http://en.wikipedia.org/wiki/ISO_639-1">ISO 639-1</a>.
35  * The country codes are two-letter uppercase ISO country codes (such as "US") as defined by
36  * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-3">ISO 3166-1</a>.
37  * The variant codes are unspecified.
38  *
39  * <p>Note that Java uses several deprecated two-letter codes. The Hebrew ("he") language
40  * code is rewritten as "iw", Indonesian ("id") as "in", and Yiddish ("yi") as "ji". This
41  * rewriting happens even if you construct your own {@code Locale} object, not just for
42  * instances returned by the various lookup methods.
43  *
44  * <a name="available_locales"><h3>Available locales</h3></a>
45  * <p>This class' constructors do no error checking. You can create a {@code Locale} for languages
46  * and countries that don't exist, and you can create instances for combinations that don't
47  * exist (such as "de_US" for "German as spoken in the US").
48  *
49  * <p>Note that locale data is not necessarily available for any of the locales pre-defined as
50  * constants in this class except for en_US, which is the only locale Java guarantees is always
51  * available.
52  *
53  * <p>It is also a mistake to assume that all devices have the same locales available.
54  * A device sold in the US will almost certainly support en_US and es_US, but not necessarily
55  * any locales with the same language but different countries (such as en_GB or es_ES),
56  * nor any locales for other languages (such as de_DE). The opposite may well be true for a device
57  * sold in Europe.
58  *
59  * <p>You can use {@link Locale#getDefault} to get an appropriate locale for the <i>user</i> of the
60  * device you're running on, or {@link Locale#getAvailableLocales} to get a list of all the locales
61  * available on the device you're running on.
62  *
63  * <a name="locale_data"><h3>Locale data</h3></a>
64  * <p>Note that locale data comes solely from ICU. User-supplied locale service providers (using
65  * the {@code java.text.spi} or {@code java.util.spi} mechanisms) are not supported.
66  *
67  * <p>Here are the versions of ICU (and the corresponding CLDR and Unicode versions) used in
68  * various Android releases:
69  * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
70  * <tr><td>Cupcake/Donut/Eclair</td> <td>ICU 3.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-5">CLDR 1.5</a></td>   <td><a href="http://www.unicode.org/versions/Unicode5.0.0/">Unicode 5.0</a></td></tr>
71  * <tr><td>Froyo</td>                <td>ICU 4.2</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-7">CLDR 1.7</a></td>   <td><a href="http://www.unicode.org/versions/Unicode5.1.0/">Unicode 5.1</a></td></tr>
72  * <tr><td>Gingerbread/Honeycomb</td><td>ICU 4.4</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-8">CLDR 1.8</a></td>   <td><a href="http://www.unicode.org/versions/Unicode5.2.0/">Unicode 5.2</a></td></tr>
73  * <tr><td>Ice Cream Sandwich</td>   <td>ICU 4.6</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td>   <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
74  * <tr><td>Jelly Bean</td>           <td>ICU 4.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-2-0">CLDR 2.0</a></td>   <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
75  * <tr><td>Jelly Bean MR2</td>       <td>ICU 50</td>  <td><a href="http://cldr.unicode.org/index/downloads/cldr-21-1">CLDR 22.1</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
76  * </table>
77  *
78  * <a name="default_locale"><h3>Be wary of the default locale</h3></a>
79  * <p>Note that there are many convenience methods that automatically use the default locale, but
80  * using them may lead to subtle bugs.
81  *
82  * <p>The default locale is appropriate for tasks that involve presenting data to the user. In
83  * this case, you want to use the user's date/time formats, number
84  * formats, rules for conversion to lowercase, and so on. In this case, it's safe to use the
85  * convenience methods.
86  *
87  * <p>The default locale is <i>not</i> appropriate for machine-readable output. The best choice
88  * there is usually {@code Locale.US}&nbsp;&ndash; this locale is guaranteed to be available on all
89  * devices, and the fact that it has no surprising special cases and is frequently used (especially
90  * for computer-computer communication) means that it tends to be the most efficient choice too.
91  *
92  * <p>A common mistake is to implicitly use the default locale when producing output meant to be
93  * machine-readable. This tends to work on the developer's test devices (especially because so many
94  * developers use en_US), but fails when run on a device whose user is in a more complex locale.
95  *
96  * <p>For example, if you're formatting integers some locales will use non-ASCII decimal
97  * digits. As another example, if you're formatting floating-point numbers some locales will use
98  * {@code ','} as the decimal point and {@code '.'} for digit grouping. That's correct for
99  * human-readable output, but likely to cause problems if presented to another
100  * computer ({@link Double#parseDouble} can't parse such a number, for example).
101  * You should also be wary of the {@link String#toLowerCase} and
102  * {@link String#toUpperCase} overloads that don't take a {@code Locale}: in Turkey, for example,
103  * the characters {@code 'i'} and {@code 'I'} won't be converted to {@code 'I'} and {@code 'i'}.
104  * This is the correct behavior for Turkish text (such as user input), but inappropriate for, say,
105  * HTTP headers.
106  */
107 public final class Locale implements Cloneable, Serializable {
108 
109     private static final long serialVersionUID = 9149081749638150636L;
110 
111     /**
112      * Locale constant for en_CA.
113      */
114     public static final Locale CANADA = new Locale(true, "en", "CA");
115 
116     /**
117      * Locale constant for fr_CA.
118      */
119     public static final Locale CANADA_FRENCH = new Locale(true, "fr", "CA");
120 
121     /**
122      * Locale constant for zh_CN.
123      */
124     public static final Locale CHINA = new Locale(true, "zh", "CN");
125 
126     /**
127      * Locale constant for zh.
128      */
129     public static final Locale CHINESE = new Locale(true, "zh", "");
130 
131     /**
132      * Locale constant for en.
133      */
134     public static final Locale ENGLISH = new Locale(true, "en", "");
135 
136     /**
137      * Locale constant for fr_FR.
138      */
139     public static final Locale FRANCE = new Locale(true, "fr", "FR");
140 
141     /**
142      * Locale constant for fr.
143      */
144     public static final Locale FRENCH = new Locale(true, "fr", "");
145 
146     /**
147      * Locale constant for de.
148      */
149     public static final Locale GERMAN = new Locale(true, "de", "");
150 
151     /**
152      * Locale constant for de_DE.
153      */
154     public static final Locale GERMANY = new Locale(true, "de", "DE");
155 
156     /**
157      * Locale constant for it.
158      */
159     public static final Locale ITALIAN = new Locale(true, "it", "");
160 
161     /**
162      * Locale constant for it_IT.
163      */
164     public static final Locale ITALY = new Locale(true, "it", "IT");
165 
166     /**
167      * Locale constant for ja_JP.
168      */
169     public static final Locale JAPAN = new Locale(true, "ja", "JP");
170 
171     /**
172      * Locale constant for ja.
173      */
174     public static final Locale JAPANESE = new Locale(true, "ja", "");
175 
176     /**
177      * Locale constant for ko_KR.
178      */
179     public static final Locale KOREA = new Locale(true, "ko", "KR");
180 
181     /**
182      * Locale constant for ko.
183      */
184     public static final Locale KOREAN = new Locale(true, "ko", "");
185 
186     /**
187      * Locale constant for zh_CN.
188      */
189     public static final Locale PRC = new Locale(true, "zh", "CN");
190 
191     /**
192      * Locale constant for the root locale. The root locale has an empty language,
193      * country, and variant.
194      *
195      * @since 1.6
196      */
197     public static final Locale ROOT = new Locale(true, "", "");
198 
199     /**
200      * Locale constant for zh_CN.
201      */
202     public static final Locale SIMPLIFIED_CHINESE = new Locale(true, "zh", "CN");
203 
204     /**
205      * Locale constant for zh_TW.
206      */
207     public static final Locale TAIWAN = new Locale(true, "zh", "TW");
208 
209     /**
210      * Locale constant for zh_TW.
211      */
212     public static final Locale TRADITIONAL_CHINESE = new Locale(true, "zh", "TW");
213 
214     /**
215      * Locale constant for en_GB.
216      */
217     public static final Locale UK = new Locale(true, "en", "GB");
218 
219     /**
220      * Locale constant for en_US.
221      */
222     public static final Locale US = new Locale(true, "en", "US");
223 
224 //    /**
225 //     * The current default locale. It is temporarily assigned to US because we
226 //     * need a default locale to lookup the real default locale.
227 //     */
228     private static Locale defaultLocale = US;
229 
230 //    static {
231 //        String language = System.getProperty("user.language", "en");
232 //        String region = System.getProperty("user.region", "US");
233 //        String variant = System.getProperty("user.variant", "");
234 //        defaultLocale = new Locale(language, region, variant);
235 //    }
236 
237     private transient String countryCode;
238     private transient String languageCode;
239     private transient String variantCode;
240     private transient String cachedToStringResult;
241 
242     /**
243      * There's a circular dependency between toLowerCase/toUpperCase and
244      * Locale.US. Work around this by avoiding these methods when constructing
245      * the built-in locales.
246      *
247      * @param unused required for this constructor to have a unique signature
248      */
Locale(boolean unused, String lowerCaseLanguageCode, String upperCaseCountryCode)249     private Locale(boolean unused, String lowerCaseLanguageCode, String upperCaseCountryCode) {
250         this.languageCode = lowerCaseLanguageCode;
251         this.countryCode = upperCaseCountryCode;
252         this.variantCode = "";
253     }
254 
255     /**
256      * Constructs a new {@code Locale} using the specified language.
257      */
Locale(String language)258     public Locale(String language) {
259         this(language, "", "");
260     }
261 
262     /**
263      * Constructs a new {@code Locale} using the specified language and country codes.
264      */
Locale(String language, String country)265     public Locale(String language, String country) {
266         this(language, country, "");
267     }
268 
269     /**
270      * Constructs a new {@code Locale} using the specified language, country,
271      * and variant codes.
272      */
Locale(String language, String country, String variant)273     public Locale(String language, String country, String variant) {
274         if (language == null || country == null || variant == null) {
275             throw new NullPointerException("language=" + language +
276                                            ",country=" + country +
277                                            ",variant=" + variant);
278         }
279         if (language.isEmpty() && country.isEmpty()) {
280             languageCode = "";
281             countryCode = "";
282             variantCode = variant;
283             return;
284         }
285 
286 //        languageCode = language.toLowerCase(Locale.US);      // not supported by GWT
287         languageCode = language.toLowerCase();
288         // Map new language codes to the obsolete language
289         // codes so the correct resource bundles will be used.
290         if (languageCode.equals("he")) {
291             languageCode = "iw";
292         } else if (languageCode.equals("id")) {
293             languageCode = "in";
294         } else if (languageCode.equals("yi")) {
295             languageCode = "ji";
296         }
297 
298 //        countryCode = country.toUpperCase(Locale.US);      // not supported by GWT
299         countryCode = country.toUpperCase();
300 
301         // Work around for be compatible with RI
302         variantCode = variant;
303     }
304 
305 //    @Override public Object clone() {
306 //        try {
307 //            return super.clone();
308 //        } catch (CloneNotSupportedException e) {
309 //            throw new AssertionError(e);
310 //        }
311 //    }
312 
313     /**
314      * Returns true if {@code object} is a locale with the same language,
315      * country and variant.
316      */
equals(Object object)317     @Override public boolean equals(Object object) {
318         if (object == this) {
319             return true;
320         }
321         if (object instanceof Locale) {
322             Locale o = (Locale) object;
323             return languageCode.equals(o.languageCode)
324                     && countryCode.equals(o.countryCode)
325                     && variantCode.equals(o.variantCode);
326         }
327         return false;
328     }
329 
330 //    /**
331 //     * Returns the system's installed locales. This array always includes {@code
332 //     * Locale.US}, and usually several others. Most locale-sensitive classes
333 //     * offer their own {@code getAvailableLocales} method, which should be
334 //     * preferred over this general purpose method.
335 //     *
336 //     * @see java.text.BreakIterator#getAvailableLocales()
337 //     * @see java.text.Collator#getAvailableLocales()
338 //     * @see java.text.DateFormat#getAvailableLocales()
339 //     * @see java.text.DateFormatSymbols#getAvailableLocales()
340 //     * @see java.text.DecimalFormatSymbols#getAvailableLocales()
341 //     * @see java.text.NumberFormat#getAvailableLocales()
342 //     * @see java.util.Calendar#getAvailableLocales()
343 //     */
344 //    public static Locale[] getAvailableLocales() {
345 //        return ICU.getAvailableLocales();
346 //    }
347 
348     /**
349      * Returns the country code for this locale, or {@code ""} if this locale
350      * doesn't correspond to a specific country.
351      */
getCountry()352     public String getCountry() {
353         return countryCode;
354     }
355 
356     /**
357      * Returns the user's preferred locale. This may have been overridden for
358      * this process with {@link #setDefault}.
359      *
360      * <p>Since the user's locale changes dynamically, avoid caching this value.
361      * Instead, use this method to look it up for each use.
362      */
getDefault()363     public static Locale getDefault() {
364         return defaultLocale;
365     }
366 
367 //    /**
368 //     * Equivalent to {@code getDisplayCountry(Locale.getDefault())}.
369 //     */
370 //    public final String getDisplayCountry() {
371 //        return getDisplayCountry(getDefault());
372 //    }
373 //
374 //    /**
375 //     * Returns the name of this locale's country, localized to {@code locale}.
376 //     * Returns the empty string if this locale does not correspond to a specific
377 //     * country.
378 //     */
379 //    public String getDisplayCountry(Locale locale) {
380 //        if (countryCode.isEmpty()) {
381 //            return "";
382 //        }
383 //        String result = ICU.getDisplayCountryNative(toString(), locale.toString());
384 //        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
385 //            result = ICU.getDisplayCountryNative(toString(), Locale.getDefault().toString());
386 //        }
387 //        return result;
388 //    }
389 //
390 //    /**
391 //     * Equivalent to {@code getDisplayLanguage(Locale.getDefault())}.
392 //     */
393 //    public final String getDisplayLanguage() {
394 //        return getDisplayLanguage(getDefault());
395 //    }
396 //
397 //    /**
398 //     * Returns the name of this locale's language, localized to {@code locale}.
399 //     * If the language name is unknown, the language code is returned.
400 //     */
401 //    public String getDisplayLanguage(Locale locale) {
402 //        if (languageCode.isEmpty()) {
403 //            return "";
404 //        }
405 //
406 //        // http://b/8049507 --- frameworks/base should use fil_PH instead of tl_PH.
407 //        // Until then, we're stuck covering their tracks, making it look like they're
408 //        // using "fil" when they're not.
409 //        String localeString = toString();
410 //        if (languageCode.equals("tl")) {
411 //            localeString = toNewString("fil", countryCode, variantCode);
412 //        }
413 //
414 //        String result = ICU.getDisplayLanguageNative(localeString, locale.toString());
415 //        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
416 //            result = ICU.getDisplayLanguageNative(localeString, Locale.getDefault().toString());
417 //        }
418 //        return result;
419 //    }
420 //
421 //    /**
422 //     * Equivalent to {@code getDisplayName(Locale.getDefault())}.
423 //     */
424 //    public final String getDisplayName() {
425 //        return getDisplayName(getDefault());
426 //    }
427 //
428 //    /**
429 //     * Returns this locale's language name, country name, and variant, localized
430 //     * to {@code locale}. The exact output form depends on whether this locale
431 //     * corresponds to a specific language, country and variant.
432 //     *
433 //     * <p>For example:
434 //     * <ul>
435 //     * <li>{@code new Locale("en").getDisplayName(Locale.US)} -> {@code English}
436 //     * <li>{@code new Locale("en", "US").getDisplayName(Locale.US)} -> {@code English (United States)}
437 //     * <li>{@code new Locale("en", "US", "POSIX").getDisplayName(Locale.US)} -> {@code English (United States,Computer)}
438 //     * <li>{@code new Locale("en").getDisplayName(Locale.FRANCE)} -> {@code anglais}
439 //     * <li>{@code new Locale("en", "US").getDisplayName(Locale.FRANCE)} -> {@code anglais (tats-Unis)}
440 //     * <li>{@code new Locale("en", "US", "POSIX").getDisplayName(Locale.FRANCE)} -> {@code anglais (tats-Unis,informatique)}.
441 //     * </ul>
442 //     */
443 //    public String getDisplayName(Locale locale) {
444 //        int count = 0;
445 //        StringBuilder buffer = new StringBuilder();
446 //        if (!languageCode.isEmpty()) {
447 //            String displayLanguage = getDisplayLanguage(locale);
448 //            buffer.append(displayLanguage.isEmpty() ? languageCode : displayLanguage);
449 //            ++count;
450 //        }
451 //        if (!countryCode.isEmpty()) {
452 //            if (count == 1) {
453 //                buffer.append(" (");
454 //            }
455 //            String displayCountry = getDisplayCountry(locale);
456 //            buffer.append(displayCountry.isEmpty() ? countryCode : displayCountry);
457 //            ++count;
458 //        }
459 //        if (!variantCode.isEmpty()) {
460 //            if (count == 1) {
461 //                buffer.append(" (");
462 //            } else if (count == 2) {
463 //                buffer.append(",");
464 //            }
465 //            String displayVariant = getDisplayVariant(locale);
466 //            buffer.append(displayVariant.isEmpty() ? variantCode : displayVariant);
467 //            ++count;
468 //        }
469 //        if (count > 1) {
470 //            buffer.append(")");
471 //        }
472 //        return buffer.toString();
473 //    }
474 //
475 //    /**
476 //     * Returns the full variant name in the default {@code Locale} for the variant code of
477 //     * this {@code Locale}. If there is no matching variant name, the variant code is
478 //     * returned.
479 //     */
480 //    public final String getDisplayVariant() {
481 //        return getDisplayVariant(getDefault());
482 //    }
483 //
484 //    /**
485 //     * Returns the full variant name in the specified {@code Locale} for the variant code
486 //     * of this {@code Locale}. If there is no matching variant name, the variant code is
487 //     * returned.
488 //     */
489 //    public String getDisplayVariant(Locale locale) {
490 //        if (variantCode.length() == 0) {
491 //            return variantCode;
492 //        }
493 //        String result = ICU.getDisplayVariantNative(toString(), locale.toString());
494 //        if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
495 //            result = ICU.getDisplayVariantNative(toString(), Locale.getDefault().toString());
496 //        }
497 //        return result;
498 //    }
499 //
500 //    /**
501 //     * Returns the three-letter ISO 3166 country code which corresponds to the country
502 //     * code for this {@code Locale}.
503 //     * @throws MissingResourceException if there's no 3-letter country code for this locale.
504 //     */
505 //    public String getISO3Country() {
506 //        String code = ICU.getISO3CountryNative(toString());
507 //        if (!countryCode.isEmpty() && code.isEmpty()) {
508 //            throw new MissingResourceException("No 3-letter country code for locale: " + this, "FormatData_" + this, "ShortCountry");
509 //        }
510 //        return code;
511 //    }
512 //
513 //    /**
514 //     * Returns the three-letter ISO 639-2/T language code which corresponds to the language
515 //     * code for this {@code Locale}.
516 //     * @throws MissingResourceException if there's no 3-letter language code for this locale.
517 //     */
518 //    public String getISO3Language() {
519 //        String code = ICU.getISO3LanguageNative(toString());
520 //        if (!languageCode.isEmpty() && code.isEmpty()) {
521 //            throw new MissingResourceException("No 3-letter language code for locale: " + this, "FormatData_" + this, "ShortLanguage");
522 //        }
523 //        return code;
524 //    }
525 //
526 //    /**
527 //     * Returns an array of strings containing all the two-letter ISO 3166 country codes that can be
528 //     * used as the country code when constructing a {@code Locale}.
529 //     */
530 //    public static String[] getISOCountries() {
531 //        return ICU.getISOCountries();
532 //    }
533 //
534 //    /**
535 //     * Returns an array of strings containing all the two-letter ISO 639-1 language codes that can be
536 //     * used as the language code when constructing a {@code Locale}.
537 //     */
538 //    public static String[] getISOLanguages() {
539 //        return ICU.getISOLanguages();
540 //    }
541 
542     /**
543      * Returns the language code for this {@code Locale} or the empty string if no language
544      * was set.
545      */
getLanguage()546     public String getLanguage() {
547         return languageCode;
548     }
549 
550     /**
551      * Returns the variant code for this {@code Locale} or an empty {@code String} if no variant
552      * was set.
553      */
getVariant()554     public String getVariant() {
555         return variantCode;
556     }
557 
558     @Override
hashCode()559     public synchronized int hashCode() {
560         return countryCode.hashCode() + languageCode.hashCode()
561                 + variantCode.hashCode();
562     }
563 
564     /**
565      * Overrides the default locale. This does not affect system configuration,
566      * and attempts to override the system-provided default locale may
567      * themselves be overridden by actual changes to the system configuration.
568      * Code that calls this method is usually incorrect, and should be fixed by
569      * passing the appropriate locale to each locale-sensitive method that's
570      * called.
571      */
setDefault(Locale locale)572     public synchronized static void setDefault(Locale locale) {
573         if (locale == null) {
574             throw new NullPointerException("locale == null");
575         }
576         defaultLocale = locale;
577     }
578 
579     /**
580      * Returns the string representation of this {@code Locale}. It consists of the
581      * language code, country code and variant separated by underscores.
582      * If the language is missing the string begins
583      * with an underscore. If the country is missing there are 2 underscores
584      * between the language and the variant. The variant cannot stand alone
585      * without a language and/or country code: in this case this method would
586      * return the empty string.
587      *
588      * <p>Examples: "en", "en_US", "_US", "en__POSIX", "en_US_POSIX"
589      */
590     @Override
toString()591     public final String toString() {
592         String result = cachedToStringResult;
593         if (result == null) {
594             result = cachedToStringResult = toNewString(languageCode, countryCode, variantCode);
595         }
596         return result;
597     }
598 
toNewString(String languageCode, String countryCode, String variantCode)599     private static String toNewString(String languageCode, String countryCode, String variantCode) {
600         // The string form of a locale that only has a variant is the empty string.
601         if (languageCode.length() == 0 && countryCode.length() == 0) {
602             return "";
603         }
604         // Otherwise, the output format is "ll_cc_variant", where language and country are always
605         // two letters, but the variant is an arbitrary length. A size of 11 characters has room
606         // for "en_US_POSIX", the largest "common" value. (In practice, the string form is almost
607         // always 5 characters: "ll_cc".)
608         StringBuilder result = new StringBuilder(11);
609         result.append(languageCode);
610         if (countryCode.length() > 0 || variantCode.length() > 0) {
611             result.append('_');
612         }
613         result.append(countryCode);
614         if (variantCode.length() > 0) {
615             result.append('_');
616         }
617         result.append(variantCode);
618         return result.toString();
619     }
620 
621 //    private static final ObjectStreamField[] serialPersistentFields = {
622 //        new ObjectStreamField("country", String.class),
623 //        new ObjectStreamField("hashcode", int.class),
624 //        new ObjectStreamField("language", String.class),
625 //        new ObjectStreamField("variant", String.class),
626 //    };
627 //
628 //    private void writeObject(ObjectOutputStream stream) throws IOException {
629 //        ObjectOutputStream.PutField fields = stream.putFields();
630 //        fields.put("country", countryCode);
631 //        fields.put("hashcode", -1);
632 //        fields.put("language", languageCode);
633 //        fields.put("variant", variantCode);
634 //        stream.writeFields();
635 //    }
636 //
637 //    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
638 //        ObjectInputStream.GetField fields = stream.readFields();
639 //        countryCode = (String) fields.get("country", "");
640 //        languageCode = (String) fields.get("language", "");
641 //        variantCode = (String) fields.get("variant", "");
642 //    }
643 }
644