amhest
amhest

Reputation: 137

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.

Upvotes: 7

Views: 881

Answers (1)

Mattia Maestrini
Mattia Maestrini

Reputation: 32790

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.

Upvotes: 4

Related Questions