Modifica delle impostazioni internazionali nell'app stessa


197

I miei utenti possono modificare le impostazioni internazionali all'interno dell'app (potrebbero voler mantenere le impostazioni del telefono in inglese ma leggere il contenuto della mia app in francese, olandese o in qualsiasi altra lingua ...)

Perché funziona perfettamente in 1.5 / 1.6 ma NON più in 2.0 ???

@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...

Il problema è che il MENU "si restringe" sempre di più ogni volta che l'utente passa attraverso le righe di codice sopra ...

Questo è il menu che si restringe:

@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);
}

Cosa devo fare in API Level 5 per far funzionare di nuovo questo?

ECCO IL CODICE COMPLETO SE VUOI TESTARE QUESTO:

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);
    }
}

E QUI È IL MANIFESTO:

<?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>

QUESTO È QUELLO CHE HO TROVATO:

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

=> FUNZIONA SOLO FINE ...

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

=> Il menu si restringe ogni volta che cambi le impostazioni locali !!!

come voglio mantenere la mia applicazione accessibile per gli utenti su 1.5, cosa devo fare ??


Cosa intendi con "restringimento"?
CommonsWare,

diventa sempre più piccolo ogni volta. dovrei forse usare qualcos'altro oltre a getBaseContext?
Hubert,

Forse non sono queste le linee che creano il problema come quando faccio un'app molto semplice facendo solo il cambiamento in Locale, non ho lo stesso "restringimento del menu". Ho pensato che fosse forse perché la mia attività era in realtà un TabActivity, ma nonostante ciò, non riesco a ricreare il problema. Dovrò indagare ulteriormente qual è la causa esatta di questo errore ... non cercare oltre. Pubblicherò la risposta qui quando la trovo. Saluti, H.
Hubert,

Ho modificato il mio primo post, dando un esempio su come creare il problema. E ho notato infatti che quando cambio linea <usi-sdk android: minSdkVersion = "5" /> in "3" ... in effetti il ​​problema appare!
Hubert,

1
È possibile utilizzare la seguente libreria, che fornisce l'elenco delle lingue, le preferenze per la schermata delle impostazioni e sovrascrive la lingua dell'applicazione: github.com/delight-im/Android-Languages
caw

Risposte:


151

Attraverso la domanda originale non si tratta esattamente della locale stessa, tutte le altre domande relative alla locale fanno riferimento a questa. Ecco perché volevo chiarire il problema qui. Ho usato questa domanda come punto di partenza per il mio codice di commutazione locale e ho scoperto che il metodo non è esattamente corretto. Funziona, ma solo fino a quando non viene modificata la configurazione (ad es. Rotazione dello schermo) e solo in quella particolare attività. Giocando con un codice per un po 'ho finito con il seguente approccio:

Ho esteso android.app.Application e ho aggiunto il seguente codice:

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());
        }
    }
}

Questo codice garantisce che ogni attività abbia impostazioni internazionali personalizzate e non verrà ripristinata in caso di rotazione e altri eventi.

Ho anche trascorso molto tempo a cercare di applicare immediatamente la modifica delle preferenze ma non ci sono riuscito: la lingua è cambiata correttamente al riavvio delle attività, ma i formati numerici e le altre proprietà delle impostazioni locali non sono stati applicati fino al riavvio completo dell'applicazione.

Cambia in AndroidManifest.xml

Non dimenticate di aggiungere android:configChanges="layoutDirection|locale"ad ogni attività a AndroidManifest, così come l' android:name=".MyApplication"al <application>elemento.


1
La tua attività preferita viene chiamata dalla tua attività principale? potresti metterlo nel resume on ricaricare(); } super.onResume (); } private void refresh () {finish (); Intent myIntent = new Intent (Main.this, Main.class); startActivity (myIntent); }
trgraglia,

1
Questo è effettivamente ciò di cui ho bisogno, chiamavo updateConfiguration () in MainActivity ma i menu non si aggiornano quando termina l'applicazione, quindi si avvia. La tua soluzione è giusta. Se vuoi aggiornare immediatamente le stringhe, penso che tu debba ancora resettare manualmente le stringhe. (ad es. ripristinare l'etichetta del pulsante o ricreare il menu).
emeraldhieu,

Questo interferisce con le impostazioni locali del sistema Android?
Kostadin,

17
Nota che non devi mai modificare un oggetto Configurazione che Android ti dà ( confige newConfig). Dovresti fare una copia con config = new Configuration(config);prima. Ho appena trascorso un'ora a eseguire il debug di questo codice prima di scoprire il problema (stava causando il flashing continuo dell'attività).
imgx64

1
Questa tecnica presenta anomalie su Android 7+ quando si avvia l'app da un avvio a freddo, con la dimensione del carattere di sistema impostata su grande. Google ha cambiato il modo in cui updateConfiguration()funziona, quindi a volte la tua attività verrà visualizzata in 2 lingue diverse (specialmente nelle finestre di dialogo). Maggiori informazioni: stackoverflow.com/questions/39705739/…
Mr-IDE

61

Dopo una buona notte di sonno, ho trovato la risposta sul Web (una semplice ricerca su Google nella seguente riga " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), eccola:

link text => questo link mostra anche screenshotscosa sta succedendo!

La densità era il problema qui , avevo bisogno di avere questo in AndroidManifest.xml

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

Il più importante è Android: anyDensity = "true" .

Non dimenticare di aggiungere quanto segue in AndroidManifest.xmlper ogni attività (per Android 4.1 e versioni precedenti):

android:configChanges="locale"

Questa versione è necessaria quando si crea una spiegazione per Android 4.2 (livello API 17) qui :

android:configChanges="locale|layoutDirection"

3
Avevo android: anyDensity = "false" grazie al consiglio di Google (Strategie per le applicazioni legacy - punto 9: developer.android.com/intl/fr/guide/practices/… ). Stai attento allora!
Hubert,

2
I testi sui pulsanti non cambiano dopo aver cambiato le impostazioni internazionali. Mi chiedo se esiste una soluzione per aggiornare tutti i widget che contengono risorse di stringa. Ora sto ridisegnando il testo usando setText (getString (R.string.button_label)) in OnRestart (). (Naturalmente cambia dopo aver riavviato l'applicazione.)
emeraldhieu,

puoi chiamare ricreare attivitàactivity.recreate()
A Kra

59

In Android M la soluzione migliore non funzionerà. Ho scritto una classe di supporto per risolvere ciò che dovresti chiamare dalla tua classe Applicazione e da tutte le Attività (suggerirei di creare una BaseActivity e quindi fare in modo che tutte le Attività ereditino da essa.

Nota : questo supporterà anche la corretta direzione del layout RTL.

Classe di aiuto:

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());
        }
    }
}

Applicazione:

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);
    }
}

11
Questa dovrebbe essere una risposta accettata. Assicurati che updateConfig sia chiamato dal costruttore non dalla classe onCreate di attività, stavo facendo questo errore.
Hitesh Sahu

1
dipende da come lo hai implementato nel tuo programma. Se hai sovrascritto onConfigurationChanged () non è necessario chiamare .updateConfig () ed è per questo che stai ricevendo l'errore, perché viene chiamato due volte. se stai dicendo che solo la chiamata a .setLocale () non funziona, questo perché devi aggiornare la tua UI, chiamando ricreate () sull'attività o il tuo metodo in attività / frammenti (preferisco il primo anche se non è fluido ma non ti preoccuperai mai dei problemi)
RJFares

3
// import android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; // namespace corretto
Mehdi Rostami,

1
Il BaseActivity()costruttore non dovrebbe chiamare super()?
LarsH,

1
Questa soluzione include implicitamente l'inserimento <application android:name=".App">del manifest? Che ne dici di aggiungere android:configChanges="layoutDirection|locale"ad ogni attività nel manifest?
LarsH,

10

Questo è per il mio commento sulla risposta di Andrey, ma non posso includere il codice nei commenti.

La tua attività preferita viene chiamata dalla tua attività principale? potresti metterlo nel resume ...

@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);
}

2
Vuoi dire se voglio cambiare la lingua in fase di esecuzione, allora devo finire quell'attività e ricominciare quella attività per ottenere le modifiche? c'è un altro modo per farlo cambiare in quanto non è bene ogni volta terminare l'attività e ricominciare.
Shreyash Mahajan,

1
meglio fareactivity.recreate()
Per Kra

@trgradlia, voglio usare il codice sopra. Per favore, dimmi, cos'è langPreference? Penso che stiamo confrontando lang recentemente modificato e lang precedente.
Mahen,

1
L'attività ha un recreate()metodo, quindi usalo recreate()al posto del refresh()tuo codice. Ciò consentirà di salvare le 3 righe di codice nel refresh()metodo
Inzimam Tariq IT

1
@InzimamTariqIT, Grazie per la punta ... La risposta ha quasi 7 anni, quindi la lascerò così com'è e la gente può trovare il tuo miglioramento qui nei commenti.
trgraglia,

6

Non potevo usare Android: anyDensity = "true" perché gli oggetti nel mio gioco sarebbero posizionati in modo completamente diverso ... sembra che questo faccia anche il trucco:

// creazione locale
Locale2 locale = nuovo Locale (loc); 
Locale.setDefault (locale2);
Configuration config2 = new Configuration ();
config2.locale = locale2;

// aggiornamento delle impostazioni internazionali
mContext.getResources (). updateConfiguration (config2, null);

Questa soluzione è la migliore per me appena usatacode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk,

1

Se vuoi influenzare le opzioni di menu per cambiare immediatamente le impostazioni locali, devi fare così.

//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);
}

Ma questa soluzione non cancella il menu su ogni menu aperto? Suppongo che onMenuOpeneddovrebbe essere chiamato solo una volta, dopo il cambio di locale.
trante,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.