I have an app that can be used in either the default Locale (english) or French.
I am using Android's per-app's language feature to allow the user to change the language in the app itself.
Below in my code:
Settings.kt (This is how the language is applied in settings)
AppCompatDelegate.setApplicationLocales(
if(it.lowercase() == KEYWORD_LOCALE_DEFAULT){
Log.i(TAG, "Setting default locale")
LocaleListCompat.getEmptyLocaleList()
}else{
Log.i(TAG, "Setting locale: $it")
LocaleListCompat.forLanguageTags(it)
}
)
I am then adding a log in the MainActivity to check if it's working
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i(
TAG, "Starting app with local system ${
getSystemService(
LocaleManager::class.java
).applicationLocales
}"
)
Log.i(
TAG, "Starting app with app compat ${
AppCompatDelegate.getApplicationLocales()
}"
)
As expected, it is logging fine as below:
Starting app with local system []
Starting app with app compat []
// After changing settings
Starting app with local system [fr]
Starting app with app compat [fr]
This issue is the strings are not changing in the app.
I have defined 3 strings file as shown below but the strings are not working
strings.xml (fr)
<string name="headline_home_primary">Change to French Test</string>
<string name="headline_home_secondary">Change to French Test</string>
<string name="headline_home_tertiary">Change to French Test</string>
<string name="headline_home_quaternary">Change to French Test</string>
strings.xml
<string name="headline_home_primary">Change to Engl Test</string>
<string name="headline_home_secondary">Change to English Test</string>
<string name="headline_home_tertiary">Change to English Test</string>
<string name="headline_home_quaternary">Change to English Test</string>
I am using Jetpack Compose for my UI and the below are my settings
minSdk 29
targetSdk 33
Also i ran the app on the following devices:
Emulator
Pixel 5 (API 30)
Pixel 7 (API 33)
Physical
Pixel 7 (Android 13 - API 33)
Nothing works. Can someone please help out?
Related
I have a function to rewrite and pick app language based on system language while you install an app. This function is working as expected if you build your app from Android Studio (even release build). Also, I can confirm it is working from Play Store on all Androids except Android 10, 11 and 12.
It looks like I will correctly pick locale according to my logs and I will rewrite resource config, but after activity restart, it will jump to English as default no matter what system language is currently set (even if default locale in code is correct - in my case Czech (lang code "cs").
As I said, it is caused by the Google Play version of APK, not from Android Studio one.
Is there any undocumented change by Google Play Terms of Service, that they are blocking resource config to be read-only if uploaded on Play Store since Android 10?
Here is function:
fun applyLanguage() {
val defaultLocale = startupLocale
val langs = App.languages
val langCode = app.languageIndex.let {
if (it == 0) {
if(langs.any{ l -> l.first==defaultLocale.language }) {
defaultLocale.language
}else {
langs[App.LANGUAGE_INIT_DEFAULT].first
}
} else {
langs[it - 1].first
}
}
App.log("LangChange: MainActivity -> applyLanguage (langCodeSet) $langCode")
app.sysLog("LangChange: MainActivity -> applyLanguage (langCodeSet) $langCode")
if (resources.configuration.locale.language != langCode) {
val l = if (langCode == defaultLocale.language) {
defaultLocale
} else
Locale(langCode, "", "")
arrayOf(resources, app.resources).forEach { res ->
val cfg = res.configuration
cfg.locale = l
res.updateConfiguration(cfg, res.displayMetrics)
}
Locale.setDefault(l)
}
app.langCode = if (langs.any { it.first == langCode }) langCode else "cs"
App.log("LangChange: MainActivity -> applyLanguage (langCode) ${app.langCode}")
app.sysLog("LangChange: MainActivity -> applyLanguage (langCode) ${app.langCode}")
}
Its simple function, I have an array of available languages (in my case it's 3 of them based on available resources - translates) and if the default system language is one of them I will set the app in that language, if it's not I will set Czech as default. So if I pick English, I should have the app in English, if I pick German, I should have the app in German, if I pick Czech, I should have the app in Czech and if I pick any other language (for example French) it should be set to Czech as a fallback is there.
Also, the same function is used for language picker in App settings and it's the same issue. Default locale has langCode "cs" but if I pick any of those languages from the picker, it will always set resources to default state (string.xml file) which is of course English.
Another example, I setup default language as French in device settings. I downloaded an app from store and it correctly rewrites resources locale to Czech (language code "cs"). But app was still in English.
So, resources.configuration.locale.language was "cs" after activity restart, but this resource config was completely ignored by the system and system picked default resource xml - string.xml which is English.
So it looks like you cant rewrite the resources config anymore, or technically you can, but this altered resource config is completely ignored by the system.
UPDATE
Additional debugging.
Android 10: Default language (French): App was installed and default language was set to English(suppose to be Czech).
If you change language in settings, no matter what language you pick, it will always be set to English.
Android 11: Default language (French): App was installed and default language was set to Czech(correct).
If you change your language in settings it gets interesting:
If you change to English, app switches to English. If you change back to Czech, app switches to Czech. If you change to German, app switches to English (I dont know whats going on).
Android 12: Default language (French): App was installed and default language was set to English(suppose to be Czech).
If you change to English, app switches to English. If you change back to Czech, app switches to English. If you change to German, app switches to German.
Android 9, 8, 7, 6 (and probably lower) - working as intended.
I'm not sure whats going on but its kinda funny.
According to your description above, I got that your issue only happened in the Google Play app version.
I had faced a similar issue, all I did is adding this setting:
bundle {
language {
enableSplit = false
}
}
build.gradle(.app) within android tag
To expand Eng. OsamaYousef answer.
In Short you have to disallow bundle to remove "not needed" languages when user downloads your application.
bundle {
language {
enableSplit = false
}
}
Explanation:
The reason your language picker is working with android studio and not with play store version is because through android studio you install .apk and play store installs .aab (bundle). To test what user gets by downloading your app through play store simply go to android studio -> Run -> Edit configurations... -> Under installation Options search for Deploy and select APK from app bundle.
In addition, I would also recommend following Android guide to implement latest recommendations.
I've create a short test Application with the following pre conditions:
My Smartphone run on 7.0 (and up). I've setup multiple languages in my settings English (United States) & Deutsch (Deutschland).
This is my App setup:
compile-, target-, min-SdkVersion 24
String-Resources:
The content from strings.xml ("default") is:
<resources>
<string name="app_name">Test</string>
<string name="test">default</string>
</resources>
And the content from string.xml (values-en-rGB) is:
<resources>
<string name="test">GB Fallback!</string>
</resources>
The MainActivity have only a onCreate() with the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String string = getString(R.string.test);
Log.d("TestString", string);
final LocaleList locaList = LocaleList.getDefault();
for (int i = 0; i < locaList.size(); i++) {
Log.d("AvailableLocale", locaList.get(i).toLanguageTag());
}
}
When I run my App I expect the output from "TestString" is GB Fallback!. Why? Because Android 7.0 support multilingual. * (See update)
Instead of GB Fallback! I get always the default output. That means default.
The output from AvailableLocale is always up to date with the setup from my phone settings.
So. Why I got default from getString(R.string.test). Do I miss understand something with the multilingual stuff from the Android docs?
* Update
Why I claim I got GB Fallback!. The reason is the multilingual feature. The documentation shows a nice table:
I will try it to map it to my situation:
(Sorry, my Image is wrong. I don't mean de-GB. It's en-GB of course)
So, what is wrong here?
I'm builind an app to iOS and Android with AppCelerator.
So I have created Under i18n, two folder
i18n
--en
----string.xml
--it
----string.xml
In the string.xml I have insert some resources.
Now how can I change the language of application at runtime???
I'm try to do this but not works:
Ti.App.Properties.setString('SETTING_LANUAGE','it');
There are many ways to change the app language at runtime but it will only change your app's language, not the entire device language. So I hope this is what you want to achieve.
The simple solution I know is that:
e.g. Use L("appTitle") method to change the language.
Here is a short code snippet to use above method:
en -> strings.xml
<string name="en_home">Home</string>
<string name="en_cl">Change Language</string>
it -> strings.xml
<string name="it_home">Casa</string>
<string name="it_cl">Cambia lingua</string>
alloy.js
// you can set this to any language at default or you can save the last applied language in the app
Alloy.Globals.Lang = "en_";
index.js
var win = Ti.UI.createWindow({
backgroundColor : "#aaaaaa"
});
var lang = Alloy.Globals.Lang;
var label = Ti.UI.createLabel({
text : L(lang + "home")
});
var btn = Ti.UI.createButton({
title : L(lang + "cl"),
bottom : 50,
color : 'white',
backgroundColor : "#0aa"
});
btn.addEventListener('click', changeLanguageItalian);
win.add(label);
win.add(btn);
win.open();
function changeLanguageItalian() {
Alloy.Globals.Lang = "it_";
lang = Alloy.Globals.Lang;
btn.title = L(lang+"cl");
label.text = L(lang+"home");
}
Note that this is just a simple explanation of how you can change the language at run time. You can try anything you feel right according to your app. Good Luck!
Creating multi languages support for the app with possibility to change language in App settings.
As for now everything works fine for English, Spanish, Franch, Russian languages,
but doesn't work for Hindi and Chineese.
1) I specify the language name in original language
but in the app instead of हिन्दी I can see "Hindi".
\res\values-hi\arrays.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="languages">
<item name="english">English</item>
<item name="russian">Русский</item>
<item name="spanish">Espagnol</item>
<item name="russian">Français</item>
<item name="chineese">中国</item>
<item name="hindi">हिन्दी</item>
</string-array>
<string-array name="languagesValues">
<item name="english">en</item>
<item name="russian">ru</item>
<item name="spanish">es</item>
<item name="russian">fr</item>
<item name="chineese">zh-CN</item>
<item name="hindi">hi</item>
</string-array>
</resources>
2) End after selecting "Hindi" - actually default (English) is being selected.
\res\values-hi\strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">लकी बीनने</string>
<string name="score">0000</string>
<string name="settings">सेटिंग</string>
<string name="start_button">प्रारंभ</string>
<string name="about_button">के बारे में</string>
<string name="about">लियोनिद द्वारा बनाया गया</string>
<string name="feedback">प्रतिक्रिया भेजें </string>
<string name="high_score">उच्च स्कोर के</string>
<string name="score_set">स्कोर निर्धारित किया गया है \r\nमें:</string>
<string name="game_over">खेल खत्म</string>
....
Saving locale in Preferences.
public class Settings extends PreferenceActivity {
Locale myLocale;
static boolean localeChanged;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
// language changing
Preference langPreference = getPreferenceScreen().findPreference(
"language");
langPreference.setOnPreferenceChangeListener(languageChangeListener);
}
Preference.OnPreferenceChangeListener languageChangeListener = new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
switch (newValue.toString()) {
case "en":
setLocale("en");
break;
case "ru":
setLocale("ru");
break;
case "fr":
setLocale("fr");
break;
case "es":
setLocale("es");
break;
case "zh-CN":
setLocale("zh-CN");
break;
case "hi":
setLocale("hi");
break;
}
localeChanged = true;
return true;
}
};
// * manually changing locale/
public void setLocale(String lang) {
myLocale = new Locale(lang);
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = myLocale;
res.updateConfiguration(conf, dm);
Intent refresh = new Intent(this, Settings.class);
startActivity(refresh);
finish();
}
What is wrong? Is something wrong in my code, or in my device?
It depends on the device. Also, please check whether your device supports Hindi and Chinese languages by going to Settings --> Language & input.
I. First thing first:
Your post has lots of logical and spelling mistakes. Lint will report errors and warnings for such, and code will not compile.
Please see the corrections:
Creating multi languages support for the app with possibility to change language in App settings.
As for now everything works fine for English, Spanish, French, Russian languages,
but doesn't work for Hindi and Chinese.
1) I specify the language name in original language
but in the app instead of हिन्दी I can see "Hindi".
\res\values-hi\arrays.xml
<string-array name="languages">
<item name="english">English</item>
<item name="russian">Русский</item>
<item name="spanish">Espagnol</item>
<item name="french">Français</item>
<item name="chinese">中国</item>
<item name="hindi">हिन्दी</item>
</string-array>
<string-array name="languagesValues">
<item name="english">en</item>
<item name="russian">ru</item>
<item name="spanish">es</item>
<item name="french">fr</item>
<item name="chinese">zh-CN</item>
<item name="hindi">hi</item>
</string-array>
[COMMENTS]
Spelling mistakes: in the words chinese, french.
Logical mistake: repetition in the key russian, in both the arrays, where on the second repeat, it should be french.
II. Improvements for #Chandrakanth's answer:
It depends on the device, and your device seems to be old one, while your code is correct. Giving you the reasons why:
Beginning with my answer, I want to quote from your question:
As for now everything works fine for English, Spanish, French, Russian
languages, but doesn't work for Hindi and Chinese.
When you are using Chinese and Hindi, you are using following locales, as from your string resources:
<item name="chineese">zh-CN</item>
<item name="hindi">hi</item>
Please refactor it to:
<item name="chinese">zh</item>
<item name="hindi">hi</item>
So you should be using the values folders as:
values-zh and values-hi
If the above is correct then your application is not accepting or recognizing values-zh and values-hi in some old device.
Please share your device make, manufacturer and model, so that more
investigations can be done.
Your code is OK, hence you can test your application in any other
and newer device.
Please call the method where it changes the locale, just before
calling "setContentView" inside "onCreate" call back.
Maybe you can check whether your device supports Hindi and Chinese
language by going Settings --> Language & input.
I have devices that don't have Hindi and Chinese in the device
settings, but still the locales hi and zh are recognized within the
application, though I have to install corresponding fonts to prevent
the fallback.
Please note that Hindi and Chinese are locales from different time zones, so most likely, they can not be found in the same devices settings.
Please see that your code is working on other new devices. If not, then you have to follow steps below. There are some basic understandings first:
Firstly, if you change locale in your device settings, it should have no effect on your application level locale settings. That is, if your device locale is set to Spanish, and your Application level locale preference is set to Hindi, Hindi should continue to show on every fragment/activity of your application if each one of these components are have the strings in them localized. Now if you change your locale from Spanish to Chinese, and your application or device is restarted, since zh is stored in your preferences, your method changes the locale to Chinese, on Application load and it continues to show text in Chinese.
Secondly, if your language depends upon font for example: since you have English, Hindi and Chinese locale support in your application, you have to have all TTF fonts in your asset folder and apply the Font-face to TextView, EditText, Radio Buttons, Check Box, Spinners etc. appropriately, when you change the language from your application settings. Here Hindi, and Chinese locales need TTF fonts to be there for each, in assets folder and has to be used in every component's font-face as mentioned above.
Thirdly, and most importantly, the best place to change your locale is just before setContentView(layout_id) of an activity is called in onCreate(Bundle restoreState) method. Therefore as soon as locale is changed, you have to send the control back to your dashboard/home screen so as to immediately see the effect.
Please see Android get hosting Activity from a view
You can also see How to use font assets using XML resources, in Android
and Using styles for your components in Android
Happy Coding :-)
I have an iPhone/ Android app built using Appaccelerator. I have translated all text by placing it into locales (i18n//strings.xml) ,and I have also implemented a switch :
<label for="flip-1" class="username">Language:</label>
<select name="flip-1" id="flip-1" data-role="slider">
<option value="off">EN</option>
<option value="on">NL</option>
</select>
which works reasonably well on Android and iPhone.
How can I enable this select element to update active language locale within the app, so the language of the application would change, and is this possible without restarting the app?
There is no way to change current locale in runtime. Locale depends on current language of a cell phone, but you can still do some work around.
Create strings.xml file in your i18n directory and put all your texts there like this:
<resources>
<string name="en_car">Car</string>
<string name="de_car">Auto</string>
</resources>
The you can implement your own L() function:
Ti.App.defaultLang = "en"; // global variable with default language (set it in app.js)
function myL(str, lang) {
if(lang && str) {
return L(lang + '_' + str);
} else {
return L(Ti.App.defaultLang + '_' + str);
}
}
I haven't test it, just want to give you some example to deal with it. You can maybe pass also hint option like in Ti docs: http://docs.appcelerator.com/titanium/2.1/index.html#!/api/Titanium.Locale-method-getString