Search code examples
androidlocale

Getting Locale variant for simplified chinese


How can I get simplified chinese description (简体)? From the available locale Locale.SIMPLIFIED_CHINESE, no method seems to return this description:

  • getDisplayLanguage() returns the correct language name, but without the variant.
  • getDisplayName() returns the correct language name and country, but also without the variant.
  • getDisplayVariant() returns an empty string.

I've also tried to build a new Locale using the different constructors, also to no avail.

new Locale("zh", "CN");
new Locale("zh", "CN", "Hans");

I've checked the Android source code for LocalePicker and I've concluded that it is loaded from the resources (special_locale_codes and special_locale_names).

Any solutions besides having to hardcode/include this string in my resources?


Solution

  • Let me explain my process on how I tackled this. First, I found this block of code in LocalePicker.java

    private static String getDisplayName(Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
        String code = l.toString();
    
        for (int i = 0; i < specialLocaleCodes.length; i++) {
            if (specialLocaleCodes[i].equals(code)) {
                return specialLocaleNames[i];
            }
        }
        return l.getDisplayName(l);
    }
    

    which takes in a Locale as you already know. Then it tries to find the locale code in the specialLocaleCodes string array. The specialLocaleNames you are seeking are obtained from arrays.xml as you've helpfully stated:

    <string-array translatable="false" name="special_locale_codes">
        <item>ar_EG</item>
        <item>zh_CN</item>
        <item>zh_TW</item>
    </string-array>
    

    and the corresponding languages

    <string-array translatable="false" name="special_locale_names">
        <item>العربية</item>
        <item>中文 (简体)</item>
        <item>中文 (繁體)</item>
    </string-array>
    

    Notice the code with the simplified Chinese is zh_CN and the last two characters are capitalized.

    However,

    Locale locale = new Locale("zh_CN");
    System.out.println("Locale: " + locale);
    

    prints

    Locale: zh_cn

    Notice the lower case. So there is no way specialLocaleCodes[i].equals(code) will return true. So then I poked around Locale.java and, long story short, we can bypass that case-changing jumble by doing this (and you MUST keep the 3rd parameter as an empty string for this to work):

    Locale locale = new Locale("zh", "CN", "");
    System.out.println("Locale: " + locale);
    

    Prints

    Locale: zh_CN

    With this you should be able to do this:

    Locale locale = new Locale("zh", "CN", "");
    System.out.println("Name:" + locale.getDisplayName(locale));
    

    Upon further inspection on Kitkat using this (thank you Andrew!)

    int specialLocaleNamesId = Resources.getSystem().getIdentifier("special_locale_names", "array", "android");
    String[] specialLocaleNames = Resources.getSystem().getStringArray(specialLocaleNamesId);
    

    it was possible to print out

    العربية,中文 (简体),中文 (繁體)

    as expected. However, something in Kitkat is still preventing the correct string to display. Frustrating.


    However, in Lollipop 5.0+ and Java 1.7 this works using forLanguageTag() in Locale.

    Locale locale = Locale.forLanguageTag("zh-Hans");
    System.out.println("getDisplayName:" + locale.getDisplayName(locale));
    System.out.println("getDisplayLanguage:" + locale.getDisplayLanguage(locale));
    

    which prints

    getDisplayName:中文 (简体中文)
    getDisplayLanguage:中文