studio programacion móviles language desarrollo curso change app aplicaciones java android android-n android-7.0-nougat

java - programacion - Android N cambia el idioma mediante programación



set language programmatically android (3)

Encontré un error realmente extraño que se reproduce solo en dispositivos Android N.

En el recorrido de mi aplicación, existe la posibilidad de cambiar el idioma. Aquí está el código que lo cambia.

public void update(Locale locale) { Locale.setDefault(locale); Configuration configuration = res.getConfiguration(); if (BuildUtils.isAtLeast24Api()) { LocaleList localeList = new LocaleList(locale); LocaleList.setDefault(localeList); configuration.setLocales(localeList); configuration.setLocale(locale); } else if (BuildUtils.isAtLeast17Api()){ configuration.setLocale(locale); } else { configuration.locale = locale; } res.updateConfiguration(configuration, res.getDisplayMetrics()); }

Este código funciona muy bien en la actividad de mi recorrido (con la llamada recreate() ) pero en todas las actividades siguientes todos los recursos de String están mal. La rotación de la pantalla lo arregla. ¿Qué puedo hacer con este problema? ¿Debo cambiar la configuración regional para Android N de manera diferente o es solo un error del sistema?

PD: Esto es lo que encontré. En el primer inicio de MainActivity (que es después de mi recorrido) Locale.getDefault() es correcto pero los recursos están mal. Pero en otras actividades me da una configuración regional incorrecta y recursos incorrectos de esta configuración regional. Después de la pantalla de rotación (o quizás algún otro cambio de configuración) Locale.getDefault() es correcto.


Inspirado por varios códigos (es decir, nuestro equipo de (gritar personas)), había producido una versión mucho más simple. La extensión ContextWrapper es innecesaria.

Primero, digamos que tiene 2 botones para 2 idiomas, EN y KH. En onClick para los botones, guarde el código de idioma en SharedPreferences , luego llame al método recreate() la actividad.

Ejemplo:

@Override public void onClick(View v) { switch(v.getId()) { case R.id.btn_lang_en: //save "en" to SharedPref here break; case R.id.btn_lang_kh: //save "kh" to SharedPref here break; default: break; } getActivity().recreate(); }

Luego cree un método estático que devuelva ContextWrapper , tal vez en una clase Utils (porque eso es lo que hice, lul).

public static ContextWrapper changeLang(Context context, String lang_code){ Locale sysLocale; Resources rs = context.getResources(); Configuration config = rs.getConfiguration(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { sysLocale = config.getLocales().get(0); } else { sysLocale = config.locale; } if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) { Locale locale = new Locale(lang_code); Locale.setDefault(locale); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocale(locale); } else { config.locale = locale; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { context = context.createConfigurationContext(config); } else { context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); } } return new ContextWrapper(context); }

Finalmente, cargue el código de idioma de SharedPreferences en el método attachBaseContext(Context newBase) ALL ACTIVITY .

@Override protected void attachBaseContext(Context newBase) { String lang_code = "en"; //load it from SharedPref Context context = Utils.changeLang(newBase, lang_code); super.attachBaseContext(context); }

BONIFICACIÓN: para ahorrar el sudor de la palma en el teclado, creé una clase LangSupportBaseActivity que extiende la Activity y utilizo el último fragmento de código allí. Y tengo todas las demás actividades extiende LangSupportBaseActivity .

Ejemplo:

public class LangSupportBaseActivity extends Activity{ ...blab blab blab so on and so forth lines of neccessary code @Override protected void attachBaseContext(Context newBase) { String lang_code = "en"; //load it from SharedPref Context context = Utils.changeLang(newBase, lang_code); super.attachBaseContext(context); } } public class HomeActivity extends LangSupportBaseActivity{ ...blab blab blab }


Las respuestas anteriores me pusieron en el camino correcto pero dejaron un par de problemas

  1. En Android 7 y 9, podría cambiar a cualquier idioma que no sea el predeterminado de la aplicación. Cuando volví al idioma predeterminado de la aplicación, mostró el último idioma seleccionado, lo que no es sorprendente, ya que esto ha anulado el predeterminado (aunque, curiosamente, ¡esto no fue un problema en Android 8!).
  2. Para los idiomas RTL, no actualizó los diseños a RTL

Para arreglar el primer elemento, almacené la configuración regional predeterminada al iniciar la aplicación.

Nota Si su idioma predeterminado está configurado en "en", las configuraciones regionales de "enGB" o "enUS" deben coincidir con la configuración regional predeterminada (a menos que proporcione localizaciones separadas para ellas). Del mismo modo, en el siguiente ejemplo, si la configuración regional del teléfono del usuario es arLY (Libia árabe), entonces el defLanguage debe ser "ar", no "arLY"

private Locale defLocale = Locale.getDefault(); private Locale locale = Locale.getDefault(); public static myApplication myApp; public static Resources res; private static String defLanguage = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry(); private static sLanguage = "en"; private static final Set<String> SUPPORTEDLANGUAGES = new HashSet<>(Arrays.asList(new String[]{"en", "ar", "arEG"})); @Override protected void attachBaseContext(Context base) { if (myApp == null) myApp = this; if (base == null) super.attachBaseContext(this); else super.attachBaseContext(setLocale(base)); } @Override public void onCreate() { myApp = this; if (!SUPPORTEDLANGUAGES.contains(test)) { // The default locale (eg enUS) is not in the supported list - lets see if the language is if (SUPPORTEDLANGUAGES.contains(defLanguage.substring(0,2))) { defLanguage = defLanguage.substring(0,2); } } } private static void setLanguage(String sLang) { Configuration baseCfg = myApp.getBaseContext().getResources().getConfiguration(); if ( sLang.length() > 2 ) { String s[] = sLang.split("_"); myApp.locale = new Locale(s[0],s[1]); sLanguage = s[0] + s[1]; } else { myApp.locale = new Locale(sLang); sLanguage = sLang; } } public static Context setLocale(Context ctx) { Locale.setDefault(myApp.locale); Resources tempRes = ctx.getResources(); Configuration config = tempRes.getConfiguration(); if (Build.VERSION.SDK_INT >= 24) { // If changing to the app default language, set locale to the default locale if (sLanguage.equals(myApp.defLanguage)) { config.setLocale(myApp.defLocale); // restored the default locale as well Locale.setDefault(myApp.defLocale); } else config.setLocale(myApp.locale); ctx = ctx.createConfigurationContext(config); // update the resources object to point to the current localisation res = ctx.getResources(); } else { config.locale = myApp.locale; tempRes.updateConfiguration(config, tempRes.getDisplayMetrics()); } return ctx; }

Para solucionar los problemas de RTL, extendí AppCompatActivity según los comentarios de Fragments en esta answer

public class myCompatActivity extends AppCompatActivity { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(myApplication.setLocale(base)); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= 17) { getWindow().getDecorView().setLayoutDirection(myApplication.isRTL() ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); } } }


Okay. Finalmente logré encontrar una solución.

Primero debe saber que en 25 API Resources.updateConfiguration(...) está en desuso. Entonces, en cambio, puedes hacer algo como esto:

1) Necesita crear su propio ContextWrapper que anulará todos los parámetros de configuración en baseContext. Por ejemplo, este es ContextWrapper mío que cambia la configuración regional correctamente. Presta atención al método context.createConfigurationContext(configuration) .

public class ContextWrapper extends android.content.ContextWrapper { public ContextWrapper(Context base) { super(base); } public static ContextWrapper wrap(Context context, Locale newLocale) { Resources res = context.getResources(); Configuration configuration = res.getConfiguration(); if (BuildUtils.isAtLeast24Api()) { configuration.setLocale(newLocale); LocaleList localeList = new LocaleList(newLocale); LocaleList.setDefault(localeList); configuration.setLocales(localeList); context = context.createConfigurationContext(configuration); } else if (BuildUtils.isAtLeast17Api()) { configuration.setLocale(newLocale); context = context.createConfigurationContext(configuration); } else { configuration.locale = newLocale; res.updateConfiguration(configuration, res.getDisplayMetrics()); } return new ContextWrapper(context); } }

2) Esto es lo que debe hacer en su BaseActivity:

@Override protected void attachBaseContext(Context newBase) { Locale newLocale; // .. create or get your new Locale object here. Context context = ContextWrapper.wrap(newBase, newLocale); super.attachBaseContext(context); }

Nota:

Recuerde recrear su actividad si desea cambiar la configuración regional en su aplicación en algún lugar. Puede anular cualquier configuración que desee con esta solución.