Modification des paramètres régionaux dans l'application elle-même

197

Mes utilisateurs peuvent modifier les paramètres régionaux dans l'application (ils peuvent vouloir conserver les paramètres de leur téléphone en anglais mais lire le contenu de mon application en français, néerlandais ou toute autre langue ...)

Pourquoi cela fonctionne-t-il parfaitement bien en 1.5 / 1.6 mais PAS en 2.0 plus ???

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
    case 201:
        Locale locale2 = new Locale("fr"); 
        Locale.setDefault(locale2);
        Configuration config2 = new Configuration();
        config2.locale = locale2;
        getBaseContext().getResources().updateConfiguration(
            config2, getBaseContext().getResources().getDisplayMetrics());
        // loading data ...
        refresh();
        // refresh the tabs and their content
        refresh_Tab ();   
     break;
     case 201: etc...

Le problème est que le MENU "rétrécit" de plus en plus chaque fois que l'utilisateur parcourt les lignes de code ci-dessus ...

Voici le menu qui se réduit:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 100, 1, "REFRESH").setIcon(android.R.drawable.ic_menu_compass);
    SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
        langMenu.add(1, 201, 0, "Nederlands");
        langMenu.add(1, 202, 0, "Français");
    menu.add(0, 250, 4, R.string.OptionMenu2).setIcon(android.R.drawable.ic_menu_send);
    menu.add(0, 300, 5, R.string.OptionMenu3).setIcon(android.R.drawable.ic_menu_preferences);
    menu.add(0, 350, 3, R.string.OptionMenu4).setIcon(android.R.drawable.ic_menu_more);
    menu.add(0, 400, 6, "Exit").setIcon(android.R.drawable.ic_menu_delete);

    return super.onCreateOptionsMenu(menu);
}

Que dois-je faire dans l'API niveau 5 pour que cela fonctionne à nouveau?

VOICI LE CODE COMPLET SI VOUS VOULEZ TESTER CECI:

import java.util.Locale;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

public class Main extends Activity {
    /** Called when the activity is first created. */


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
            langMenu.add(1, 201, 0, "Nederlands");
            langMenu.add(1, 202, 0, "Français");

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){

        case 201:

            Locale locale = new Locale("nl"); 
            Locale.setDefault(locale);
            Configuration config = new Configuration();
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
            Toast.makeText(this, "Locale in Nederlands !", Toast.LENGTH_LONG).show();
            break;

        case 202:

            Locale locale2 = new Locale("fr"); 
            Locale.setDefault(locale2);
            Configuration config2 = new Configuration();
            config2.locale = locale2;
            getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

            Toast.makeText(this, "Locale en Français !", Toast.LENGTH_LONG).show();
            break;  

        }
        return super.onOptionsItemSelected(item);
    }
}

ET VOICI LE MANIFESTE:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cousinHub.ChangeLocale"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Main"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" /> 
    </manifest>

C'EST CE QUE J'AI TROUVÉ:

<uses-sdk android:minSdkVersion="5" />

=> IL FONCTIONNE JUSTE FIN ...

<uses-sdk android:minSdkVersion="3" />

=> Le menu rétrécit à chaque fois que vous modifiez les paramètres régionaux !!!

comme je veux garder mon application accessible aux utilisateurs sur 1.5, que dois-je faire ??

Hubert
la source
Qu'entendez-vous par «rétrécit»?
CommonsWare
il devient de plus en plus petit à chaque fois. dois-je utiliser peut-être autre chose que getBaseContext?
Hubert
Peut-être que ce ne sont pas ces lignes qui créent le problème car lorsque je fais une application très basique qui fait juste le changement dans Locale, je n'ai pas le même "rétrécissement du menu". Je pensais que c'était peut-être parce que mon activité était en fait une TabActivity, mais même avec ça, je ne peux pas recréer le problème. Je vais devoir enquêter davantage sur la cause exacte de ce bogue ... ne cherchez pas plus loin alors. Je posterai la réponse ici quand je la trouverai. Cheers, H.
Hubert
J'ai édité mon premier article, donnant un exemple sur la façon de créer le problème. Et j'ai remarqué en fait que lorsque je change la ligne <uses-sdk android: minSdkVersion = "5" /> en "3" ... en effet le problème apparaît!
Hubert
1
Vous pouvez utiliser la bibliothèque suivante, qui fournit la liste des langues, la préférence pour votre écran de paramètres et remplace la langue dans votre application: github.com/delight-im/Android-Languages
caw

Réponses:

151

Grâce à la question d'origine, il ne s'agit pas exactement des paramètres régionaux eux-mêmes, toutes les autres questions liées aux paramètres régionaux font référence à celle-ci. C'est pourquoi je voulais clarifier la question ici. J'ai utilisé cette question comme point de départ pour mon propre code de commutation locale et j'ai découvert que la méthode n'était pas exactement correcte. Cela fonctionne, mais uniquement jusqu'à tout changement de configuration (par exemple, rotation de l'écran) et uniquement dans cette activité particulière. Jouer avec un code pendant un certain temps, je me suis retrouvé avec l'approche suivante:

J'ai étendu android.app.Application et ajouté le code suivant:

public class MyApplication extends Application
{
    private Locale locale = null;

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        if (locale != null)
        {
            newConfig.locale = locale;
            Locale.setDefault(locale);
            getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
        }
    }

    @Override
    public void onCreate()
    {
        super.onCreate();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        Configuration config = getBaseContext().getResources().getConfiguration();

        String lang = settings.getString(getString(R.string.pref_locale), "");
        if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
        {
            locale = new Locale(lang);
            Locale.setDefault(locale);
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
        }
    }
}

Ce code garantit que chaque activité aura un ensemble de paramètres régionaux personnalisé et ne sera pas réinitialisé lors de la rotation et d'autres événements.

J'ai également passé beaucoup de temps à essayer d'apporter la modification des préférences à appliquer immédiatement, mais sans succès: la langue a changé correctement au redémarrage de l'activité, mais les formats numériques et les autres propriétés locales n'ont été appliqués qu'au redémarrage complet de l'application.

Modifications apportées à AndroidManifest.xml

N'oubliez pas d'ajouter android:configChanges="layoutDirection|locale"à chaque activité sur AndroidManifest, ainsi qu'à android:name=".MyApplication"l' <application>élément.

Andrey Novikov
la source
1
Votre activité préférée est-elle appelée de votre activité principale? vous pouvez le placer dans le résumé ... @Override protected void onResume () {if (! (PreferenceManager.getDefaultSharedPreferences (getApplicationContext ()). getString ("listLanguage", "en") .equals (langPreference))) { rafraîchir(); } super.onResume (); } private void refresh () {finish (); Intention myIntent = new Intent (Main.this, Main.class); startActivity (myIntent); }
trgraglia
1
C'est en fait ce dont j'ai besoin, j'avais l'habitude d'appeler updateConfiguration () dans MainActivity mais les menus ne sont pas mis à jour lorsque l'application se termine puis démarre. Votre solution est juste. Si vous souhaitez actualiser les chaînes immédiatement, je pense que vous devez toujours réinitialiser les chaînes manuellement. (par exemple, réinitialiser l'étiquette du bouton ou recréer le menu).
emeraldhieu
Est-ce que cela interfère avec les paramètres régionaux du système Android?
Kostadin
17
Notez que vous ne devez jamais modifier un objet Configuration qu'Android vous donne ( configet newConfig). Vous devez d'abord en faire une copie config = new Configuration(config);. Je viens de passer une heure à déboguer ce code avant de découvrir le problème (cela faisait clignoter l'activité à l'infini).
imgx64
1
Cette technique présente des problèmes sur Android 7+ lors du démarrage de l'application à partir d'un démarrage à froid, avec une taille de police système définie sur grande. Google a changé la façon dont updateConfiguration()fonctionne, donc parfois votre activité sera affichée dans 2 langues différentes (en particulier dans les boîtes de dialogue). Plus d'infos: stackoverflow.com/questions/39705739/…
Mr-IDE
61

Après une bonne nuit de sommeil, j'ai trouvé la réponse sur le Web (une simple recherche Google sur la ligne suivante " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), la voici:

texte du lien => ce lien montre également screenshotsce qui se passe!

La densité était le problème ici , je devais l'avoir dans le AndroidManifest.xml

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>

Le plus important est l' androïde: anyDensity = "true" .

N'oubliez pas d'ajouter ce qui suit dans AndroidManifest.xmlchaque activité (pour Android 4.1 et ci-dessous):

android:configChanges="locale"

Cette version est nécessaire lorsque vous créez une explication pour Android 4.2 (API niveau 17) ici :

android:configChanges="locale|layoutDirection"
Hubert
la source
3
J'avais android: anyDensity = "false" en raison des conseils de Google (Stratégies pour les applications héritées - point 9: developer.android.com/intl/fr/guide/practices/… ). Attention alors!
Hubert
2
Les textes sur les boutons ne changent pas après avoir changé les paramètres régionaux. Je me demande s'il existe une solution pour actualiser tous les widgets qui contiennent des ressources de chaîne. Maintenant, je redessine le texte en utilisant setText (getString (R.string.button_label)) dans OnRestart (). (Bien sûr, il change après le redémarrage de l'application.)
emeraldhieu
vous pouvez appeler l'activité de recréationactivity.recreate()
À Kra
59

Dans Android M, la meilleure solution ne fonctionnera pas. J'ai écrit une classe d'assistance pour corriger ce que vous devez appeler à partir de votre classe Application et de toutes les activités (je suggère de créer une BaseActivity et de faire en sorte que toutes les activités en héritent.

Remarque : Cela prendra également en charge correctement la direction de mise en page RTL.

Classe d'assistance:

public class LocaleUtils {

    private static Locale sLocale;

    public static void setLocale(Locale locale) {
        sLocale = locale;
        if(sLocale != null) {
            Locale.setDefault(sLocale);
        }
    }

    public static void updateConfig(ContextThemeWrapper wrapper) {
        if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Configuration configuration = new Configuration();
            configuration.setLocale(sLocale);
            wrapper.applyOverrideConfiguration(configuration);
        }
    }

    public static void updateConfig(Application app, Configuration configuration) {
        if (sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Wrapping the configuration to avoid Activity endless loop
            Configuration config = new Configuration(configuration);
            // We must use the now-deprecated config.locale and res.updateConfiguration here,
            // because the replacements aren't available till API level 24 and 17 respectively.
            config.locale = sLocale;
            Resources res = app.getBaseContext().getResources();
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
    }
}

Application:

public class App extends Application {
    public void onCreate(){
        super.onCreate();

        LocaleUtils.setLocale(new Locale("iw"));
        LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleUtils.updateConfig(this, newConfig);
    }
}

BaseActivity:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}
Roberto B.
la source
11
Cette réponse devrait être acceptée. Assurez-vous que updateConfig doit être appelé depuis le constructeur et non depuis onCreate de la classe d'activité, je faisais cette erreur ..
Hitesh Sahu
1
cela dépend de la façon dont vous avez implémenté cela dans votre programme. Si vous avez remplacé onConfigurationChanged (), vous n'avez pas besoin d'appeler .updateConfig () et c'est pourquoi vous obtenez l'erreur, car elle est appelée deux fois. si vous dites que seul l'appel de .setLocale () ne fonctionne pas, c'est parce que vous devez rafraîchir votre interface utilisateur, soit en appelant recréer () sur l'activité ou votre propre méthode dans activités / fragments (je préfère la première même si ce n'est pas fluide mais vous ne vous inquiétez jamais des problèmes)
RJFares
3
// import android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; // correct namespace
Mehdi Rostami
1
Le BaseActivity()constructeur ne devrait-il pas appeler super()?
LarsH
1
Cette solution inclut-elle implicitement la mise <application android:name=".App">en manifeste? Que diriez-vous d'ajouter android:configChanges="layoutDirection|locale"à chaque activité du manifeste?
LarsH
10

C'est pour mon commentaire sur la réponse d'Andrey mais je ne peux pas inclure de code dans les commentaires.

Votre activité préférée est-elle appelée de votre activité principale? vous pouvez le placer dans le CV ...

@Override
protected void onResume() {
    if (!(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getString("listLanguage", "en")
            .equals(langPreference))) {
        refresh();
    }
    super.onResume();
}

private void refresh() {
    finish();
    Intent myIntent = new Intent(Main.this, Main.class);
    startActivity(myIntent);
}
trgraglia
la source
2
Vous voulez dire que si je veux changer la langue au moment de l'exécution, je dois terminer cette activité et recommencer cette activité pour obtenir des changements? existe-t-il un autre moyen de le faire changer, car il n'est pas bon de terminer l'activité à chaque fois et de la recommencer.
Shreyash Mahajan
1
mieux faireactivity.recreate()
Pour Kra
@trgradlia, je veux utiliser le code ci-dessus. Dites-moi, qu'est-ce que langPreference? Je pense que nous comparons la langue récemment modifiée et la langue précédente.
Mahen
1
L' activité a recreate()méthode si l' utilisation recreate()à la place de refresh()votre code. Cela permettra d'économiser vos 3 lignes de code dans la refresh()méthode
Inzimam Tariq IT
1
@InzimamTariqIT, Merci pour l'astuce ... La réponse a presque 7 ans, donc je vais la laisser telle quelle et les gens peuvent trouver votre amélioration ici dans les commentaires.
trgraglia
6

Je ne pouvais pas utiliser Android: anyDensity = "true" car les objets de mon jeu seraient positionnés de manière complètement différente ... cela semble aussi faire l'affaire:

// création de paramètres régionaux
Paramètres régionaux2 = nouveaux paramètres régionaux (loc); 
Locale.setDefault (locale2);
Configuration config2 = nouvelle configuration ();
config2.locale = locale2;

// mise à jour des paramètres régionaux
mContext.getResources (). updateConfiguration (config2, null);
nikib3ro
la source
Cette solution me code Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
convient
1

Si vous souhaitez appliquer immédiatement les options de menu pour modifier les paramètres régionaux, vous devez procéder comme suit.

//onCreate method calls only once when menu is called first time.
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    //1.Here you can add your  locale settings . 
    //2.Your menu declaration.
}
//This method is called when your menu is opend to again....
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    menu.clear();
    onCreateOptionsMenu(menu);
    return super.onMenuOpened(featureId, menu);
}
Ramesh Akula
la source
Mais cette solution n'efface-t-elle pas le menu à chaque menu ouvert? Je suppose qu'il onMenuOpenedne devrait être appelé qu'une seule fois, après un changement de paramètres régionaux.
trante