I override the Locale of my Android App at startup and allow to change the region at runtime.
I'm settings the Locale like this:
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
DisplayMetrics metrics = res.getDisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale);
}else{
configuration.locale = locale;
}
Locale.setDefault(locale);
res.updateConfiguration(configuration, metrics);
Now i want to use Firebase-Remote-Config with a Region/Country Condition.
e.g.
test_property -> fr_FR = Bonjour
-> de_DE = Hallo
-> default = Hi
If i now have my Device set to de_DE and my app set to fr_FR i will get the test_property from the de_DE region (Hallo).
What i want to get is the test_property for the region fr_FR set by the app.
I tried to FirebaseApp.initializeApp(...) after changing the region in the app, but Firebase seems to get the region from somewhere else.
Does someone know a way to set/override the region programmatically so firebase will use that region for the region condition?
I don't want to use Firebase Analytics User Properties, as they get updated way too late.
In your Firebase Console:
a) Set a user property for locale. Go to Analytics menu and select USER PROPERTIES tab.
Provide user property name. Like user_locale.
Add description, save, and publish changes.
b) Set a condition for remote configs. Go to Remote Config menu and select CONDITIONS tab.
Select User property - user_locale.
Select FOR TEXT - exactly matches.
Put locale you need. Like de for German.
Put name like translation_de for condition and select a color.
Save and publish changes.
c) For every remote config parameter add value for condition.
From drop down menu select appropriate condition like translation_de.
Update and publish changes.
On locale change in your code set User Property
FirebaseAnalytics.getInstance(getContext())
.setUserProperty("user_locale", "de");
FirebaseRemoteConfig.getInstance()
.fetch(1)
.addOnCompleteListener(activity, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
FirebaseRemoteConfig.getInstance().activateFetched();
}
}
});
Related
I'd like to localize or event better change the prompt text of User Consent API.
Default behavior:
So instead of Allow example to read the message below and enter the code I want it to be translated in other languages, based on my app's locale not based on device's locale.
This view's translation is handled by the library gms:play-services-auth-api-phone.
Didn 't find any information in their documentation explaining how to customize the message displayed in the bottomSheetView.
About the language of the message, it should update automatically depending on the phone's language.
Since you're receiving an intent from another app, chances are that you won't be able to 'force' a locale sadly.
The only thing you could try is to "force" the Locale on the Android part in your FlutterActivity but note that it could have unpredicted consequences over your app, and I really doubt it'll work.
private void setLocale(Locale locale){
SharedPrefUtils.saveLocale(locale); // optional - Helper method to save the selected language to SharedPreferences in case you might need to attach to activity context (you will need to code this)
Resources resources = getResources();
Configuration configuration = resources.getConfiguration();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
configuration.setLocale(locale);
} else{
configuration.locale=locale;
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N){
getApplicationContext().createConfigurationContext(configuration);
} else {
resources.updateConfiguration(configuration,displayMetrics);
}
}
Code retrieved from this StackOverflow post, credit to the author.
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.
...and if so, how?
We make a dedicated Android device for use in an industrial environment. It's basically a tablet, but with only one app running. The user is not expected to access any other features of the device and even the system settings, like WiFi and Time settings are performed through our app instead of through the Android Settings widget. So basically every button and message they see uses our strings.xml file.
Currently all of our customers are satisfied to use the default US-English settings but we will soon have some customers who want local languages and have supplied us with translation files. Currently one of them is Romanian, which is not a language with any native support on this device (a Samsung Galaxy tab 4); another is Czech.
So we want to add strings.xml files in appropriate res folders, for the non-English languages and a dropdown in our app to select which language we're using. Programmatically we think we can use Locale to set which strings.xml file it uses, so for example, if Romanian has been selected from the dropdown we would use Locale to set the tablet into Romanian so all of our app's UI will use the Romanian strings.xml file.
Our settings, including the new dropdown, are inaccessible to customers - they're set at the customer site by a field-service engineer.
Questions:
Will this work? I.e., can we control which strings.xml file it uses via Locale, even if the device has no native support for that language?
Since Romanian is not a natively-supported language with this device we assume that system messages will still come up in English. Is this true? (it's not a problem if it does - system messages are rare with our app and the users of our products are trained to contact support if that happens. I just want to make sure that if we set the Locale to Romanian, or Czech or some other language without native support it won't crash the tablet if it does try to issue a system message).
Will this work? I.e., can we control which strings.xml file it uses via Locale, even if the device has no native support for that language?
Yes, you can, by updating Locale within Configuration (see an example below). If you try to use the locale for which there are no corresponding resources (either within your app or system), the default string resources (res/values/strings.xml) of your app will be utilized.
Since Romanian is not a natively-supported language with this device we assume that system messages will still come up in English. Is this true?
It is true, if English is the current system locale.
I just want to make sure that if we set the Locale to Romanian, or Czech or some other language without native support it won't crash the tablet if it does try to issue a system message.
Your app won't crash. Locale changes made within an app effect locale resources of the app, not system one's.
An example to answer "if so, how?" The method can be used to test locale changes while an Activity is running*.
public static void changeLocale(Context context, String locale) {
Resources res = context.getResources();
Configuration conf = res.getConfiguration();
conf.locale = new Locale(locale);
res.updateConfiguration(conf, res.getDisplayMetrics());
}
* You might want to call recreate() to see string resource changes "on the fly".
Yes you can definitely switch locale of single app. For that you have to extend every activity from a base class like following:
public abstract class MyLangActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
Locale locale = // get the locale to use...
Configuration conf = getResources().getConfiguration();
if (Build.VERSION.SDK_INT >= 17) {
conf.setLocale(locale);
} else {
conf.locale = locale;
}
DisplayMetrics metrics = getResources().getDisplayMetrics();
getResources().updateConfiguration(conf, metrics);
super.onCreate(savedInstanceState);
}
}
Now about the issue of device not supporting the language applied. The device font being used might not have support for characters in the custom language file. You might use your own fonts files to support that.
The best way to do this is to actually set the device locale. The code to do that is
Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");
Object am = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative);
Object config = am.getClass().getMethod("getConfiguration").invoke(am);
config.getClass().getDeclaredField("locale").set(config, item.getLocale());
config.getClass().getDeclaredField("userSetLocale").setBoolean(config, true);
am.getClass().getMethod("updateConfiguration", android.content.res.Configuration.class).invoke(am, config);
ActivityManagerNative.java
package android.app;
public abstract class ActivityManagerNative implements IActivityManager {
public static IActivityManager getDefault(){
return null;
}
}
IActivityManager
package android.app;
import android.content.res.Configuration;
import android.os.RemoteException;
public interface IActivityManager {
public abstract Configuration getConfiguration () throws RemoteException;
public abstract void updateConfiguration (Configuration configuration) throws RemoteException;
}
This way you'll set the device locale, and let everything change through the normal pathways. You'll need the android:name="android.permission.CHANGE_CONFIGURATION" permission in your manifest. This is a secure permission, but installing yourself as a system app shouldn't be a problem for you.
Override getResources in Application and BaseActivity
#Override
public Resources getResources() {
if(mRes == null)
mRes = new PowerfulResources(getAssets(),new DisplayMetrics(), null);
return mRes;
}
public static class PowerfulResources extends Resources{
/**
* Create a new Resources object on top of an existing set of assets in an
* AssetManager.
*
* #param assets Previously created AssetManager.
* #param metrics Current display metrics to consider when
* selecting/computing resource values.
* #param config Desired device configuration to consider when
*/
public PowerfulResources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
super(assets, metrics, config);
}
#NonNull
#Override
public String getString(int id) throws NotFoundException {
//do your stuff here
return super.getString(id);
}
}
also read this
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.
I have several language files in Android application: value/strings.xml, value-en/strings.xml, ...etc
It is possible to load the content of this files in some arrays or something. For example, I would like to load default text strings and english strings in 2 different arrays at run time.
Thanks
Alin
Create a method like this:
Resources getResourcesByLocale( Context context, String localeName ) {
Resource res = context.getResources();
Configuration conf = new Configuration(res.getConfiguration());
conf.locale = new Locale(localeName);
return new Resources(res.getAssets(), res.getDisplayMetrics(), conf);
}
Than you can get resources for any locale you've defined, for example:
Resources res_en = getResourcesByLocale(context, "en");
Resources res_de = getResourcesByLocale(context, "de");
Resources res_fr = getResourcesByLocale(context, "fr");
String some_name_en = res_en.getString(R.string.some_name);
String some_name_fr = res_fr.getString(R.string.some_name);
// etc...
Moreover, you do not need to take care about exceptions if you did not define string for some locale, because anyway default (from res/values/*) will be loaded instead.
Actually the situation is like that. Imagine I have this scenario: some chinese open the application. He has the mobile phone set with ch locale. The application default is xx as language, meaning I have 2 language files values/strings.xml (spanish for eg as default) and another language values-en/strings.xml for english. The default will make no sense for him, so english will be the most appropiate for his understanding, even if he does not understand it very good. So at the app start I open language settings (android language settings), where any selection will set the app in spanish unless he select english. I am forcing him to change the phone locale in english basically, just to use my app. Overall the concept of android is wrong, because i need to be able to see an application in any language I want without changing device language.
What I have done: - I created in values folder one more string_xx.xml file. Now, for a translation string name = "txtTranslation" I have in string_xx file "en_txtTranslation" key. R.java loads them all and in my app, based on a global var selectedLanguage = xx, I attach the write string using this code:
public String translate(String text)
{
String appLanguage = UtilsCentral.getUserLanguage(getApplicationContext());
if (appLanguage != "")
{
return getString(getResources().getIdentifier(appLanguage + "_" + text, "string", this.getPackageName()));
}
else
{
return getString(getResources().getIdentifier(text, "string", this.getPackageName()));
}
}
Indeed, at activity on create i need to set all views with .text = tarnslate("txtTranslation")
Note: UtilsCentral.getUserLanguage(getApplicationContext()) returns app language (user selection)
Conclusion, there is more unuseful work, but lets me do what i need, and what i believe is normal.