140

Is there a neat way of getting a Locale instance from its "programmatic name" as returned by Locale's toString() method? An obvious and ugly solution would be parsing the String and then constructing a new Locale instance according to that, but maybe there's a better way / ready solution for that?

The need is that I want to store some locale specific settings in a SQL database, including Locales themselves, but it would be ugly to put serialized Locale objects there. I would rather store their String representations, which seem to be quite adequate in detail.

Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169

13 Answers13

156

Method that returns locale from string exists in commons-lang library: LocaleUtils.toLocale(localeAsString)

yurilo
  • 3,079
  • 2
  • 23
  • 14
77

Since Java 7 there is factory method Locale.forLanguageTag and instance method Locale.toLanguageTag using IETF language tags.

nilskp
  • 3,097
  • 1
  • 30
  • 34
  • 26
    Just want to emphasize that `Locale.forLanguageTag` works IETF locale strings (ie `en-US`) and does not work with ISO locale strings (ie `en_US`) – Fabian Nov 02 '18 at 20:46
50
  1. Java provides lot of things with proper implementation lot of complexity can be avoided. This returns ms_MY.

    String key = "ms-MY";
    Locale locale = new Locale.Builder().setLanguageTag(key).build();
    
  2. Apache Commons has LocaleUtils to help parse a string representation. This will return en_US

    String str = "en-US";
    Locale locale =  LocaleUtils.toLocale(str);
    System.out.println(locale.toString());
    
  3. You can also use locale constructors.

    // Construct a locale from a language code.(eg: en)
    new Locale(String language)
    // Construct a locale from language and country.(eg: en and US)
    new Locale(String language, String country)
    // Construct a locale from language, country and variant.
    new Locale(String language, String country, String variant)
    

Please check this LocaleUtils and this Locale to explore more methods.

rds
  • 26,253
  • 19
  • 107
  • 134
VedantK
  • 9,728
  • 7
  • 66
  • 71
  • 1
    LocaleUtils.toLocale(localeStringRepresentation) does the job neatly. Also if you see the implementation of this method, it is quite comprehensive! – coretechie Jul 26 '17 at 11:19
  • org.apache.commons.lang3.LocaleUtils.toLocale will throw an exception if it receives "en-US". From the docs: This method validates the input strictly. The language code must be lowercase. The country code must be uppercase. The separator must be an underscore. The length must be correct. – Barsum May 19 '23 at 13:35
37

See the Locale.getLanguage(), Locale.getCountry()... Store this combination in the database instead of the "programatic name"...
When you want to build the Locale back, use public Locale(String language, String country)

Here is a sample code :)

// May contain simple syntax error, I don't have java right now to test..
// but this is a bigger picture for your algo...
public String localeToString(Locale l) {
    return l.getLanguage() + "," + l.getCountry();
}

public Locale stringToLocale(String s) {
    StringTokenizer tempStringTokenizer = new StringTokenizer(s,",");
    if(tempStringTokenizer.hasMoreTokens())
    String l = tempStringTokenizer.nextElement();
    if(tempStringTokenizer.hasMoreTokens())
    String c = tempStringTokenizer.nextElement();
    return new Locale(l,c);
}
florian negre
  • 114
  • 1
  • 5
raj
  • 3,769
  • 4
  • 25
  • 43
23

Option 1 :

org.apache.commons.lang3.LocaleUtils.toLocale("en_US")

Option 2 :

Locale.forLanguageTag("en-US")

Please note Option 1 is "underscore" between language and country , and Option 2 is "dash".

anatol
  • 1,680
  • 2
  • 24
  • 47
junjun
  • 296
  • 2
  • 5
15

If you are using Spring framework in your project you can also use:

org.springframework.util.StringUtils.parseLocaleString("en_US");

Documentation:

Parse the given String representation into a Locale

12

This answer may be a little late, but it turns out that parsing out the string is not as ugly as the OP assumed. I found it quite simple and concise:

public static Locale fromString(String locale) {
    String parts[] = locale.split("_", -1);
    if (parts.length == 1) return new Locale(parts[0]);
    else if (parts.length == 2
            || (parts.length == 3 && parts[2].startsWith("#")))
        return new Locale(parts[0], parts[1]);
    else return new Locale(parts[0], parts[1], parts[2]);
}

I tested this (on Java 7) with all the examples given in the Locale.toString() documentation: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "zh_CN_#Hans", "zh_TW_#Hant-x-java", and "th_TH_TH_#u-nu-thai".

IMPORTANT UPDATE: This is not recommended for use in Java 7+ according to the documentation:

In particular, clients who parse the output of toString into language, country, and variant fields can continue to do so (although this is strongly discouraged), although the variant field will have additional information in it if script or extensions are present.

Use Locale.forLanguageTag and Locale.toLanguageTag instead, or if you must, Locale.Builder.

andy
  • 2,953
  • 2
  • 24
  • 26
  • 6
    Java 7 `Locale.forLanguageTag` applies only for language tags encoded as indicated in IETF's BCP 47, with an hyphen (`-`), not an underscore (`_`) as in the return of `Locale`'s `toString` method – Jaime Hablutzel Nov 16 '13 at 22:27
  • 1
    You're right. There still needs to be some way to convert the existing Locale representations to BCP47 format. My intention was to suggest that going forward, `Locale`s should not be stored in their `toString` form, but in their `toLanguageTag` form, which is convertible back to a `Locale` more easily and accurately. – andy Nov 22 '13 at 21:08
  • Wouldn't this method have a number of edge cases that could cause index out of bounds? – user2524908 Feb 09 '18 at 16:45
  • @user2524908: I don't think so, as he is always testing the array length before accessing its elements. The solution might have many edge cases where it does not work properly, but not "index out of bounds" – MestreLion Jul 07 '20 at 10:29
8

Old question with plenty of answers, but here's more solutions:

Gray
  • 115,027
  • 24
  • 293
  • 354
Tauren
  • 26,795
  • 42
  • 131
  • 167
3

There doesn't seem to be a static valueOf method for this, which is a bit surprising.

One rather ugly, but simple, way, would be to iterate over Locale.getAvailableLocales(), comparing their toString values with your value.

Not very nice, but no string parsing required. You could pre-populate a Map of Strings to Locales, and look up your database string in that Map.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Ah, the iteration might be quite a reasonable solution. Indeed it's surprising that Locale doens't have a static method for this. – Joonas Pulakka Mar 26 '10 at 09:55
  • The predifned `Locale` instances represent a very smal subset of valid locales only. It's by no means complete. – BetaRide Sep 09 '14 at 07:00
3

Might be late but if someone looking for simple solution:

Instead of toString() set Locale string by using: String langTag = localeObj.toLanguageTag();

Store langTag in DB or wherever you want

At the consumer side get String lanTag = fromDB(); // or wherever

Get the Locale by: Locale locale = Locale.forLanguageTag(langTag);

No additional dependency needed!

firoj_mujawar
  • 301
  • 4
  • 14
2

You can use this on Android. Works fine for me.

private static final Pattern localeMatcher = Pattern.compile
        ("^([^_]*)(_([^_]*)(_#(.*))?)?$");

public static Locale parseLocale(String value) {
    Matcher matcher = localeMatcher.matcher(value.replace('-', '_'));
    return matcher.find()
            ? TextUtils.isEmpty(matcher.group(5))
                ? TextUtils.isEmpty(matcher.group(3))
                    ? TextUtils.isEmpty(matcher.group(1))
                        ? null
                        : new Locale(matcher.group(1))
                    : new Locale(matcher.group(1), matcher.group(3))
                : new Locale(matcher.group(1), matcher.group(3),
                             matcher.group(5))
            : null;
}
Mygod
  • 2,077
  • 1
  • 19
  • 43
1

Because I have just implemented it:

In Groovy/Grails it would be:

def locale = Locale.getAvailableLocales().find { availableLocale ->
      return availableLocale.toString().equals(searchedLocale)
}
Martin L.
  • 3,006
  • 6
  • 36
  • 60
1

Well, I would store instead a string concatenation of Locale.getISO3Language(), getISO3Country() and getVariant() as key, which would allow me to latter call Locale(String language, String country, String variant) constructor.

indeed, relying of displayLanguage implies using the langage of locale to display it, which make it locale dependant, contrary to iso language code.

As an example, en locale key would be storable as

en_EN
en_US

and so on ...

Riduidel
  • 22,052
  • 14
  • 85
  • 185