Mengubah Lokal di dalam aplikasi itu sendiri

197

Pengguna saya dapat mengubah Lokal dalam aplikasi (mereka mungkin ingin menjaga pengaturan ponsel mereka dalam bahasa Inggris tetapi membaca konten aplikasi saya dalam bahasa Prancis, Belanda atau bahasa lainnya ...)

Mengapa ini berfungsi dengan baik di 1.5 / 1.6 tapi TIDAK di 2.0 lagi ???

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

Masalahnya adalah MENU "menyusut" semakin banyak setiap kali pengguna melewati baris kode di atas ...

Ini adalah Menu yang menyusut:

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

Apa yang harus saya lakukan di API Level 5 untuk membuat ini berfungsi lagi?

DI SINI KODE LENGKAP JIKA ANDA INGIN MENGUJI INI:

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

DAN DI SINI ADALAH MANIFEST:

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

INI ADALAH APA YANG SAYA TEMUKAN:

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

=> ITU BEKERJA HANYA HALUS ...

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

=> Menu menyusut setiap kali Anda mengubah lokal !!!

karena saya ingin agar aplikasi saya dapat diakses oleh pengguna di 1.5, apa yang harus saya lakukan ??

Hubert
sumber
Apa yang Anda maksud dengan "menyusut"?
CommonsWare
semakin kecil dan semakin kecil setiap kali. haruskah saya menggunakan hal lain selain getBaseContext?
Hubert
Mungkin bukan garis-garis ini yang menciptakan masalah seperti ketika saya melakukan aplikasi yang sangat mendasar hanya melakukan perubahan di Lokal, saya tidak memiliki "menyusut menu" yang sama. Saya pikir itu mungkin karena Aktivitas saya sebenarnya adalah TabActivity, tetapi bahkan dengan itu, saya tidak dapat membuat kembali masalah. Saya harus menyelidiki lebih lanjut apa penyebab sebenarnya dari bug ini ... jangan cari lebih jauh. Saya akan mengirim jawabannya di sini ketika saya menemukannya. Cheers, H.
Hubert
Saya mengedit posting pertama saya, memberikan contoh tentang cara membuat masalah. Dan saya perhatikan bahwa ketika saya mengubah baris <uses-sdk android: minSdkVersion = "5" /> menjadi "3" ... memang masalahnya muncul!
Hubert
1
Anda dapat menggunakan perpustakaan berikut, yang menyediakan daftar bahasa, preferensi untuk layar pengaturan Anda, dan menimpa bahasa dalam aplikasi Anda: github.com/delight-im/Android-Languages
gak

Jawaban:

151

Melalui pertanyaan asli tidak persis tentang lokal itu sendiri semua pertanyaan terkait lokal lainnya mengacu pada yang satu ini. Itu sebabnya saya ingin menjelaskan masalah ini di sini. Saya menggunakan pertanyaan ini sebagai titik awal untuk kode switching lokal saya sendiri dan menemukan bahwa metode ini tidak sepenuhnya benar. Ini berfungsi, tetapi hanya sampai konfigurasi berubah (mis. Rotasi layar) dan hanya di aktivitas tertentu. Bermain dengan kode untuk sementara waktu saya berakhir dengan pendekatan berikut:

Saya telah memperpanjang android.app.Application dan menambahkan kode berikut:

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

Kode ini memastikan bahwa setiap Kegiatan akan memiliki set lokal kustom dan tidak akan diatur ulang pada rotasi dan acara lainnya.

Saya juga menghabiskan banyak waktu untuk mencoba membuat perubahan preferensi diterapkan segera tetapi tidak berhasil: bahasa berubah dengan benar saat Restart aktivitas, tetapi format angka dan properti lokal lainnya tidak diterapkan sampai aplikasi penuh restart.

Perubahan menjadi AndroidManifest.xml

Jangan lupa menambahkan android:configChanges="layoutDirection|locale"ke setiap aktivitas di AndroidManifest, dan juga android:name=".MyApplication"ke <application>elemen.

Andrey Novikov
sumber
1
Apakah Anda memilih aktivitas yang dipanggil dari aktivitas utama Anda? Anda dapat menempatkan ini di resume ... @Override protected void onResume () {if (! (PreferenceManager.getDefaultSharedPreferences (getApplicationContext ()). getString ("listLanguage", "en"). equals (langPreference))) { menyegarkan(); } super.onResume (); } private void refresh () {finish (); Intent myIntent = Intent baru (Main.this, Main.class); startActivity (myIntent); }
trgraglia
1
Ini sebenarnya yang saya butuhkan, saya biasa memanggil updateConfiguration () di MainActivity tetapi menu tidak memperbarui ketika Aplikasi berhenti kemudian mulai. Solusi Anda benar. Jika Anda ingin segera me-refresh string, saya pikir Anda masih harus mengatur ulang string secara manual. (mis. reset tombol label atau buat ulang menu).
emeraldhieu
Apakah ini mengganggu sistem lokal Android?
Kostadin
17
Perhatikan bahwa Anda tidak boleh memodifikasi objek Konfigurasi yang diberikan Android kepada Anda ( configdan newConfig). Anda harus membuat salinannya config = new Configuration(config);terlebih dahulu. Saya baru saja menghabiskan satu jam men-debug kode ini sebelum menemukan masalah (itu menyebabkan aktivitas flash tanpa henti).
imgx64
1
Teknik ini memiliki gangguan pada Android 7+ ketika memulai aplikasi dari awal yang dingin, dengan ukuran font sistem diatur ke besar. Google telah mengubah cara updateConfiguration()kerjanya, jadi terkadang Aktivitas Anda akan ditampilkan dalam 2 bahasa yang berbeda (terutama dalam Dialog). Info lebih lanjut: stackoverflow.com/questions/39705739/…
Mr-IDE
61

Setelah tidur nyenyak, saya menemukan jawabannya di Web (pencarian Google sederhana pada baris berikut " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), ini dia:

tautan teks => tautan ini juga menunjukkan screenshotsapa yang terjadi!

Kepadatan adalah masalah di sini , saya perlu memiliki ini di AndroidManifest.xml

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

Yang paling penting adalah android: anyDensity = "true" .

Jangan lupa untuk menambahkan yang berikut ini di AndroidManifest.xmluntuk setiap aktivitas (untuk Android 4.1 dan di bawah):

android:configChanges="locale"

Versi ini diperlukan saat Anda membuat penjelasan untuk Android 4.2 (API level 17) di sini :

android:configChanges="locale|layoutDirection"
Hubert
sumber
3
Saya memiliki android: anyDensity = "false" karena saran dari Google (Strategi untuk Aplikasi Legacy - poin 9: developer.android.com/intl/fr/guide/practices/… ). Hati-hati kalau begitu!
Hubert
2
Teks pada tombol tidak berubah setelah saya mengubah lokal. Saya ingin tahu apakah ada solusi untuk menyegarkan semua widget yang berisi sumber daya string. Sekarang saya menggambar ulang teks dengan menggunakan setText (getString (R.string.button_label)) di OnRestart (). (Tentu saja itu berubah setelah restart Aplikasi.)
emeraldhieu
Anda dapat memanggil aktivitas buat ulangactivity.recreate()
Ke Kra
59

Di Android M solusi teratas tidak akan berfungsi. Saya telah menulis sebuah kelas pembantu untuk memperbaiki apa yang harus Anda panggil dari kelas Aplikasi Anda dan semua Kegiatan (Saya akan menyarankan membuat BaseActivity dan kemudian membuat semua Kegiatan mewarisi darinya.

Catatan : Ini juga akan mendukung arah tata letak RTL dengan benar.

Kelas pembantu:

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

Aplikasi:

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.
sumber
11
Ini harus diterima jawaban. Pastikan updateConfig harus dipanggil dari konstruktor bukan dari onCreate dari kelas aktivitas, saya melakukan kesalahan ini ..
Hitesh Sahu
1
itu tergantung pada bagaimana Anda menerapkan ini dalam program Anda. Jika Anda telah menimpa pada ConfigurationChanged () Anda tidak perlu memanggil .updateConfig () dan itulah sebabnya Anda mendapatkan kesalahan, karena itu dipanggil dua kali. jika Anda mengatakan bahwa hanya memanggil .setLocale () tidak berfungsi, ini karena Anda perlu menyegarkan UI Anda, baik dengan memanggil createate () pada aktivitas atau metode Anda sendiri dalam kegiatan / fragmen (saya lebih suka yang sebelumnya bahkan jika itu tidak mulus tetapi Anda tidak akan pernah khawatir tentang masalah)
RJFares
3
// impor android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; // correct namespace
Mehdi Rostami
1
Bukankah seharusnya BaseActivity()konstruktor memanggil super()?
LarsH
1
Apakah solusi ini secara implisit termasuk memasukkan <application android:name=".App">manifes? Bagaimana kalau menambahkan android:configChanges="layoutDirection|locale"ke setiap aktivitas di manifes?
LarsH
10

Ini untuk komentar saya pada jawaban Andrey tapi saya tidak bisa memasukkan kode dalam komentar.

Apakah Anda memilih aktivitas yang dipanggil dari aktivitas utama Anda? Anda dapat menempatkan ini di 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);
}
trgraglia
sumber
2
Maksud Anda jika saya ingin mengubah Bahasa saat runtime maka saya harus menyelesaikan kegiatan itu dan memulai kegiatan itu lagi untuk mendapatkan perubahan? apakah ada cara lain untuk mengubahnya karena tidak baik setiap kali menyelesaikan aktivitas dan memulainya lagi.
Shreyash Mahajan
1
lebih baik lakukanactivity.recreate()
Ke Kra
@trgradlia, saya ingin menggunakan kode di atas. Tolong beritahu saya, apa itu langPreference? Saya pikir kita sedang membandingkan lang yang baru saja berubah dan yang sebelumnya.
Mahen
1
Aktivitas memiliki recreate()metode, jadi gunakan recreate()di refresh()dalam kode Anda. Ini akan menghemat 3 baris kode Anda dalam refresh()metode
Inzimam Tariq IT
1
@InzimamTariqIT, Terima kasih atas tipnya ... Jawabannya hampir 7 tahun jadi saya akan membiarkannya apa adanya dan orang-orang dapat menemukan peningkatan Anda di komentar.
trgraglia
6

Saya tidak bisa menggunakan android: anyDensity = "true" karena objek dalam gim saya akan diposisikan sangat berbeda ... sepertinya ini juga cukup membantu:

// membuat lokal
Lokal lokal2 = Lokal baru (loc); 
Locale.setDefault (locale2);
Konfigurasi config2 = Konfigurasi baru ();
config2.locale = locale2;

// memperbarui lokal
mContext.getResources (). updateConfiguration (config2, null);
nikib3ro
sumber
Solusi ini terbaik bagi saya hanya menggunakan inicode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk
1

Jika Anda ingin memberi efek pada opsi menu untuk segera mengubah lokal. Anda harus melakukan ini.

//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
sumber
Tapi bukankah solusi ini membersihkan menu di setiap menu terbuka? Saya kira onMenuOpenedharus dipanggil hanya sekali, setelah perubahan lokal.
trante