I need to change my Java locale to another language system-wide for work purposes, but there seems to be no easy way to do it by default. I do development for CJK applications, but changing my actual system locale to match also renames my home folders, meaning if my input method decides to stop working I would have to reboot my entire system.
I've tried setting JVM arguments (-Duser.language=ja -Duser.locale=JP) on
maven command line arguments (really not what I want but tried out of desperation)
{$project_dir}/.mvn/jvm.config (not ideal)
environment JAVA_ARGS (under /etc/environment)
maven JVM arguments in eclipse/intellij
MAVEN_OPTS
but none of them work. Pretty much at my wits end here.
To be clear I'm looking for a solution with the following criteria:
Not project specific
Doesn't require project source modification (e.g. project properties files) outside of .mvn
Doesn't involve changing my actual system locale (en_US)
Doesn't require constant maintenance (e.g. constantly add command line arguments)
So basically some kind of environment variable.
You can include these properties on your application.properties or application.yaml:
spring.mvc.locale=pt_BR
spring.mvc.locale-resolver=fixed
Changing the locale to the one you need.
These properties are used to defined the locale when the WebMvcAutoConfiguration is being configured, through the following method:
#Bean
#ConditionalOnMissingBean
#ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
You can check also the source code here
Your -Duser.locale=JP JVM argument should be -Duser.country=JP.
From Oracle technical resource on Locale:
... on some Java runtime implementations, the application user can override the host's default locale by providing this information on the command line by setting the user.language, user.country, and user.variant system properties.
(not a complete answer, but I don't have enough reputation to comment)
The JVM maintains its own current default locale. This behavior is required by the Java specifications.
Typically a JVM implementation detects the host OS default when the JVM starts up, and uses that as its own default. Later changing the host OS’ current default has no effect on the JVM. This behavior is not specified in the Java specs.
You can typically override that behavior by specifying a default locale on the command-line used to launch the JVM. You said you tried this, but did not explain why this is not a solution. This behavior is not specified in the Java specs.
Externally monitor & manipulate
You could externally monitor and manipulate the locale within Java by opening some communication path.
JMX springs to mind.
External look-up
When your app starts, it could look in an external source for an idea of what locale to use, then make that a singleton within your app.
An LDAP server is one such external place to hold such values. You would use JNDI in Java to access the server and retrieve the value.
Call Locale.setDefault
Calling Locale.setDefault immediately affects all code in all apps within the JVM. But this does not persist. You must call again when relaunching your app or JVM.
Any other code can call this as well as your code, making it unreliable. I do not recommend this approach.
You can hard-code the desired Locale by declaring a static class variable.
static final public Locale LOCALE = Locale.ITALY ;
In the lifecycle code called when your app launches, call Locale.setDefault( SomeClass.LOCALE ) ;.
Pass Locale object in your code
➥ I suggest always passing Locale explicitly as the optional argument in the various places you care about.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Locale locale = Locale.CANADA_FRENCH ; // Pass explicitly your desired/expected `Locale`.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL ).withLocale( locale ) ;
String output = today.format( f ) ;
mardi 11 juin 2019
Depending on the JVM’s current default locale is unreliable. As a programmer you are depending on externalities you cannot control.
The sysadmin or end-user may alter the default in deployment.
Any code in any thread of any app or library within the JVM calling Locale.setDefault immediately affects your code.
By the way, same goes for time zone, ZoneId. Better to always pass explicitly your desired/expected time zone.
Also, if the locale or time zone is crucial, best to confirm with the user.
You can try settig jvm parameters like (presudocode)
First way, you can do it like this-
java -Duser.country=CA -Duser.language=fr ........
Second way would be to call method
Locale.setDefault(Locale)
above method will set it jvm wise locale.
Third way, as per docs
Using an Explicit Locale
In some computing environments, applications use only a single locale
throughout their life cycle. In other environments, applications use a
global locale that can be changed. Those environments allow you to
programmatically change the global locale preference, which remains in
effect until you explicitly change it again. The Java application
environment is unique, providing you with the ability to use a variety
of locales throughout your application in any way you require.
Multinational companies have customers all around the globe. This
means that both their customers and employees may speak different
languages and have different expectations for how the company and its
software should behave. Moreover, it is entirely possible, even
common, to have a French employee handle a sales record for an Italian
customer. In those situations, you will need absolute control over
which locale your business and user interface objects use to
manipulate and represent data. Your application may need to print
sales receipts using Italian date and currency formats, yet sort
customer lists for an English sales employee. The combinations are far
too numerous to list, but Java technology provides you the flexibility
to handle that complexity.
In order to get the most flexibility, you must explicitly request
support for a target locale for each locale-sensitive class that you
use. That means you must track the locale preferences for multiple
aspects of the application or assign locale preferences to different
users and customers.
If you have tracked the user's locale preference, you would create
instances of locale-sensitive classes by explicitly providing a locale
in a constructor or creation method. Imagine that a preferences object
stores your customer's locale choice:
Locale userLocale = preferences.getLocale();
NumberFormat nf = NumberFormat.getInstance(userLocale);
So, in here, you can store your customer locale prefernce in this Preference object api, then do the required formatting operation etc.
What you can do is, set one global locale or leave one default locale.
And whenever required, set explicitly the required "locale" in the method.
Kindly go throw the following links as well-
https://www.top-password.com/blog/tag/change-system-locale-windows-command-line/
https://www.oracle.com/technetwork/articles/javase/locale-140624.html#using
The only way that I've found that works for me is setting LANG=ja_JP.UTF-8 as an actual system environment variable on my IDEs that kicks in after boot (both eclipse and intellij provide this under run configurations). But I really really dislike this approach.
Related
Is there any reason why certain display names are displayed in English despite the locales not being in English (ie: not translated).
For example:
Locale: "ru" is not translated
Locale locale = new Locale("ru");
Currency curr = Currency.getInstance("USD");
System.out.println(curr.getDisplayName(locale));
// US Dollar
Locale: "es" is translated
Locale locale = new Locale("es");
Currency curr = Currency.getInstance("USD");
System.out.println(curr.getDisplayName(locale));
// dólar estadounidense
Is this intentional?
Or has Java not gotten around to translating it?
Or am I doing something incorrectly?
I tried to locate the files where these translations are stored but couldn't find them. If someone could point me to that resource as well that would be helpful.
Thanks.
Locale Service Providers
Java uses an extensible mechanism to provide data (such as strings,
formatters, etc) that is localized.
Classes can implement LocaleServiceProvider to be a factory of
local-sensitive data. A lot of classes in java.util and java.text
rely on these providers to work properly with different Locale by
delegating the creating of objects to them.
You can find examples of Local Service Providers in the java.util.spi package, that are usually used to display text or numbers in a way that is dependent on the Locale. It includes CurrencyNameProvider which is used by Currency when calling Currency#getDisplayName.
Finding implementations
Classes wanting to use a specific LocaleServiceProvider (such as CurrencyNamePovider) use LocaleServiceProviderPool to find the instances of the provider that support a specific Locale.
LocaleServiceProviderPool tries first to use a default implementation included in the JRE. If it's not found, it relies on the simple Service Provider Interface (SPI) mechanism in Java and uses a ServiceLoader1 to try to find implementations provided by third party libraries.
This is what is written in the tutorial about Locale:
These methods first check whether the Java runtime environment supports the requested locale; if so, they use that support. Otherwise, the methods call the getAvailableLocales() methods of installed providers for the appropriate interface to find a provider that supports the requested locale.
The default implementations of the providers that come with the JRE can be found in the package sun.util.locale.provider. It's complex, but it essentially gets the data from the jar localedata.jar. In Oracle JDK, it is located in java_home/jre/lib/ext/localedata.jar. If you list the files that are in this jar, and check the files in sun.util.resources.es and in sun.util.resources.ru, you will see that there is many more currency names defined for Spanish than for Russian.
Here are the files for OpenJDK: Russian vs Spanish.
What if it's not defined at all
Locales are organized in a hierarchy. For example, a specific region of a country can have a Locale which reflects some local differences from the Locale of the country. If data is not found for a Locale, LocaleServiceProviderPool will try to use the Locale's parent.
The root of the tree of Locales is basically a "fallback" fictional Locale which provides default values for all localized data.
That's what probably happening when you ask for the display name of USD in russian.
Extensibility
Any program can provide additional Locale information. They need to define a service provider1 by creating the metadata file and implement a CurrencyNameProvider. You can fill in the missing localized data in your own jar.
Conclusion
Or has Java not gotten around to translating it?
That's pretty much the case.
Or am I doing something incorrectly?
No, you can either rely on the defaults or provide the localized data yourself.
1 The ServiceLoader will find them by asking the classloader to load the resource META-INF/services/java.text.spi.DateFormatProvider. If such file is found, it should have in it the specific class name of the implementation. It then tries to create an instance of it through the classloader.
Is there any difference in between the two, given that both java.time.Clock.systemDefaultZone().getZone() and java.util.TimeZone.getDefault().toZoneId() return the same output.
For instance this code
import java.time.Clock;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
System.out.println("Clock.systemDefaultZone().getZone() : "
+ Clock.systemDefaultZone().getZone());
System.out.println("TimeZone.getDefault().toZoneId() : "
+ TimeZone.getDefault().toZoneId());
}
}
returns this output
Clock.systemDefaultZone().getZone() : Europe/Paris
TimeZone.getDefault().toZoneId() : Europe/Paris
Both returns the JVM's default timezone (in the end, Clock calls TimeZone.getDefault(), as explained in #Kiskae's answer), but it's not guaranteed that all calls will always return the same value everytime.
That's because the default timezone can be changed:
the system where the JVM is running can change its configuration. In Windows machines, for example, this information is read from the registry, while in Linux it gets from /etc/localtime (usually a link to a specific file in /usr/share/zoneinfo) or another similar folder (it can vary in each version/distribution), or by setting the TZ environment variable. If this system configuration changes it and the JVM is restarted, suddenly your code starts returning different values
the JVM can be configured to use a different timezone, regardless of OS's config. One example is when the maintanance/infrastructure team changes this configuration (on purpose or by accident, and usually without telling the developers...) and then your code doesn't return the same values anymore (and everything that depends on the timezone will suddenly break)
your application (or another application running the same JVM) calls TimeZone.setDefault() method. This will affect all the applications running in the same JVM, at runtime, so if you run this code:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
System.out.println(ZoneId.systemDefault());
TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
System.out.println(ZoneId.systemDefault());
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println(ZoneId.systemDefault());
The output will be:
Europe/London
America/New_York
UTC
Note how easily the default timezone is changed at runtime, and all subsequent calls to get it are affected. The same will happen if you call Clock.systemDefaultZone().getZone() or TimeZone.getDefault().toZoneId(), because both uses the default timezone.
As this changes the JVM's default timezone, all applications running in the same JVM will be affected by it. This might lead to unexpected errors that are hard to debug.
Although the methods that use the default timezone are convenient, you must check how your code depends on it and how it can be affected if the zone changes.
If you don't want to depend on the default, the ideal is to use a specific timezone, such as ZoneId.of("Europe/Paris"). Always prefer IANA timezones names (always in the format Region/City, like America/New_York or Europe/Paris).
Avoid using the short abbreviations (like CET or CEST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
Looking at the source code on grepcode, they end up executing the exact same methods, resulting in the same result. Clock.systemDefaultZone() calls ZoneId.systemDefault(), which returns TimeZone.getDefault().toZoneId():
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/time/ZoneId.java#ZoneId.systemDefault%28%29
Java has two overloads each for String.toLowerCase and toUpperCase. One of the overloads takes a Locale as a parameter while the other one takes no parameters and uses the default locale (Locale.getDefault()).
The parameterless variants might not work as expected because case conversion respects internationalization, and the default locale is system dependent. Most notably, the lower case i is converted to an upper case dotted İ in the Turkish locale.
What is the purpose of these methods? Do the parameterless variants have any legitimate use? Or perhaps they were just a design mistake? (Not unlike several I/O APIs that use the system default character encoding by default.)
I think they're just convenience methods that will work most of the time, since apps that really need I18n are probably a small minority in the universe of java apps in the world.
If you hardcode a unix path for a File name in a java program and try to run in a windows box, you will also get wrong results and it's not java's fault.
I guess that's an implementation of write once run anywhere principle.
It makes sense, cause you can provide the default locale at JVM startup as one of the runtime parameters.
Furthermore, Java runtime has got a bunch of similar formatting methods for Dates and Numbers. (SimpleDateFormat, NumberFormat etc)
Several blog posts suggest that default locales and charsets indeed were a design mistake and have no meaningful use.
I have a situation in which a client for obscure reasons wants a specific locale to be in place, except for the modification that month names in lower case as per the locale should be shown in upper case (which is not a standard variant of the locale in question). I already have SimpleDateFormatter code in place referencing an instance of Locale.
My question is whether it is possibly to dynamically construct an instance of Locale based on a designated country code, but with specifically given modifications? Or, alternatively, whether it is possible to build a locale instance from scratch, specifying all details at runtime, such that a SimpleDateFormatter referencing it would change its casing of months accordingly?
Thanks in advance.
The Javadoc for LocaleServiceProvider should get you started.
I am looking for a way to add more Locales to the Locales available in Java 1.6. But the Locales I want to create do not have ISO-3166 country codes, nor ISO-639 language codes. Is there any way to do this anyways? The Locales I want to add only differ in the language names, but the smaller an ethnic group is, the more picky they get about their identity ;-)
So I thought about extending an existing Locale, something like
UserDefinedLocale extends Locale {
UserDefinedLocale (Locale parentLocale) {...}
}
but java.util.Locale is final, which makes it especially hard to hack something around...
So, is the idea that the list of Java Locales is exhaustive? Am I the first to miss some more Locales?
Read the javadoc for java.util Locale.
It says :
"Create a Locale object using the constructors in this class: "
It also says :
"Because a Locale object is just an identifier for a region, no validity check is performed when you construct a Locale"
It also says :
"A Locale is the mechanism for identifying the kind of object (NumberFormat) that you would like to get. The locale is just a mechanism for identifying objects, not a container for the objects themselves"
And finally, the javadoc for the getAvailableLocales() method says :
"The returned array represents the union of locales supported by the Java runtime environment and by installed LocaleServiceProvider implementations"
So you just have to invent a language code which is not in the standard list, and use it as an identifier for your locale.
See this answer:
...You can plug in support for additional locales via the SPIs (described here). For example, to provide a date formatter for a new locale, you would do it by implementing a DateFormatProvider service. You might be able to do this by decorating an existing implementation - I'd have a look at the ICU4J library to see if it provides the support you want.