Recientemente context.getResources (). updateConfiguration () ha quedado obsoleto en la API de Android 25 y se recomienda utilizar el contexto. createConfigurationContext () en su lugar.
¿Alguien sabe cómo se puede usar createConfigurationContext para anular la configuración regional del sistema Android?
antes de que esto lo hiciera:
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
android
locale
right-to-left
Bassel Mourjan
fuente
fuente
Respuestas:
Inspirado por la caligrafía , terminé creando un contenedor de contexto. En mi caso, necesito sobrescribir el idioma del sistema para brindar a los usuarios de mi aplicación la opción de cambiar el idioma de la aplicación, pero esto se puede personalizar con cualquier lógica que necesite implementar.
import android.annotation.TargetApi; import android.content.Context; import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Build; import java.util.Locale; public class MyContextWrapper extends ContextWrapper { public MyContextWrapper(Context base) { super(base); } @SuppressWarnings("deprecation") public static ContextWrapper wrap(Context context, String language) { Configuration config = context.getResources().getConfiguration(); Locale sysLocale = null; if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) { sysLocale = getSystemLocale(config); } else { sysLocale = getSystemLocaleLegacy(config); } if (!language.equals("") && !sysLocale.getLanguage().equals(language)) { Locale locale = new Locale(language); Locale.setDefault(locale); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setSystemLocale(config, locale); } else { setSystemLocaleLegacy(config, locale); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { context = context.createConfigurationContext(config); } else { context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); } return new MyContextWrapper(context); } @SuppressWarnings("deprecation") public static Locale getSystemLocaleLegacy(Configuration config){ return config.locale; } @TargetApi(Build.VERSION_CODES.N) public static Locale getSystemLocale(Configuration config){ return config.getLocales().get(0); } @SuppressWarnings("deprecation") public static void setSystemLocaleLegacy(Configuration config, Locale locale){ config.locale = locale; } @TargetApi(Build.VERSION_CODES.N) public static void setSystemLocale(Configuration config, Locale locale){ config.setLocale(locale); } }
y para inyectar su contenedor, en cada actividad agregue el siguiente código:
@Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(MyContextWrapper.wrap(newBase,"fr")); }
ACTUALIZACIÓN 23/09/2020 En caso de anular el tema de la aplicación para aplicar el modo oscuro, por ejemplo, ContextThemeWrapper romperá la configuración de idioma, por lo tanto, agregue el siguiente código a su Actividad para restablecer la configuración regional deseada
@Override public void applyOverrideConfiguration(Configuration overrideConfiguration) { Locale locale = new Locale("fr"); overrideConfiguration.setLocale(locale); super.applyOverrideConfiguration(overrideConfiguration); }
ACTUALIZACIÓN 19/10/2018 A veces, después de un cambio de orientación, o una pausa / reanudación de la actividad, el objeto de configuración se restablece a la configuración predeterminada del sistema y, como resultado, veremos que la aplicación muestra el texto "en" en inglés a pesar de que envolvemos el contexto con la configuración regional "fr" en francés . Por lo tanto, y como buena práctica, nunca retenga el objeto Contexto / Actividad en una variable global en actividades o fragmentos.
además, cree y use lo siguiente en un MyBaseFragment o MyBaseActivity:
public Context getMyContext(){ return MyContextWrapper.wrap(getContext(),"fr"); }
Esta práctica le proporcionará una solución 100% libre de errores.
fuente
Probablemente así:
Configuration overrideConfiguration = getBaseContext().getResources().getConfiguration(); overrideConfiguration.setLocales(LocaleList); Context context = createConfigurationContext(overrideConfiguration); Resources resources = context.getResources();
Bonificación: un artículo de blog que usa createConfigurationContext ()
fuente
LocaleList
Inspirado por Calligraphy, Mourjan y yo mismo, creé esto.
primero debe crear una subclase de Aplicación:
public class MyApplication extends Application { private Locale locale = null; @Override public void onCreate() { super.onCreate(); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); Configuration config = getBaseContext().getResources().getConfiguration(); String lang = preferences.getString(getString(R.string.pref_locale), "en"); String systemLocale = getSystemLocale(config).getLanguage(); if (!"".equals(lang) && !systemLocale.equals(lang)) { locale = new Locale(lang); Locale.setDefault(locale); setSystemLocale(config, locale); updateConfiguration(config); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (locale != null) { setSystemLocale(newConfig, locale); Locale.setDefault(locale); updateConfiguration(newConfig); } } @SuppressWarnings("deprecation") private static Locale getSystemLocale(Configuration config) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return config.getLocales().get(0); } else { return config.locale; } } @SuppressWarnings("deprecation") private static void setSystemLocale(Configuration config, Locale locale) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocale(locale); } else { config.locale = locale; } } @SuppressWarnings("deprecation") private void updateConfiguration(Configuration config) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { getBaseContext().createConfigurationContext(config); } else { getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } } }
entonces necesitas configurar esto en tu etiqueta de aplicación AndroidManifest.xml:
<application ... android:name="path.to.your.package.MyApplication" >
y agregue esto a su etiqueta de actividad AndroidManifest.xml.
<activity ... android:configChanges="locale" >
tenga en cuenta que pref_locale es un recurso de cadena como este:
<string name="pref_locale">fa</string>
y el código "en" es el idioma predeterminado si pref_locale no está configurado
fuente
Aquí no hay una solución que funcione al 100%. Necesita usar ambos
createConfigurationContext
yapplyOverrideConfiguration
. De lo contrario, incluso si se reemplazabaseContext
en todas las actividades con la nueva configuración, la actividad seguiría utilizarResources
desdeContextThemeWrapper
la antigua configuración regional.Así que aquí está la solución mía que funciona hasta API 29:
Subclasifica tu
MainApplication
clase de:abstract class LocalApplication : Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext( base.toLangIfDiff( PreferenceManager .getDefaultSharedPreferences(base) .getString("langPref", "sys")!! ) ) } }
También cada
Activity
desde:abstract class LocalActivity : AppCompatActivity() { override fun attachBaseContext(newBase: Context) { super.attachBaseContext( PreferenceManager .getDefaultSharedPreferences(base) .getString("langPref", "sys")!! ) } override fun applyOverrideConfiguration(overrideConfiguration: Configuration) { super.applyOverrideConfiguration(baseContext.resources.configuration) } }
Agregar
LocaleExt.kt
con las siguientes funciones de extensión:const val SYSTEM_LANG = "sys" const val ZH_LANG = "zh" const val SIMPLIFIED_CHINESE_SUFFIX = "rCN" private fun Context.isAppLangDiff(prefLang: String): Boolean { val appConfig: Configuration = this.resources.configuration val sysConfig: Configuration = Resources.getSystem().configuration val appLang: String = appConfig.localeCompat.language val sysLang: String = sysConfig.localeCompat.language return if (SYSTEM_LANG == prefLang) { appLang != sysLang } else { appLang != prefLang || ZH_LANG == prefLang } } fun Context.toLangIfDiff(lang: String): Context = if (this.isAppLangDiff(lang)) { this.toLang(lang) } else { this } @Suppress("DEPRECATION") fun Context.toLang(toLang: String): Context { val config = Configuration() val toLocale = langToLocale(toLang) Locale.setDefault(toLocale) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocale(toLocale) val localeList = LocaleList(toLocale) LocaleList.setDefault(localeList) config.setLocales(localeList) } else { config.locale = toLocale } return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { config.setLayoutDirection(toLocale) this.createConfigurationContext(config) } else { this.resources.updateConfiguration(config, this.resources.displayMetrics) this } } /** * @param toLang - two character representation of language, could be "sys" - which represents system's locale */ fun langToLocale(toLang: String): Locale = when { toLang == SYSTEM_LANG -> Resources.getSystem().configuration.localeCompat toLang.contains(ZH_LANG) -> when { toLang.contains(SIMPLIFIED_CHINESE_SUFFIX) -> Locale.SIMPLIFIED_CHINESE Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> Locale(ZH_LANG, "Hant") else -> Locale.TRADITIONAL_CHINESE } else -> Locale(toLang) } @Suppress("DEPRECATION") private val Configuration.localeCompat: Locale get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { this.locales.get(0) } else { this.locale }
Agregue a sus
res/values/arrays.xml
idiomas compatibles en matriz:<string-array name="lang_values" translatable="false"> <item>sys</item> <!-- System default --> <item>ar</item> <item>de</item> <item>en</item> <item>es</item> <item>fa</item> ... <item>zh</item> <!-- Traditional Chinese --> <item>zh-rCN</item> <!-- Simplified Chinese --> </string-array>
Quiero mencionar:
config.setLayoutDirection(toLocale);
para cambiar la dirección del diseño cuando use configuraciones regionales RTL como árabe, persa, etc."sys"
en el código hay un valor que significa "heredar el idioma predeterminado del sistema".ContextWraper
lo publicado aquí, solo configure el nuevo contexto devueltocreateConfigurationContext
como baseContextcreateConfigurationContext
, debe pasar la configuración creada desde cero y solo con elLocale
conjunto de propiedades. No debería haber ninguna otra propiedad establecida en esta configuración. Porque si establecemos otras propiedades para esta configuración ( orientación, por ejemplo), anulamos esa propiedad para siempre, y nuestro contexto ya no cambia esta propiedad de orientación incluso si giramos la pantalla.recreate
actividad cuando el usuario selecciona un idioma diferente, porque applicationContext permanecerá con la configuración regional anterior y podría proporcionar un comportamiento inesperado. Así que escuche el cambio de preferencia y reinicie toda la tarea de la aplicación en su lugar:fun Context.recreateTask() { this.packageManager .getLaunchIntentForPackage(context.packageName) ?.let { intent -> val restartIntent = Intent.makeRestartActivityTask(intent.component) this.startActivity(restartIntent) Runtime.getRuntime().exit(0) } }
fuente
recreateTask(Context context)
método no funciona correctamente ya que todavía veo el diseño sin ningún cambio.Aquí está la solución de @ bassel-mourjan con un poco de bondad kotlin :):
import android.annotation.TargetApi import android.content.ContextWrapper import android.os.Build import java.util.* @Suppress("DEPRECATION") fun ContextWrapper.wrap(language: String): ContextWrapper { val config = baseContext.resources.configuration val sysLocale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { this.getSystemLocale() } else { this.getSystemLocaleLegacy() } if (!language.isEmpty() && sysLocale.language != language) { val locale = Locale(language) Locale.setDefault(locale) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { this.setSystemLocale(locale) } else { this.setSystemLocaleLegacy(locale) } } return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { val context = baseContext.createConfigurationContext(config) ContextWrapper(context) } else { baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics) ContextWrapper(baseContext) } } @Suppress("DEPRECATION") fun ContextWrapper.getSystemLocaleLegacy(): Locale { val config = baseContext.resources.configuration return config.locale } @TargetApi(Build.VERSION_CODES.N) fun ContextWrapper.getSystemLocale(): Locale { val config = baseContext.resources.configuration return config.locales[0] } @Suppress("DEPRECATION") fun ContextWrapper.setSystemLocaleLegacy(locale: Locale) { val config = baseContext.resources.configuration config.locale = locale } @TargetApi(Build.VERSION_CODES.N) fun ContextWrapper.setSystemLocale(locale: Locale) { val config = baseContext.resources.configuration config.setLocale(locale) }
Y así es como se usa:
override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(ContextWrapper(newBase).wrap(defaultLocale.language)) }
fuente
val config = baseContext.resources.configuration
está muy, muy mal. Terminará con muchos errores debido a esto. En su lugar, necesita crear una nueva configuración. Mira mi respuesta.hay una solución simple con contextWrapper aquí: Android N cambia el idioma programáticamente Presta atención al método recreate ()
fuente
Prueba esto:
Configuration config = getBaseContext().getResources().getConfiguration(); config.setLocale(locale); context.createConfigurationContext(config);
fuente