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
Related
Whenever I want to print out date/time format to a human readable form, IDE will recommend me to use one of the following way
getDateInstance()
getDateTimeInstance()
getTimeInstance()
However, most of the time, applying different int style doesn't meet my requirement. End up, I need to define my own.
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal <SimpleDateFormat>() {
#Override protected SimpleDateFormat initialValue() {
// January 2
return new SimpleDateFormat("MMMM d");
}
};
This create a trouble for me, if I want to support non-English as well. For instance, for Chinese market, I need to use separate format.
private static final ThreadLocal<SimpleDateFormat> dateFormatForChineseThreadLocal = new ThreadLocal <SimpleDateFormat>() {
#Override protected SimpleDateFormat initialValue() {
// 1月2日
return new SimpleDateFormat("MMMMd日");
}
};
My code will end up with the following
public String dateString() {
if (chinese user) {
return dateFormatForChineseThreadLocal.get().format(calendar.getTime());
}
return dateFormatThreadLocal.get().format(calendar.getTime());
}
This make maintenance job difficult. I was wondering, is there a better way, to customize date/time display format for different localization?
When you localize your app you usually create strings.xml files for each language your app shall support in /src/main/res/, where values/ contains mostly english files and in values-de or values-cn german or chinese for instance. When you define a string resource with your format there, you can simply read that out and pass that format to your date formatter.
With this, you can simply add new languages without changing any line in your code.
This question already has answers here:
Acquiring a country's currency code
(4 answers)
Closed 6 years ago.
Suppose I have country name as USA .So I want to convert to USD.I have only country name from webservice.
I have tried this code but how to convert country name into Locale object. Can anybody suggest in this problem?
String countryName=jsonObject.getString("country");
Provide country code instead of country name
public static void main(String[] args) throws InterruptedException {
Map<String, String> countries = new HashMap<>();
for (String iso : Locale.getISOCountries()) {
Locale l = new Locale("", iso);
countries.put(l.getDisplayCountry(), iso);
}
System.out.println(countries.get("Switzerland"));
System.out.println(countries.get("Andorra"));
System.out.println(countries.get("Japan"));
}
You can also use https://github.com/TakahikoKawasaki/nv-i18n library this is specifically designed for this purpose.
From its GitHub page,
Package to support internationalization, containing ISO 3166-1 country
code enum, ISO 639-1 language code enum, etc.
Example specific to this question
String countryName = jsonObject.getString("country");
List<CurrencyCode> codes = CurrencyCode.getByCountry(countryName, false)
Simple example to see available list (from Github page),
// List all the currency codes.
for (CurrencyCode code : CurrencyCode.values())
{
System.out.format("[%s] %03d %s\n", code, code.getNumeric(), code.getName());
}
// List all the country codes.
for (CountryCode code : CountryCode.values())
{
System.out.format("[%s] %s\n", code, code.getName());
}
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.
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)
}
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'.