I am writing an app that has an option menu, with three options for language : automatic, english , greek.
When the user changes the setting, I save it in shared preferences, and call my starting activity with the new task flag
when that activity starts (before even setContentView) I call the following method:
string language = MainApplication.Language; //this is from shared preferences
if (language == null || language.Equals("auto")) {
language = Java.Util.Locale.Default.Language; //if language is not set, or the setting is auto then load the default language
MainApplication.Language = "auto"; //save into the shared preferences that the language was set to auto
}
App.instance.set_language(language); //update the data layer's language
Java.Util.Locale locale = new Java.Util.Locale(language);
Java.Util.Locale.Default = locale; //change the default locale
Configuration config = new Configuration();//create a new configuration and set its locale
config.Locale = locale;
Application.Context.Resources.UpdateConfiguration(config, null);//update the system locale
The above is written in Xamarin, so it's C# but it's the same code for java
My problem is that if I change the language to greek for example, when the code above runs it sets the default locale to "el" , but if the user selects the automatic option after that , because the default locale for the application is "el" it preserves that setting, even though my system language is english.
when I restart the application it works correctly because the application's default locale is that of the system
so my question is, is there a way to get the system's locale and not the applications?
p.s. I know that I can always store the system's locale in Application and change it in OnConfigurationChanged ,I was just wandering if there is another way
You can get it by adb shell getprop | grep locale
It will print similar output like:
[persist.sys.locale]: [tr-TR]
[ro.product.locale]: [tr-TR]
So you can run a process as:
Process process = Runtime.getRunTime().exec("getprop");
and check the output string with a BufferedReader.
or basically check this answer.
Related
I've a problem with AAB when I need to change the app locale from within the app itself(i.e. have the language change setting inside the app), the issue is that the AAB gives me only my device languages resources, for example:
my device has English and French languages installed in it, so AAb gives me only the resources for English and French,
but from within the app itself there is a choice to switch the language between English, French, and Indonesian,
in that case, when changing the language to English or French everything is working perfectly, but when changing it to Indonesian, the app simply enters a crash loop as it keep looking for Indonesian language but it can't find.
The problem here is that even if I restarted the app, it enters the crash loop again as the app is still looking for the missing language resources, and here the only solution is to clear cash or reinstall which are the solutions that the normal user won't go through.
Just to mention it, this is how I change the locale through the app:
// get resources
Resources res = context.getResources();
// create the corresponding locale
Locale locale = new Locale(language); // for example "en"
// Change locale settings in the app.
android.content.res.Configuration conf = res.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
conf.setLocale(locale);
conf.setLayoutDirection(locale);
} else {
conf.locale = locale;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.getApplicationContext().createConfigurationContext(conf);
}
res.updateConfiguration(conf, null);
P.S. The app is working perfectly when build it as APK.
Edit:
The PlayCore API now supports downloading the strings for another language on-demand:
https://developer.android.com/guide/playcore/feature-delivery/on-demand#lang_resources
Alternative solution (discouraged):
You can disable the splitting by language by adding the following configuration in your build.gradle
android {
bundle {
language {
// Specifies that the app bundle should not support
// configuration APKs for language resources. These
// resources are instead packaged with each base and
// dynamic feature APK.
enableSplit = false
}
}
}
This latter solution will increase the size of the app.
This is not possible with app bundles: Google Play only downloads resources when the device's selected languages change.
You'll have to use APKs if you want to have an in app language picker.
Details of downloading the language on demand can be found here
https://android-developers.googleblog.com/2019/03/the-latest-android-app-bundle-updates.html
In your app’s build.gradle file:
dependencies {
// This dependency is downloaded from the Google’s Maven repository.
// So, make sure you also include that repository in your project's build.gradle file.
implementation 'com.google.android.play:core:1.10.0'
// For Kotlin users also add the Kotlin extensions library for Play Core:
implementation 'com.google.android.play:core-ktx:1.8.1'
...
}
Get a list of installed languages
val splitInstallManager = SplitInstallManagerFactory.create(context)
val langs: Set<String> = splitInstallManager.installedLanguages
Requesting additional languages
val installRequestBuilder = SplitInstallRequest.newBuilder()
installRequestBuilder.addLanguage(Locale.forLanguageTag("pl"))
splitInstallManager.startInstall(installRequestBuilder.build())
Check above link for full details
After many hours I was finally able to use the on-demand language with the new PlayCore API.
Step 1.) As the user changes the language, you need to first check whether the language is already available, if not then download the language
private void changeLocale(final String languageSelected){
SplitInstallManager splitInstallManager = SplitInstallManagerFactory.create(PlayAgainstComputer.this);
final Set<String> installedLangs = splitInstallManager.getInstalledLanguages();
if(installedLangs.contains(languageSelected)){ // checking if lang already available
Toast.makeText(PlayAgainstComputer.this,"Done! The language settings will take effect, once you restart the app!").show();
}
else{
SplitInstallRequest request =
SplitInstallRequest.newBuilder()
.addLanguage(Locale.forLanguageTag(languageSelected))
.build();
splitInstallManager.startInstall(request);
splitInstallManager.registerListener(new SplitInstallStateUpdatedListener() {
#Override
public void onStateUpdate(#NonNull SplitInstallSessionState splitInstallSessionState) {
if(splitInstallSessionState.status() == SplitInstallSessionStatus.INSTALLED){
Toast.makeText(PlayAgainstComputer.this,"Download complete! The language settings will take effect, once you restart the app!").show();
}
}
});
}}
Step2.) The downloaded languages must be installed when the user starts the app. which is done in the attchBaseContext() method
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
SplitCompat.install(this); // It will install all the downloaded langauges into the app
}
Step 3.) You need to tell the Activity to use the chosen language. Following code should be placed before setContentView(R.layout.layout); of that activity
String selectedLanguage = getFromPrefernceOrWhereEverYouSavedIt(); // should be 2 letters. like "de", "es"
Locale locale = new Locale(selectedLanguage);
Locale.setDefault(locale);
Resources resources = getResources();
Configuration config = new Configuration(resources.getConfiguration());
config.locale = locale;
resources.updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
Done!
Please Note
When a user (who chose a non-default/downloaded language) updates the app, that language needs to be downloaded again into the app, so make sure you handle that in your code.
when I used activity.recreate(); after the download finished (to automatically refresh the app for new language) I faced some problems, that is why I used Toast to ask the user to manually restart the app. but you can try other methods
I also noticed some other inconsistencies (even sometimes faced memory leak because of SplitCompat.install(this);) with this method, so make sure you test and optimize it according to your code.
In my application I'm using SpeechRecognizer, in order to detect what the user said.
My device's language is set to English and it works perfect when I say something in English, but when I say something in other languages, for example - Hebrew, it doesn't work all the time as it works for English, until I set the device's language to Hebrew and then it works OK.
I'm trying to avoid from setting the device's language and want it to automatically detect the user language.
I've noticed that "OK Google" works and detect the correct words in Hebrew even when the device's language is set to English.
For the meantime, what I tried to do is when the user enter for the first time
to my application, I'm asking him to enter his country.
Then when I have his country -> I'm getting the country code and then I create a Locale using the country code.
This locale is then sent as the language to the speech recognizer.
But it didn't help..
// example of how to get the locale using the country code
Locale myLocale = null;
String toSearch = "IL";
toSearch = toSearch.trim().toLowerCase();
for (Locale locale : Locale.getAvailableLocales())
{
if(locale.getCountry().trim().toLowerCase().contains(toSearch))
{
myLocale = locale;
break;
}
}
// example of how I'm sending the locale
Intent recIntent= new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recIntent.putExtra(RecognizerIntent.ExtraLanguage,myLocale);
I use this snippet to allow the user to set his favorite locale in the appplication:
Locale locale = new Locale(newLan);
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,getBaseContext().getResources().getDisplayMetrics());
Problem is that I would like to write a setting that would allow the user to get back to the default langage of the phone.
How is it possible?
Because after using the snippet above and imagine user chose French, I cannot get back the phone locale (which might be english for instance)
I just tried this, my phone locale is US, the toast is shown in french but in the log I still see US, maybe if you don't set the new locale as default it works anyway?
Locale locale = new Locale("fr");
//Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,getBaseContext().getResources().getDisplayMetrics());
Toast.makeText(this, android.R.string.cancel, Toast.LENGTH_LONG).show();
Log.d("LOCALE", Locale.getDefault().getCountry());
I've seen using Locale.setDefault() in other questions and answers, now I'm wondering, why would you be required to set the default Locale manually? If that was necessary, wouldn't it be done in updateConfiguration() anyway? this answer is also interesting
How about:
Saving current locale to shared preferences
Force to whatever you want
Use the shared preferences value to move back to original locale
Firstly this bit of code config.locale = locale; is deprecated, you should use config.setLocale(locale);
Have you tried getting the current device locale with Locale.getDefault().getDisplayLanguage();, and set it once the user chooses the default locale to be the selected language of your application with your code snippet?
In https://stackoverflow.com/a/34675427/519334 I solved the issue "go back to device-languge" by remembering the device-language in a static variable before any app-changes to locale have been taken place:
public class Global {
public static final Locale systemLocale = Locale.getDefault();
}
I am trying to change my device's language in my app. I have this code:
Locale locale = new Locale("en_US");
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getApplicationContext().getResources().updateConfiguration(config, null);
Log.i("some","Well, I tried!");
But this code does not change state of my device, and in LogCat I can see "Well, I tried" message. What are the possible reasons of such strange behaviour?
Edit this line
getApplicationContext().getResources().updateConfiguration(config, null);
like this:
context.getApplicationContext().getResources().updateConfiguration(config, null);
If you came here for language problems for builds after Summer 2021, it may have nothing to do with your code. We had the same issue and the problem was the new bundle (.aab) requirement (required since Summer 2021).
With app bundles, devices download only the code and resources they require to run your app. So, for language resources, a user’s device downloads only your app’s language resources that match the one or more languages currently selected in the device’s settings.
Read further
Basically, the language file is not downloaded if the device does not support that language. There are 2 ways to solve it:
Option 1 (Easiest): Just disable the bundle optimization
Add this in your build.gradle file:
android {
bundle {
language {
enableSplit = false
}
}
// ...
// Other configuration
}
Option 2: Download the language file on demand
Use the method described here.
How to debug
Before applying these methods,
Delete the app
Add the new language in the device settings
Download the app again
If the app supports the new language then the problem is definitely the bundle optimization and the above mentioned solutions will work.
To change the applications locale manually.
You will need to set the locale using the code below.
Locale locale = new Locale("AR"); // AR here is for arabic
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
Make sure it is set in the activity "onCreate" method before calling the "setContentView" method.
Also see that the resource files are specified for the language required.
I'm quite new to android dev, and I have a little problem of localization.
I have an app in which the user may choose to use an other default language than the current language of the device.
For example, a French user who live in Germany and who have his phone set to "German", could want to use my app in french (for various reasons), but not to set the default language of his phone in French.
My app have only ONE activity, into which I load and unload some views.
It's a very simple app, but with large contents (texts and images).
To accomplish this, I use the code below.
It works well, but there are some issues :
When the user chose a new language with the appropriate button, and relaunch the app (or if I force the app to finish()...) : ONLY THE FIRST VIEW uses the new language. The next views are still set to the previous language.
If the user chose to turn off his device, same issue : only the first view of the activity is set to the choosen language (the variable is read from the preferences.).
However, if the user chose to relaunch the app a second time, all views and subviews are correctly set to the new language.
And, also, if the user has previously relaunched the app at least one time, each language change is successfully accomplished. No need to relaunch the app a second time.
So, is there a way to correctly set the new language
immediately after the first relaunch of the app
if the user have previously turned of and on his device
Any help will be appreciated.
Here is the code I use (simplified):
public void onCreate(Bundle savedInstanceState) {
SharedPreferences myPrefs;
myPrefs = getSharedPreferences("langage", MODE_PRIVATE);
String langageToLoad = myPrefs.getString("langageToLoad", "");
changeLangage(langageToLoad);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
(...some other code...)
}
public void setLangageEn(View view){
changeLangage( "en");
setPreference( "en");
// finish(); // optionnal
}
public void changeLangage(String langage) {
Locale locale = new Locale(langage);
Locale systemLocale = Locale.getDefault();
if (systemLocale != null && systemLocale.equals(locale)) {
return;
}
Locale.setDefault(locale);
android.content.res.Configuration config = new android.content.res.Configuration();
config.locale = locale;
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
}
public void setPreference( String langage){
SharedPreferences languagepref = getSharedPreferences("langage",MODE_PRIVATE);
SharedPreferences.Editor editor = languagepref.edit();
editor.putString("langageToLoad",langage );
editor.commit();
}
Following thing: just because you finish() one activity, the previous activities don't need to be closed and therefor MIGHT call onResume() and just "jump over" onCreate(), they aren't out of the memory yet and don't call onCreate() (which comes before onResume()), where you implement your language check.
One thing to avoid this issue, might be to create a custom "Application class" (extends Application) and there, check the localize-preference and set it to a field with getter and setter methods. To get the application instance then (which is created on the application start!(not when just starting a second activity..) you can do ((CustomApp)Context.getApplicationContext).getCustomLocalization() in your activities..
However. To really load up the new language settings in every activity, be sure to check it in onResume() and set the language 1. to the field in CustomApp and 2. to the preference (maybe with the same setter method?) when changing it. Then finish() your settings activity and recreate it to load the new settings.