Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

toUpperCase on Android is incorrect for two-argument and default Greek and Turkish Locales

I am having trouble with Greek and Turkish when using toUpperCase() with the default locale or more interestingly the two argument Locale constructor.

Issue happens on Galaxy Tab S2 Android 5.0.2 (also reproduced on 5.1.1)
The Issue is reproducible via BOTH the Settings App AND MoreLocale 2

Considering this value for title: Τέλος συνεδρίας

These calls work fine.

title.toUpperCase(new Locale("el_GR")) 
title.toUpperCase(new Locale("el-GR"))

Both generate the correct result. If you look closely there are tick marks after the T and the P.

ΤΈΛΟΣ ΣΥΝΕΔΡΊΑΣ

However, I get a different result for the default locale and the two-argument Locale constructor.

This is the default Locale on my tablet:

Locale.getDefault() ==  el_GR

Which is used in the generic toUpperCase()

public String toUpperCase() {
    return CaseMapper.toUpperCase(Locale.getDefault(), this, value, offset, count);
}

When I call this it returns the incorrect result.

title.toUpperCase()

Note the missing tick marks on the T and P.

ΤΕΛΟΣ ΣΥΝΕΔΡΙΑΣ

I get the same result if I use the two argument constructor for a new locale:

title.toUpperCase(new Locale("el","GR"))

ΤΕΛΟΣ ΣΥΝΕΔΡΙΑΣ

Adding these lines to application startup resolves the issues but is rather hackish.

String language = Locale.getDefault().getLanguage();
String country = Locale.getDefault().getCountry();
Locale.setDefault(new Locale(language + "-" + country));

I would prefer to simply defer to the default locale.

like image 444
amhest Avatar asked Aug 25 '16 16:08

amhest


People also ask

How do I change the default Locale in Android?

Configuration config = new Configuration(); config. locale = selectedLocale; // set accordingly // eg. if Hindi then selectedLocale = new Locale("hi"); Locale. setDefault(selectedLocale); // has no effect Resources res = getApplicationContext().

What is toUpperCase Locale root?

toUpperCase(Locale locale) method converts all of the characters in this String to upper case using the rules of the given Locale.

Which method is used to obtain the locales supported on the device?

Use of LocaleUse getCountry to get the country (or region) code and getLanguage to get the language code. You can use getDisplayCountry to get the name of the country suitable for displaying to the user. Similarly, you can use getDisplayLanguage to get the name of the language suitable for displaying to the user.


1 Answers

As you can see in CaseMapper.java the method toUpperCase have a special behaviour for the Azeri, Lithuanian, Turkish and Greek locales:

public static String toUpperCase(Locale locale, String s, int count) {
    String languageCode = locale.getLanguage();
    if (languageCode.equals("tr") || languageCode.equals("az") || languageCode.equals("lt")) {
        return ICU.toUpperCase(s, locale);
    }
    if (languageCode.equals("el")) {
        return EL_UPPER.get().transliterate(s);
    }

    ...
}

Why you got different results depending on the type of Locale used?

Let's try to analyze the different kind of Locale created:

locale = new Locale("el_GR");
Log.d(TAG, String.format("[el_GR] Language: '%s' ~ Country: '%s'", locale.getLanguage(), locale.getCountry()));

locale = new Locale("el", "GR");
Log.d(TAG, String.format("[el, GR] Language: '%s' ~ Country: '%s'", locale.getLanguage(), locale.getCountry()));

locale = Locale.getDefault(); //Device with el_GR language
Log.d(TAG, String.format("[default] Language: '%s' ~ Country: '%s'", locale.getLanguage(), locale.getCountry()));

These are the results obtained:

[el_GR] Language: 'el_gr' ~ Country: ''
[el, GR] Language: 'el' ~ Country: 'GR'
[default] Language: 'el' ~ Country: 'GR'

As you can see in the manually created Locale with only one parameter the language is el_gr, for the other two cases instead the language is el. The toUpperCase has a different behaviour only when the language code is tr, az, lt or el, otherwise it behave normally. For this reason the output is different.

Unfortunately if you want the tick marks the only viable solution is to use the Locale constructor with only one parameter.

like image 96
Mattia Maestrini Avatar answered Sep 29 '22 00:09

Mattia Maestrini