изменения в локали работают при добавлении большого количества модулей
Локаль действительно работает, когда есть много модулей.
Контекст:
Мы используем Crowdin (эта библиотека применяет оболочку над контекстом)
Приложение отлично работает, когда есть только один модуль
Используйте Appcompat:1.2
Когда работает изменение языкового стандарта
но когда я добавляю новый модуль в
app
изменение языкового стандарта действительно работает.
implementation project(":newmodule")
Когда используется одиночный модуль:
BaseContext
знак равноCrowdinContextWrapper
Когда используется Multi Module:
BaseContext
знак равноContextThemeWrapper
Activity
расширенный BaseActivity
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Crowdin.forceUpdate(context = this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
open class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(Crowdin.wrapContext(Localization.wrap(context = newBase)))
}
}
class Localization(base: Context) : ContextThemeWrapper(base, R.style.AppTheme) {
companion object {
fun wrap(context: Context, language: String = "es", country: String = "MX"): ContextThemeWrapper {
var ctx = context
val config = context.resources.configuration
if (language != "") {
val locale = Locale(language, country)
Locale.setDefault(locale)
config.setLocale(locale)
// Used setLayoutDirection for RTL and LTR
config.setLayoutDirection(locale)
ctx = context.createConfigurationContext(config)
}
return Localization(ctx)
}
}
}
1 ответ
Объяснение
Проблема связана с
Appcompat
. Есть два разных исправления в зависимости от версии
AppCompat
. Поскольку вы используете
1.2.0
, вам нужно будет его реализовать.
AppCompatDelegateImpl
заканчивается удалением локали, потому что по сути ContextThemeWrapper обертывает ваш ContextWrapper. Смотрите реализацию @ AppCompatDelegateImpl.java в строке 368. Также строки 388 и 480.
try {
ContextThemeWrapperCompatApi17Impl.applyOverrideConfiguration(
(android.view.ContextThemeWrapper) baseContext, config);
return baseContext;
} catch (IllegalStateException e) {
if (DEBUG) {
Log.d(TAG, "Failed to apply configuration to base context", e);
}
}
Чтобы решить эту проблему, нужно отменить
getDelegate
внутри вашей базовой активности следующим образом:
private var baseContextWrappingDelegate: AppCompatDelegate? = null
override fun getDelegate() = baseContextWrappingDelegate ?: BaseContextWrappingDelegate(super.getDelegate()).apply {
baseContextWrappingDelegate = this
}
И вам также понадобится следующий класс (см. Изменение языкового стандарта не работает после перехода на Androidx).
package androidx.appcompat.app
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.util.AttributeSet
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
class BaseContextWrappingDelegate(private val superDelegate: AppCompatDelegate) : AppCompatDelegate() {
override fun getSupportActionBar() = superDelegate.supportActionBar
override fun setSupportActionBar(toolbar: Toolbar?) = superDelegate.setSupportActionBar(toolbar)
override fun getMenuInflater(): MenuInflater? = superDelegate.menuInflater
override fun onCreate(savedInstanceState: Bundle?) {
superDelegate.onCreate(savedInstanceState)
removeActivityDelegate(superDelegate)
addActiveDelegate(this)
}
override fun onPostCreate(savedInstanceState: Bundle?) = superDelegate.onPostCreate(savedInstanceState)
override fun onConfigurationChanged(newConfig: Configuration?) = superDelegate.onConfigurationChanged(newConfig)
override fun onStart() = superDelegate.onStart()
override fun onStop() = superDelegate.onStop()
override fun onPostResume() = superDelegate.onPostResume()
override fun setTheme(themeResId: Int) = superDelegate.setTheme(themeResId)
override fun <T : View?> findViewById(id: Int) = superDelegate.findViewById<T>(id)
override fun setContentView(v: View?) = superDelegate.setContentView(v)
override fun setContentView(resId: Int) = superDelegate.setContentView(resId)
override fun setContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.setContentView(v, lp)
override fun addContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.addContentView(v, lp)
override fun attachBaseContext2(context: Context) = wrap(superDelegate.attachBaseContext2(super.attachBaseContext2(context)))
override fun setTitle(title: CharSequence?) = superDelegate.setTitle(title)
override fun invalidateOptionsMenu() = superDelegate.invalidateOptionsMenu()
override fun onDestroy() {
superDelegate.onDestroy()
removeActivityDelegate(this)
}
override fun getDrawerToggleDelegate() = superDelegate.drawerToggleDelegate
override fun requestWindowFeature(featureId: Int) = superDelegate.requestWindowFeature(featureId)
override fun hasWindowFeature(featureId: Int) = superDelegate.hasWindowFeature(featureId)
override fun startSupportActionMode(callback: ActionMode.Callback) = superDelegate.startSupportActionMode(callback)
override fun installViewFactory() = superDelegate.installViewFactory()
override fun createView(parent: View?, name: String?, context: Context, attrs: AttributeSet): View? = superDelegate.createView(parent, name, context, attrs)
override fun setHandleNativeActionModesEnabled(enabled: Boolean) {
superDelegate.isHandleNativeActionModesEnabled = enabled
}
override fun isHandleNativeActionModesEnabled() = superDelegate.isHandleNativeActionModesEnabled
override fun onSaveInstanceState(outState: Bundle?) = superDelegate.onSaveInstanceState(outState)
override fun applyDayNight() = superDelegate.applyDayNight()
override fun setLocalNightMode(mode: Int) {
superDelegate.localNightMode = mode
}
override fun getLocalNightMode() = superDelegate.localNightMode
private fun wrap(context: Context): Context {
//Put wrapping implementation here
}
}