Jak uzyskać Locale z jego reprezentacji String w Javie?

109

Czy istnieje zgrabny sposób na pobranie instancji Locale z jej „nazwy programowej” zwróconej przez toString()metodę Locale ? Oczywistym i brzydkim rozwiązaniem byłoby przeanalizowanie ciągu znaków, a następnie skonstruowanie nowej instancji Locale zgodnie z tym, ale może jest na to lepszy sposób / gotowe rozwiązanie?

Potrzeba jest taka, że ​​chcę przechowywać pewne ustawienia regionalne w bazie danych SQL, w tym same ustawienia regionalne, ale byłoby brzydko umieszczać tam zserializowane obiekty Locale. Wolałbym raczej przechowywać ich reprezentacje String, które wydają się być dość szczegółowe w szczegółach.

Joonas Pulakka
źródło

Odpowiedzi:

34

Zobacz Locale.getLanguage(), Locale.getCountry()... Przechowuj tę kombinację w bazie danych zamiast "programatic name"...
Jeśli chcesz przywrócić ustawienia regionalne, użyjpublic Locale(String language, String country)

Oto przykładowy kod :)

// 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);
}
raj
źródło
3
To się nawet nie skompiluje.
Adrian
1
@raj po co używać tokenizera, skoro Java daje gotowe metody? np. toLocale (String str). Zobacz przykłady w odpowiedzi
VdeX,
9
Powinieneś używać Locale.forLanguageTag (String)
Rian,
126

Metoda zwracająca locale z stringa istnieje w bibliotece commons-lang: LocaleUtils.toLocale(localeAsString)

yurilo
źródło
2
LocaleUtils.toLocale nie obsługuje ciągów, takich jak „zh-Hans”, „pt-PT” itp.
Hans van Dodewaard
10
Jeśli masz łącznik -między częściami lokalnymi, masz do czynienia ze znacznikiem IETF BCP 47, jeśli używasz Java 7, możesz użyćLocale.forLanguageTag
Jaime Hablutzel
59

Od wersji Java 7 istnieje metoda fabryczna Locale.forLanguageTagi metoda instancji Locale.toLanguageTagwykorzystująca znaczniki języka IETF .

nilskp
źródło
8
Chcę tylko podkreślić, że Locale.forLanguageTagdziała z łańcuchami ustawień regionalnych IETF (tj. en-US) I nie działa z ciągami ustawień regionalnych ISO (tj. en_US)
Fabian
34
  1. Java zapewnia wiele rzeczy przy prawidłowej implementacji, dzięki czemu można uniknąć wielu złożoności. Zwraca ms_MY .

    String key = "ms-MY";
    Locale locale = new Locale.Builder().setLanguageTag(key).build();
  2. Apache Commons musi LocaleUtilspomóc przeanalizować reprezentację ciągu. To zwróci en_US

    String str = "en-US";
    Locale locale =  LocaleUtils.toLocale(str);
    System.out.println(locale.toString());
  3. Możesz także użyć konstruktorów ustawień regionalnych.

    // 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)

Sprawdź to LocaleUtils i to Locale, aby poznać więcej metod.

VdeX
źródło
1
LocaleUtils.toLocale (localeStringRepresentation) robi to starannie. Również jeśli widzisz implementację tej metody, jest ona dość wszechstronna!
Danie
15

Opcja 1 :

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

Opcja 2 :

Locale.forLanguageTag("en-US")

Pamiętaj, że opcja 1 to „podkreślenie” między językiem a krajem, a opcja 2 to „myślnik”.

junjun
źródło
Wywołanie wymaga interfejsu API na poziomie 21 (obecnie minimum to 17): java.util.Locale # forLanguageTag
Vlad,
12

Ta odpowiedź może być trochę spóźniona, ale okazuje się, że analiza ciągu nie jest tak brzydka, jak zakładał OP. Wydało mi się to dość proste i zwięzłe:

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]);
}

Przetestowałem to (na Javie 7) ze wszystkimi przykładami podanymi w dokumentacji Locale.toString (): "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "zh_CN_ # Hans", "zh_TW_ # Hant-x-java ”i„ th_TH_TH_ # u-nu-thai ”.

WAŻNA AKTUALIZACJA : Nie jest to zalecane do użytku w Javie 7+ zgodnie z dokumentacją :

W szczególności klienci, którzy analizują dane wyjściowe metody toString na pola języka, kraju i wariantu, mogą nadal to robić (chociaż jest to zdecydowanie odradzane ), chociaż pole wariantu będzie zawierało dodatkowe informacje, jeśli obecny jest skrypt lub rozszerzenia.

Zamiast tego użyj Locale.forLanguageTag i Locale.toLanguageTag lub, jeśli musisz, Locale.Builder.

andy
źródło
5
Java 7 Locale.forLanguageTagstosuje się tylko do języka znaczników kodowanych, jak wskazano w IETF BCP 47, z łącznikiem ( -), nie podkreślenia ( _), jak w zwrocie Locale„s toStringmetody
Jaime Hablutzel
1
Masz rację. Nadal musi być jakiś sposób na przekonwertowanie istniejących reprezentacji ustawień regionalnych do formatu BCP47. Chciałem zasugerować, że idąc dalej, Locales nie powinny być przechowywane w ich toStringformie, ale w toLanguageTagformie, którą można zamienić z powrotem na Localełatwiejszą i dokładniejszą.
andy
Czy ta metoda nie miałaby wielu przypadków skrajnych, które mogłyby spowodować przekroczenie granic indeksu?
user2524908
@ user2524908: Nie sądzę, ponieważ zawsze testuje długość tablicy przed uzyskaniem dostępu do jej elementów. Rozwiązanie może mieć wiele
skrajnych
9

Jeśli korzystasz z Spring Framework w swoim projekcie, możesz również użyć:

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

Dokumentacja :

Przeanalizuj podaną reprezentację String w locale

Javad Alimohammadi
źródło
Dokumenty na ten temat mówią, że jest to dokładnie odwrotność Locale#toString()- idealnie! :)
żartobliwy
3

Wydaje się, że nie ma na to statycznej valueOfmetody, co jest nieco zaskakujące.

Jednym raczej brzydkim, ale prostym sposobem byłoby powtórzenie Locale.getAvailableLocales()i porównanie ich toStringwartości z wartością.

Niezbyt ładne, ale nie jest wymagane analizowanie ciągu. Możesz wstępnie wypełnić Mapciągi znaków na lokalizacje i wyszukać łańcuch bazy danych w tej mapie.

skaffman
źródło
Ach, ta iteracja może być całkiem rozsądnym rozwiązaniem. Rzeczywiście jest zaskakujące, że Locale nie ma na to statycznej metody.
Joonas Pulakka
Predefiniowane Localeinstancje reprezentują tylko niewielki podzbiór prawidłowych ustawień regionalnych. To wcale nie jest kompletne.
BetaRide
3

Możesz tego użyć na Androidzie. U mnie działa dobrze.

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;
}
Mój Boże
źródło
1

Cóż, chciałbym przechowywać zamiast konkatenacji ciąg Locale.getISO3Language(), getISO3Country()i getVariant () jako klucz, który pozwoliłby mnie do ostatniego wywołania Locale(String language, String country, String variant)konstruktora.

w rzeczywistości poleganie na displayLanguage implikuje użycie języka locale do jego wyświetlenia, co czyni go zależnym od locale, w przeciwieństwie do kodu języka ISO.

Na przykład klucz en locale mógłby być przechowywany jako

en_EN
en_US

i tak dalej ...

Riduidel
źródło
1

Ponieważ właśnie to zaimplementowałem:

W Groovy/ Grailsto byłoby:

def locale = Locale.getAvailableLocales().find { availableLocale ->
      return availableLocale.toString().equals(searchedLocale)
}
Martin L.
źródło