zeugma-solutions / locale-helper-android Goto Github PK
View Code? Open in Web Editor NEWChange Language Programmatically in Android
Home Page: https://gunhansancar.com/change-language-programmatically-in-android/
License: Apache License 2.0
Change Language Programmatically in Android
Home Page: https://gunhansancar.com/change-language-programmatically-in-android/
License: Apache License 2.0
Hello, me again :)
I've noticed that by using the library, when the app is already launched and the the dark theme is switched on or off, the app keep his current theme until the app killed and launched again. Do you know a way to "refresh" the UI when the setting is changed?
In the meantime I'd like to thank you a lot for your awesome library that helps a lot !
Failed to change language for version 1.2.0-rc02 (and rc01) of androidx.appcompat:appcompat.
It is important because androidx.appcompat:appcompat:1.1.0 have some major issues (like highcharts/highcharts-android#115 ) with old Android versions.
I'm using Kotlin and working on how it works: Here is my source code for somebody you are interested:
package cdn.kotlinmode
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import org.jetbrains.anko.toast
import java.util.*
import androidx.core.app.ComponentActivity.ExtraData
import androidx.core.content.ContextCompat.getSystemService
import android.icu.lang.UCharacter.GraphemeClusterBreak.T
import com.zeugmasolutions.localehelper.LocaleHelper
import com.zeugmasolutions.localehelper.LocaleHelperApplicationDelegate
import com.zeugmasolutions.localehelper.Locales
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : BaseActivity(), AdapterView.OnItemSelectedListener {
private val localeAppDelegate = LocaleHelperApplicationDelegate()
var currentLanguage: String = "en"
lateinit var myLocale: Locale
var currentLang: String = ""
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val string: String = parent?.getItemAtPosition(position).toString()
if (string.equals("English")) {
toast("English")
LocaleHelper.setLocale(this, Locale("en", "IN"))
recreate()
} else
if (string.equals("Tamil")) {
LocaleHelper.setLocale(this, Locale("ta", "IN"))
toast("Tamil")
recreate()
} else if (string.equals("Malayalam")) {
LocaleHelper.setLocale(this, Locale("ml", "IN"))
toast("Malayalam")
recreate()
} else if (string.equals("Telugu")) {
LocaleHelper.setLocale(this, Locale("te", "IN"))
toast("Telugu")
recreate()
} else if (string.equals("Hindi")) {
LocaleHelper.setLocale(this, Locale("hi", "IN"))
toast("Hindi")
recreate()
} else if (string.equals("Marathi")) {
LocaleHelper.setLocale(this, Locale("mr", "IN"))
toast("Marathi")
recreate()
} else {
toast("Unknown")
}
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(localeAppDelegate.attachBaseContext(base))
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
localeAppDelegate.onConfigurationChanged(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setTitle(R.string.activity_title)
buttonHI.setOnClickListener {
updateLocale(Locales.Hindi)
}
buttonEN.setOnClickListener {
updateLocale(Locale.ENGLISH)
}
buttonTA.setOnClickListener {
LocaleHelper.setLocale(this, Locale("ta", "IN"))
recreate()
}
secondButton.setOnClickListener { startActivity(Intent(this, SecondActivity::class.java)) }
val spinner: Spinner = findViewById(R.id.spinner)
spinner.setPrompt("Select language...");
ArrayAdapter.createFromResource(this, R.array.locale, android.R.layout.simple_spinner_item)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
}
spinner.onItemSelectedListener = this
}
override fun updateLocale(locale: Locale) {
super.updateLocale(locale)
setTitle(getString(R.string.activity_title))
}
}
This is my problem:
Since i'm using spinner to select language, everytime i apply recreate gets called and thus experiencing a recreate loop. How to fix it?
Hi I am using your library but have found this issue in my app when getting Activity results:
I have this sample app to reproduce:
https://github.com/akvo/locale-helper-test-result
Steps:
Device with Android 8.1.0, Motorola G5, Locale anything but French
First you can run the app, try adding photo, you will see image is set correctly after you take a picture
Then you can press the menu option, this will change device locale to French
Now try adding new photo, you will see that the result is ignored.
I am thinking of solving this by something like this in LocaleAwareActivity
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
receivingResult = true;
}
override fun onResume() {
super.onResume()
if (!receivingResult) {
localeDelegate.onResumed(this)
}
receivingResult = false
}
Not sure there may be side effects though
Used version 1.1.4 I have some text input layout with hints and when changing locale all screens got translated except these hints. and the hint appears in just one language for example in en even I changed the language from en to ar to en to ar over and over.
it translated when I left and come back to screen.
Calling updateLocale() from the mainActivity which is called its baseActivity, this doesn't seem to change the locale on Android 7 and 6.
Running this in loop shows no new locale was added or updated
`
Locale locale = this.getResources().getConfiguration().locale; //Always shows the same even after updating
String languageGW = //Language code derived from backend
String phoneLocale = locale.toLanguageTag();
if(languageGW != null && !phoneLocale.equals(languageGW)){
//Change language now, discrepancy found between backend and local phone language
updateLocale(Locale.forLanguageTag(languageGW));
}
`
getString(R.string.main_activity_title) - always returns valid string as expected
applicationContext.getString(R.string.main_activity_title) - returns only initial locale string
Could you provide any information on this issue?
I wondering how it will be possible to use this library in fragment
I'm calling updateLocale from locale aware child activity it shows black screen and it crash:
E/ActivityInjector: get life cycle exception java.lang.ClassCastException: android.os.BinderProxy cannot be cast to android.app.servertransaction.ClientTransaction at android.app.ActivityInjector.checkAccessControl(ActivityInjector.java:24) at android.app.Activity.onResume(Activity.java:2011) at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:310) at com.zeugmasolutions.localehelper.LocaleAwareCompatActivity.onResume(LocaleHelperActivities.kt:25) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1459) at android.app.Activity.performResume(Activity.java:8292) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4625) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4667) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ClientTransactionHandler.executeTransaction(ClientTransactionHandler.java:58) at android.app.ActivityThread.handleRelaunchActivityLocally(ActivityThread.java:5640) at android.app.ActivityThread.access$3500(ActivityThread.java:261) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2182) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:237) at android.app.ActivityThread.main(ActivityThread.java:8163) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:972)
In many parts of my application we use the application context to get string resources. So after changing the locale: the string resources obtained from the application context were still loaded from the previous locale. Those strings were only correctly updated after re-opening the app.
I even tried using a ContextWrapper as mentioned here but it didn't work.
Target SDK: 28.
My tests were done on a emulator with android API 24.
I couldn't implement a fix, so we ended up just using the activity context (which works fine) to get the string resources.
Hi,
I'm considering to use this library in my projects but I didn't find way how to switch back to original system settings? What value should be set to updateLocale
method if I don't want to override system settings anymore?
Rationale: users may try this feature but find that localization to any language from the list does not fit them. So they want to return back to original state before they tried the feature. But I don't know how to implement that.
Thanks for your help
Tom
Hello,
I have Arabic and English string resources. On app start, default language is English and accessing a string resource using application context returns English string. This is how accessing string resource using AC.
applicationContext.resources.getString(R.string.economic_department)
Now changing app language to Arabic using the following code and restart launcher activity again from fragment.
Settings Fragment
LocaleHelper.setLocale(mContext, Locale("ar"))
(activity as HomeActivity).restartHomeActivity()
HomeActivity
fun restartHomeActivity() {
finish()
val newIntent = intent
newIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(newIntent)
}
On restart, accessing the same resource again for Arabic language still returns the English versioned string resource. Please guide?
Hello,
I'd like to know if there is a way to make it working inside BroadcastReceiver. I display notifications from a custom BroadcastReceiver but the locale is wrong. The content are not translated following the user settings.
For now I'm using context.applicationContext
in the onReceive() function but it only works when the app has been killed after language selection.
Thanks
JCenter will be completely shut down February 1, 2022 ,please update to Maven Central
hi
it's a good example for a local language
but why use multiple/nested base activity
LocaleAwareCompatActivity > BaseActivity > MainActivity
I think it good approach that keeps one base activity but nested base activity cause memory /performance issue
please correct me if I'm wrong
thanks
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
localeDelegate.onCreate(this)
setTheme(R.style.AppTheme_Main)
setContentView(binding.root)
}
}
Setting the theme this way won't work. Theme needs to be set set before localeDelegate.onCreate(this)
.
There is something happening in background which messes with android's bottom navigation bar. As you can see the text goes to the top of the icon, something that hasn't happened before I started using this library.
I use targetSdkVersion 31 and minSdkVersion 23.
Looking forward to your answer.
Sincerely, Saman
when usings LocaleAwareApplication() instead of Application() ,
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM don't work correctly .
In Poco phone, MIUI 11 and Android OS version 9 the change in language is not reflecting in other activities. Do we need to set the language in every activity ? We are extending a base activity where we did implement all the Localehelper codes.
Sadly it doesnt work on Android 10 Nokia 6.1.
First language change is succesfull, but if you exit the app an launch it again it doesnt stay in that language and after that you can change the language to another but it stays in the locale of your device.
Maybe this is problem with MultiDex?
This Library in Khmer (Cambodia) Language not Working
Hey, this is a really useful library, thanks for making it! I have a couple of feature requests:
A way to "un-set" a custom locale selection (to revert back to the system default) and query whether a custom value has been set or not. This could just query/clear the settings that it saved in SharedPreferences. Just setting it back to LocaleHelper.systemLocale doesn't quite work because it doesn't change if the user updates their locale at the system level. Plus within the app we can't tell if the user picked the system default or the language that is currently active (e.g. to set a locale spinner/radio group correctly).
A check in the updateLocale/setLocale calls to exit early (skipping the recreate call) if the locale selected is the same as the current one. This would get around the "blinking screen" issue (I hit this because I was calling updateLocale() in a spinner's onSelectedListener and it would call it multiple times for the same language selection).
"getApplicationContext" return ContextImpl instead Application.
Cause: context.createConfigurationContext(configuration) from LocaleHelper.
English is not my native language, so please be kind to my mistakes.
Hello,
I've got a requirement in my app (yes, I'm new to Android) to switch locales within the app and found your library. I've followed the setup instructions: added dependency to build.gradle, create app, and BaseActivity, added app name to android.manifest. But when I compile, I get the error:
"Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath: class com.zeugmasolutions.localehelper.LocaleAwareCompatActivity, unresolved supertypes: androidx.appcompat.app.AppCompatActivity"
Do you know what could be causing this? I'm using Android Studio 3.4.2, min sdk 22, and Kotlin.
Hello, I'm using your nice library on my application but some of my user are getting issue, I couldn't reproduce it :/
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.application.beta/com.my.application.MainActivity}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment f.a.a.a.k.e.a: could not find Fragment constructor
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Caused by androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment f.a.a.a.k.e.a: could not find Fragment constructor
at androidx.fragment.app.Fragment.instantiate(Fragment.java:628)
at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:483)
at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
at androidx.fragment.app.FragmentManager.restoreSaveState(FragmentManager.java:2728)
at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1890)
at androidx.fragment.app.Fragment.onCreate(Fragment.java:1867)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:264)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2949)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:3061)
at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:240)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:276)
at com.zeugmasolutions.localehelper.LocaleAwareCompatActivity.onCreate(LocaleAwareCompatActivity.java:21)
at com.my.application.MainActivity.onCreate(MainActivity.java:41)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Caused by java.lang.NoSuchMethodException: <init> []
at java.lang.Class.getConstructor0(Class.java:2320)
at java.lang.Class.getConstructor(Class.java:1725)
at androidx.fragment.app.Fragment.instantiate(Fragment.java:613)
at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:483)
at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
at androidx.fragment.app.FragmentManager.restoreSaveState(FragmentManager.java:2728)
at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1890)
at androidx.fragment.app.Fragment.onCreate(Fragment.java:1867)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:264)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2949)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:3061)
at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:240)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:276)
at com.zeugmasolutions.localehelper.LocaleAwareCompatActivity.onCreate(LocaleAwareCompatActivity.java:21)
at com.my.application.MainActivity.onCreate(MainActivity.java:41)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
I'm using class MainActivity : LocaleAwareCompatActivity()
and class MyApplication: LocaleAwareApplication()
at first I though it was because of my Fragment so I changed it to a 0-arg constructor but I still get the issue.
Android OS 11
Using (Option 2) Using delegates
After setup all the steps the exception is throw by the application (Crash log attached at the end).
This is happening after adding the below method to the base application class:
override fun getApplicationContext(): Context =
LocaleHelper.onAttach(super.getApplicationContext())
If this method is removed, app loads fine without any exception, but when the language from the Android Settings is changed, it doesn't reflect to the app. Have to close the app by swiping to get the changes.
Crash Log:
2021-08-03 16:54:43.585 25724-25724/com.universaltvplus.android E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.universaltvplus.android, PID: 25724
java.lang.RuntimeException: Unable to get provider androidx.lifecycle.ProcessLifecycleOwnerInitializer: java.lang.ClassCastException: android.app.ContextImpl cannot be cast to android.app.Application
at android.app.ActivityThread.installProvider(ActivityThread.java:8068)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:7599)
at android.app.ActivityThread.handleMakeApplication(ActivityThread.java:7483)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7440)
at android.app.ActivityThread.access$1400(ActivityThread.java:301)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2148)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8506)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)
Caused by: java.lang.ClassCastException: android.app.ContextImpl cannot be cast to android.app.Application
at androidx.lifecycle.LifecycleDispatcher.init(LifecycleDispatcher.java:43)
at androidx.lifecycle.ProcessLifecycleOwnerInitializer.onCreate(ProcessLifecycleOwnerInitializer.java:36)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2429)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2399)
at android.app.ActivityThread.installProvider(ActivityThread.java:8063)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:7599)
at android.app.ActivityThread.handleMakeApplication(ActivityThread.java:7483)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7440)
at android.app.ActivityThread.access$1400(ActivityThread.java:301)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2148)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8506)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)
hi, I'm pretty new with this locale thing
I already try a setup with option 1 and option 2, but still no luck
also already make a string dir
but when I execute updateLocale(Locales.Indonesia)
and check with currentLocale
it returns in_ID
, but string not loaded
Does any help, please?
sorry for the poor English
My app is using notifications fired from services so settings set to LocaleHelper
should be somehow propagated to Service
's too.
Any idea?
I've followed (Option 1) Using base classes
Now I see that my activities are created twice, because they are recreated in LocaleHelperDelegates.kt:41 - onResume().
So, I see a short blink each time I open another activity.
I don't understand if that's expected or I've missed something ?
For me, when I try to extend from "AppCompatDelegate" it says: Cannot access '': it is package-private in 'AppCompatDelegate'
is there diffrent way to set locale?
why is so simple task so hard to solve?
MapView crashes on initialization: "The provided context is not an application context".
If comment out the line of LocaleAwareApplication: getApplicationContext() - MapView work fine.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.