How to get Locale from its String representation in Java? - java

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.

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

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

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();
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());
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.

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

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".

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

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.

Old question with plenty of answers, but here's more solutions:
Get Locale From String
Converts a String to a Locale

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.

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!

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

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 ...

Because I have just implemented it:
In Groovy/Grails it would be:
def locale = Locale.getAvailableLocales().find { availableLocale ->
return availableLocale.toString().equals(searchedLocale)
}

Related

Strange behaviour of Locale Class or DateTimeFormatter(java8)

I am writing a Date-Time wrapper for my project using Java8 java.time package in which user will pass me a pattern,language and his timezone id (eg. Asia/Kolkatta, etc) on the basis of which i will output the LocalDateTime object(as String) as per his preferred timezone.So basically I have this method signature in mind
public static String getDateTime(String pattern, String language, String timezone){}
For getting timezone preffered time i have used ZonedDateTime which is working fine but I am facing a strange behaviour for getting language plus pattern specific format. I am using
public static DateTimeFormatter ofPattern(String pattern, Locale locale) I just tried with basic example to go ahead before writing any concrete structure but I am getting different results for same operation. Check below mentioned 2 programs-
public static void main(String[] args) {
String lang = "hi";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy,HH:mm", new Locale("hi","IN"));
System.out.println("Date:"+ LocalDateTime.now().format(dateTimeFormatter));
}
Output:
Date:16-अगस्त-2016,14:30
Now here I am getting the expected output for Locale in (Hindi,India) but now when I thought of optimising it by using the already available locale objects rather than using new Locale(String language, String country) I am not getting the result, I did something like this:
public class App2 {
// using map to act as a cache for the Key:Language and Value: Locale
private static Map<String,Locale> localeMap = new HashMap<String, Locale>();
public static void main(String[] args) {
String lang = "hi";
if (localeMap.get(lang) == null) {
for (Locale locale1 : Locale.getAvailableLocales()) {
if (locale1.getLanguage().equals(lang)) {
System.out.println("Lang:"+locale1.getLanguage()+",Country:"+locale1.getCountry());
localeMap.put(lang, locale1);
}
}
}
DateTimeFormatter.ofPattern("dd-MMM-yyyy,HH:mm", localeMap.get(lang));
System.out.println("Date:"+ LocalDateTime.now().format(dateTimeFormatter));
}
}
Output:
Lang:hi,Country:IN
Lang:hi,Country:
Date:16-Aug-2016,15:18
Note:
Locale corresponding to "hi" is already present in
for (Locale locale1 : Locale.getAvailableLocales()) {
if (locale1.getLanguage().equals("hi")) is not null
}
Questions:
Any reason why different output for above mentioned methods ? - Resolved [there were multiple entries for locales where lang is:"hi"]
I also wants to know why I am not getting numbers in that language. I am getting month name in hindi(for new Locale("hi","IN")) but not the numbers in date and time.(eg. I want numbers in ०,१,२,३,४,५,६,७,८,९,१० - this is hindi version of 0,1,2,3,4..10)
I also observed if I log locale1.getLanguage and locale1.getCountry(), 2 entries are there lang:hi and Country:IN and lang:hi and country:" "(empty string) and same for other locales too I observe this behaviour(language is there but no country). Any reasons ?
Edit1:
After putting sopln while insertion in map, I see there are two locales for which language is hindi if put an additional check in that for loop !locale1.getCountry().isEmpty() && locale1.getLanguage().equals(lang) ; I am getting the expected output. Now in Locale class there is constructor for Locale(String language) also. It also doesn't seem to work for "hi" but for "zh" - which is for CHINESE it works which gives multiple entries for zh and locale objects and for last insertion country has empty value which is similar to "hi" behaviour but yet I get an output in Chinese. Basically,this points to 3 in questions to ask.
Lang:zh,Country:TW
Lang:zh,Country:HK
Lang:zh,Country:SG
Lang:zh,Country:CN
Lang:zh,Country:
Date:16-八月-2016,17:14

How to implement internationalization in java

I have a class called Info, and i have a bunch of static String variables described in it.
public class Info{
public static stringOne= "Hello";
public static stringTwo = "world";
}
and i'm hoping to access these variables as Info.stringTwo from other classes.
1.) I need to know if this is java-Internationalization that i have applied here ? (I have all the messages that i will display in the application assigned in this class. And, i am hoping to have different languages support to the app as well)
Have a look at Resource bundle
A copy paste from the documentation:
When your program needs a locale-specific object, it loads the ResourceBundle class using the getBundle method:
ResourceBundle myResources =
ResourceBundle.getBundle("MyResources", currentLocale);
Resource bundles contain key/value pairs. The keys uniquely identify a locale-specific object in the bundle.
Here's an example of a ListResourceBundle that contains two key/value pairs:
public class MyResources extends ListResourceBundle {
protected Object[][] getContents() {
return new Object[][] {
// LOCALIZE THE SECOND STRING OF EACH ARRAY (e.g., "OK")
{"OkKey", "OK"},
{"CancelKey", "Cancel"},
// END OF MATERIAL TO LOCALIZE
};
}
}
Keys are always Strings. In this example, the keys are "OkKey" and "CancelKey". In the above example, the values are also Strings--"OK" and "Cancel"--but they don't have to be. The values can be any type of object.
You retrieve an object from resource bundle using the appropriate getter method. Because "OkKey" and "CancelKey" are both strings, you would use getString to retrieve them:
button1 = new Button(myResources.getString("OkKey"));
button2 = new Button(myResources.getString("CancelKey"));
Here is an example from here:-
import java.util.*;
public class InternationalizationDemo {
public static void main(String[] args) {
String language;
String country;
Locale locale;
ResourceBundle rb;
if (args.length != 2) {
language = new String("en");
country = new String("US");
}
else {
language = new String(args[0]);
country = new String(args[1]);
}
locale = new Locale(language, country);
rb = ResourceBundle.getBundle("MessagesBundle", locale);
System.out.println(rb.getString("localeInfo") + " ( " +
locale.getDisplayLanguage() + "," + locale.getDisplayCountry() + ").\n");
System.out.println(rb.getString("welcome"));
System.out.println(rb.getString("sayThanks"));
}
}
Though using a ResourceBundle is the traditional and most well-known approach to internationalization in Java, it is possible to make internationalization data available as class members, somewhat similar to the way you seek.
You can further put your strings for some message in different languages in a Map, indexed by language. And make this Map a static member of some class. Thus you get the ability to reference these string collections for messages by their class member names in a compiler-checked manner. And next, if you have a way to select preferred user language at run time (you have to have it), you just pick the right string from an appropriate collection using its language key, boiling down to something like this:
logger.info (MyClassWithMessages.MY_MULTILANGUAGE_MESSAGE.s ());
And the s() method to be added to your Map subclass can be made resposible for dealing with user preferences and selection from Map by language key.
That said, the remaining task is just to formulate a convenient API for all this... You are welcome to have a look at such an implementation on my blog page Look Ma, no ResourceBundle :) ..., and the next page that goes ahead with message formatting arguments.
For internationalization of Java and other applications I implemented a Message Compiler, which creates the resource bundle files and constant definitions as Java enum or static final strings for the keys from one single source file. So the constants can be used in the Java source code, which is a much safer way than using plain string constants. The message compiler cannot only be used for Java. It creates also resource files and constants for Objective-C or Swift and can be extended for other programming environments.

Howto convert the value of a hashmap to String i Java

I have a HashMap. I am trying to retrieve the value and print it using the key from the user-code.
The code is:
lib.addbook(book2.getISBN(), book2);
Book ret = lib.getbook("978-81-291-1979-7");
System.out.println(ret);
Current Output:
O/P: LibraryPackage.Book#527c6768
I want the output to be a string and to display the actual value not the address of the book.
You have to implement (and override) the toString() method in your Book class, and specify what you want the output to be. E.g.:
#Override
String toString()
{
return this.author+": " + this.title;
}
commons-lang has a great utility for this if you don't want to override the .toString() method, or need to represent it differently in different situations:
Here's a call to build a string based on reflection:
String str = ToStringBuilder.reflectionToString(object);
In fact, this is a great way to implement the .toString() method itself. Another alternative use of this class would be a field by field creation of the string:
String str = new ToStringBuilder(object)
.append("field1", field1)
.append("field2", field2)
.toString();

Custom conversion specifiers in java

I want my data structures to be custom formatted.
e.g. I have a DS
Address {
string house_number,
string street,
string city,
long pin_code,
}
Now, I want to associate certain conversion specifiers with each of these fields.
e.g. house_number -> H
street -> S,
city -> C,
pin_code -> P
...
So that something like
myPrintWriter.printf("Mr A lives in %C", address_instance)
yields "Mr A lives in boston" (if address_instance.city = boston) etc..
It seems there is no easy way to do this. java.util.Formatter seems to be final. The only customization it provides is via the interface Formattable, but that helps in customizing the 's' conversion specifier only.
Is there a way to add our custom conversion specifiers? Any help will be much appreciated.
Thanks,
It seems there is no easy way to do this. java.util.Formatter seems to be final.
That's true, but you can still use composition. I would do something like the following:
class ExtendedFormatter {
private Formatter defaultFormatter;
// provide the same methods like the normal Formatter and pipe them through
// ...
// then provide your custom method, or hijack one of the existing ones
// to extend it with the functionality you want
// ...
public Formatter format(String format, Object... args) {
// extract format specifiers from string
// loop through and get value from param array
ExtendedFormattable eft = (ExtendedFormattable)args1;
String specifierResult = eft.toFormat(formatSpecifier); // %C would return city
// use specifierResult for the just queried formatSpecifier in your result string
}
}
The hard part is to know how to attach the different format specifiers to the fields you want to output. The first way I can think of, is to provide your own ExtendedFormattable interface that each class that should be used with the ExtendedFormatter can implement, and return the according values for your custom format specifiers. That could be:
class Address implements ExtendedFormattable {
public String toFormat(String formatSpecifier) { // just an very simple signature example
// your custom return values here ...
}
}
There's also annotations, but I think that's not a very viable way.
A sample call would look like:
ExtendedFormatter ef = new ExtendedFormatter();
ef.format("Mr A lives in %C", address_instance);
I believe you will need to write your own formatter which works the way you want.

Is there an elegant way to convert ISO 639-2 (3 letter) language codes to Java Locales?

E.g. eng, spa, ita, ger
I could iterate all locales and compare the codes, but I wonder whether there is a more elegant & performant way to achieve this....
Thanks a lot for any hints :)
I don't know if there's an easy way to convert the 3-letter to the 2-letter versions, but in a worse case scenario, you could create a Map of them, like so:
String[] languages = Locale.getISOLanguages();
Map<String, Locale> localeMap = new HashMap<String, Locale>(languages.length);
for (String language : languages) {
Locale locale = new Locale(language);
localeMap.put(locale.getISO3Language(), locale);
}
Now you can look up locales using things like localeMap.get("eng");
Edit: Modified the way the map is created. Now there should be one object per language.
Edit 2: It's been a while, but changed the code to use the actual length of the languages array when initializing the Map.
You can use constructor Locale(String language), where language is the 2 letter ISO-639-1 code. I think the easiest way to convert ISO-639-2 to ISO-639-1 would be to create HashMap<String,String> constant.
Some modified code from my project, which has a similar requirement. We have our own historical timezone format so we can't use standard libraries.
public class MyProjectTimeZoneFactory {
private static Map timeZoneDb;
/**
* Set up our timezone id mappings; call this from any constructor
* or static method that needs it.
*/
private static void init() {
if(null == TimeZoneDb) {
timeZoneDb = new HashMap(); // Maybe a TreeMap would be more appropriate
timeZoneDb.put(" ","GMT+00");
timeZoneDb.put("EAD ","GMT+10");
timeZoneDb.put("JST ","GMT+9");
// etc.
}
}
public static TimeZone getTimeZone(String id)
throws CommandFormatException {
init();
TimeZone tz;
if(timeZoneDb.containsKey(id)) {
tz = TimeZone.getTimeZone((String)timeZoneDb.get(id));
} else {
throw new CommandFormatException("Invalid Timezone value");
}
return tz;
}
}
You could argue that it would be better to have the map in configuration rather than code - perhaps in a properties file. That may be true - but do remember the Pragmatic Programmers' rule 'Your not going to need it'.

Categories

Resources