Domanda

La mia app supporta 3 (presto 4) lingue. Dato che diversi locali sono abbastanza simili, vorrei dare all'utente la possibilità di cambiare la localizzazione nella mia domanda, ad esempio una persona italiana potrebbe preferire lo spagnolo rispetto all'inglese.

Esiste un modo per l'utente di selezionare tra i locali disponibili per l'applicazione e quindi cambiare quale locale viene utilizzato? Non lo vedo come un problema per impostare la località per ogni attività poiché è un compito semplice da eseguire in una classe base.

È stato utile?

Soluzione

Per le persone ancora alla ricerca di questa risposta, da allora configuration.locale è stato deprecato dall'API 24, ora puoi usare:

configuration.setLocale(locale);

Prendi in considerazione che il minskdversion per questo metodo è API 17.

Codice di esempio completo:

@SuppressWarnings("deprecation")
private void setLocale(Locale locale){
    SharedPrefUtils.saveLocale(locale); // optional - Helper method to save the selected language to SharedPreferences in case you might need to attach to activity context (you will need to code this)
    Resources resources = getResources();
    Configuration configuration = resources.getConfiguration();
    DisplayMetrics displayMetrics = resources.getDisplayMetrics();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
        configuration.setLocale(locale);
    } else{
        configuration.locale=locale;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
        getApplicationContext().createConfigurationContext(configuration);
    } else {
        resources.updateConfiguration(configuration,displayMetrics);
    }
}

Non dimenticarlo, se si cambia il locale con un'attività in esecuzione, dovrai riavviarlo per avere effetto le modifiche.

Modifica l'11 maggio 2018

Come dal post di @Cookiemonster, potresti avere problemi a mantenere la modifica del locale nelle versioni API più alte. In tal caso, aggiungi il seguente codice alla tua attività di base in modo da aggiornare la posizione di contesto su ogni creazione di attività:

@Override
protected void attachBaseContext(Context base) {
     super.attachBaseContext(updateBaseContextLocale(base));
}

private Context updateBaseContextLocale(Context context) {
    String language = SharedPrefUtils.getSavedLanguage(); // Helper method to get saved language from SharedPreferences
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

Se lo usi, non dimenticare di salvare la lingua in preferenze condivise quando si imposta il locale con setLocate(locale)

Altri suggerimenti

Spero che questo aiuto (in OnResume):

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());

Poiché nessuna risposta è completa per il modo attuale per risolvere questo problema, cerco di fornire istruzioni per una soluzione completa. Si prega di commentare se manca qualcosa o potrebbe essere fatto meglio.

Informazione Generale

Innanzitutto, esistono alcune librerie che vogliono risolvere il problema ma sembrano tutte obsolete o mancano alcune funzionalità:

Inoltre, penso che scrivere una biblioteca potrebbe non essere un modo buono/semplice per risolvere questo problema perché non c'è molto da fare, e ciò che deve essere fatto è piuttosto cambiare il codice esistente piuttosto che usare qualcosa di completamente disaccoppiato. Pertanto ho composto le seguenti istruzioni che dovrebbero essere complete.

La mia soluzione si basa principalmente su https://github.com/gunhansancar/changelanguageexample (come già collegato a Localhost). È il codice migliore che ho trovato per orientarmi. Alcune osservazioni:

  • Se necessario, fornisce diverse implementazioni per modificare i locali per Android N (e oltre) e inferiore
  • Utilizza un metodo updateViews() In ogni attività per aggiornare manualmente tutte le stringhe dopo aver modificato la localizzazione (usando il solito getString(id)) che non è necessario nell'approccio mostrato di seguito
  • Supporta solo le lingue e non i locali completi (che includono anche i codici della regione (paese) e delle varianti)

L'ho cambiato un po ', disaccoppiando la parte che persiste il locale scelto (come si potrebbe voler farlo separatamente, come suggerito di seguito).

Soluzione

La soluzione è costituita dai seguenti due passaggi:

  • Modifica in modo permanente il locale per essere utilizzato dall'app
  • Fai in modo che l'app utilizza il set di locali personalizzato, senza riavvio

Passaggio 1: modificare il locale

Usa la classe LocaleHelper, basato su Gunhansancar's Localihelper:

  • Aggiungere un ListPreference in un PreferenceFragment con le lingue disponibili (deve essere mantenuto quando le lingue devono essere aggiunte in seguito)
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;

import java.util.Locale;

import mypackage.SettingsFragment;

/**
 * Manages setting of the app's locale.
 */
public class LocaleHelper {

    public static Context onAttach(Context context) {
        String locale = getPersistedLocale(context);
        return setLocale(context, locale);
    }

    public static String getPersistedLocale(Context context) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SettingsFragment.KEY_PREF_LANGUAGE, "");
    }

    /**
     * Set the app's locale to the one specified by the given String.
     *
     * @param context
     * @param localeSpec a locale specification as used for Android resources (NOTE: does not
     *                   support country and variant codes so far); the special string "system" sets
     *                   the locale to the locale specified in system settings
     * @return
     */
    public static Context setLocale(Context context, String localeSpec) {
        Locale locale;
        if (localeSpec.equals("system")) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                locale = Resources.getSystem().getConfiguration().getLocales().get(0);
            } else {
                //noinspection deprecation
                locale = Resources.getSystem().getConfiguration().locale;
            }
        } else {
            locale = new Locale(localeSpec);
        }
        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, locale);
        } else {
            return updateResourcesLegacy(context, locale);
        }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, Locale locale) {
        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, Locale locale) {
        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }

        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }
}

Creare un SettingsFragment Come i seguenti:

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * Fragment containing the app's main settings.
 */
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_LANGUAGE = "pref_key_language";

    public SettingsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_settings, container, false);
        return view;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        switch (key) {
            case KEY_PREF_LANGUAGE:
                LocaleHelper.setLocale(getContext(), PreferenceManager.getDefaultSharedPreferences(getContext()).getString(key, ""));
                getActivity().recreate(); // necessary here because this Activity is currently running and thus a recreate() in onResume() would be too late
                break;
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        // documentation requires that a reference to the listener is kept as long as it may be called, which is the case as it can only be called from this Fragment
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}

Crea una risorsa locales.xml Elencare tutte le località con traduzioni disponibili nel modo seguente (Elenco dei codici locali):

<!-- Lists available locales used for setting the locale manually.
     For now only language codes (locale codes without country and variant) are supported.
     Has to be in sync with "settings_language_values" in strings.xml (the entries must correspond).
  -->
<resources>
    <string name="system_locale" translatable="false">system</string>
    <string name="default_locale" translatable="false"></string>
    <string-array name="locales">
        <item>@string/system_locale</item> <!-- system setting -->
        <item>@string/default_locale</item> <!-- default locale -->
        <item>de</item>
    </string-array>
</resources>

Nel tuo PreferenceScreen È possibile utilizzare la sezione seguente per consentire all'utente di selezionare le lingue disponibili:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/preferences_category_general">
        <ListPreference
            android:key="pref_key_language"
            android:title="@string/preferences_language"
            android:dialogTitle="@string/preferences_language"
            android:entries="@array/settings_language_values"
            android:entryValues="@array/locales"
            android:defaultValue="@string/system_locale"
            android:summary="%s">
        </ListPreference>
    </PreferenceCategory>
</PreferenceScreen>

che utilizza le seguenti stringhe da strings.xml:

<string name="preferences_category_general">General</string>
<string name="preferences_language">Language</string>
<!-- NOTE: Has to correspond to array "locales" in locales.xml (elements in same orderwith) -->
<string-array name="settings_language_values">
    <item>Default (System setting)</item>
    <item>English</item>
    <item>German</item>
</string-array>

Passaggio 2: effettuare l'app Utilizzare il locale personalizzato

Ora imposta ogni attività per utilizzare il set di locali personalizzato. Il modo più semplice per raggiungere questo obiettivo è avere una classe base comune per tutte le attività con il seguente codice (in cui si trova il codice importante attachBaseContext(Context base) e onResume()):

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * {@link AppCompatActivity} with main menu in the action bar. Automatically recreates
 * the activity when the locale has changed.
 */
public class MenuAppCompatActivity extends AppCompatActivity {
    private String initialLocale;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initialLocale = LocaleHelper.getPersistedLocale(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_settings:
                Intent intent = new Intent(this, SettingsActivity.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base));
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (initialLocale != null && !initialLocale.equals(LocaleHelper.getPersistedLocale(this))) {
            recreate();
        }
    }
}

Quello che fa è

  • Oltrepassare attachBaseContext(Context base) Per utilizzare la locale precedentemente persistita con LocaleHelper
  • Rileva un cambio del locale e ricreare l'attività per aggiornare le sue stringhe

Note su questa soluzione

  • La ricreazione di un'attività non aggiorna il titolo di Actionbar (come già osservato qui: https://github.com/gunhansancar/changelanguageexample/issues/1).

    • Questo può essere ottenuto semplicemente avendo un setTitle(R.string.mytitle) nel onCreate() metodo di ogni attività.
  • Consente all'utente di scegliere il sistema predefinito del sistema, nonché il locale predefinito dell'app (che può essere nominato, in questo caso "inglese").

  • Solo codici linguistici, nessun regione (paese) e codici varianti (come fr-rCA) sono supportati finora. Per supportare le specifiche complete della localizzazione, un parser simile a quello del Biblioteca Android-Languages può essere utilizzato (che supporta la regione ma nessun codice variante).

    • Se qualcuno trova o ha scritto un buon parser, aggiungi un commento in modo da poterlo includere nella soluzione.

Ho avuto un problema con l'impostazione di una locale a livello di programmazione con i dispositivi che hanno Android OS n e superiore. Per me la soluzione era scrivere questo codice nella mia attività di base:

(Se non hai un'attività di base, allora dovresti apportare questi cambiamenti in tutte le tue attività)

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(updateBaseContextLocale(base));
}

private Context updateBaseContextLocale(Context context) {
    String language = SharedPref.getInstance().getSavedLanguage();
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

Nota che qui non è sufficiente chiamare

createConfigurationContext(configuration)

È inoltre necessario ottenere il contesto che questo metodo restituisce e quindi impostare questo contesto in attachBaseContext metodo.

@SuppressWarnings("deprecation")
public static void forceLocale(Context context, String localeCode) {
    String localeCodeLowerCase = localeCode.toLowerCase();

    Resources resources = context.getApplicationContext().getResources();
    Configuration overrideConfiguration = resources.getConfiguration();
    Locale overrideLocale = new Locale(localeCodeLowerCase);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        overrideConfiguration.setLocale(overrideLocale);
    } else {
        overrideConfiguration.locale = overrideLocale;
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        context.getApplicationContext().createConfigurationContext(overrideConfiguration);
    } else {
        resources.updateConfiguration(overrideConfiguration, null);
    }
}

Basta usare questo metodo Helper per forzare il locale specifico.

Udpate 22 agosto 2017. migliore utilizzo questo approccio.

Aggiungi una classe helper con il seguente metodo:

public class LanguageHelper {
    public static final void setAppLocale(String language, Activity activity) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Resources resources = activity.getResources();
            Configuration configuration = resources.getConfiguration();
            configuration.setLocale(new Locale(language));
            activity.getApplicationContext().createConfigurationContext(configuration);
        } else {
            Locale locale = new Locale(language);
            Locale.setDefault(locale);
            Configuration config = activity.getResources().getConfiguration();
            config.locale = locale;
            activity.getResources().updateConfiguration(config,
                    activity.getResources().getDisplayMetrics());
        }

    }
}

E chiamalo nella tua attività di avvio, come MainActivity.java:

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}

Semplice e facile

Locale locale = new Locale("en", "US");
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = locale;
res.updateConfiguration(conf, dm);

dove "en" è il codice linguistico e "noi" è il codice del paese.

 /**
 * Requests the system to update the list of system locales.
 * Note that the system looks halted for a while during the Locale migration,
 * so the caller need to take care of it.
 */
public static void updateLocales(LocaleList locales) {
    try {
        final IActivityManager am = ActivityManager.getService();
        final Configuration config = am.getConfiguration();

        config.setLocales(locales);
        config.userSetLocale = true;

        am.updatePersistentConfiguration(config);
    } catch (RemoteException e) {
        // Intentionally left blank
    }
}

C'è un modo super semplice.

In basiattività, attività o frammento di sovraccarico di allegateContext

 override fun attachBaseContext(context: Context) {
    super.attachBaseContext(context.changeLocale("tr"))
}

estensione

fun Context.changeLocale(language:String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = this.resources.configuration
    config.setLocale(locale)
    return createConfigurationContext(config)
}

Valido per API16 a API28 Basta posizionare questo metodo in cui:

    Context newContext = context;

        Locale locale = new Locale(languageCode);
        Locale.setDefault(locale);

        Resources resources = context.getResources();
        Configuration config = new Configuration(resources.getConfiguration());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {

        config.setLocale(locale);
                newContext = context.createConfigurationContext(config);

        } else {

        config.locale = locale;
                resources.updateConfiguration(config, resources.getDisplayMetrics());
        }

    return newContext;
}

Inserisci questo codice in tutte le tue attività usando:

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(localeUpdateResources(base, "<-- language code -->"));
    }

Oppure chiama LocaleupDateSources su frammenti, adattatori, ecc. Dove hai bisogno del nuovo contesto.

Crediti: Yaroslav Berezanskyi

Metti questo codice nella tua attività

 if (id==R.id.uz)
    {
        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
        return true;
    }
    if (id == R.id.ru) {

        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top