ตั้งค่าสถานที่โดยทางโปรแกรม


150

แอปของฉันรองรับ 3 ภาษา (เร็ว ๆ นี้ 4) เนื่องจากหลายภาษามีความคล้ายคลึงกันฉันจึงต้องการให้ผู้ใช้มีตัวเลือกในการเปลี่ยนภาษาในแอปพลิเคชันของฉันเช่นคนอิตาลีอาจชอบภาษาสเปนมากกว่าภาษาอังกฤษ

มีวิธีให้ผู้ใช้เลือกระหว่างภาษาที่พร้อมใช้งานสำหรับแอปพลิเคชันแล้วเปลี่ยนภาษาที่ใช้หรือไม่ ฉันไม่เห็นว่าเป็นปัญหาในการตั้งค่าโลแคลสำหรับแต่ละกิจกรรมเนื่องจากเป็นงานง่ายๆที่ต้องดำเนินการในคลาสพื้นฐาน


หากคุณต้องการวิธีคืนค่าภาษาเริ่มต้นในภายหลังหรือหากคุณต้องการค่ากำหนดภาษาที่มีรายการภาษาและหากคุณต้องการเปลี่ยนภาษาให้สะดวกยิ่งขึ้นสิ่งนี้อาจเป็นประโยชน์: github.com/delight-im/Android
กรง

คำตอบ:


136

สำหรับผู้ที่ยังคงมองหาคำตอบนี้เนื่องจากconfiguration.localeเลิกใช้ API 24 แล้วตอนนี้คุณสามารถใช้:

configuration.setLocale(locale);

พิจารณาว่า minSkdVersion สำหรับวิธีนี้คือ API 17

โค้ดตัวอย่างแบบเต็ม:

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

อย่าลืมว่าหากคุณเปลี่ยนภาษาด้วยกิจกรรมที่กำลังทำงานอยู่คุณจะต้องเริ่มต้นใหม่เพื่อให้การเปลี่ยนแปลงมีผล

แก้ไขวันที่ 11 พฤษภาคม 2561

จากโพสต์ของ @ CookieMonster คุณอาจมีปัญหาในการรักษาการเปลี่ยนแปลงภาษาในเวอร์ชัน API ที่สูงขึ้น ในกรณีนี้ให้เพิ่มรหัสต่อไปนี้ในกิจกรรมพื้นฐานของคุณเพื่อให้คุณอัปเดตบริบทโลแคลในทุกการสร้างกิจกรรม:

@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_MR1)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = new 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;
}

หากคุณใช้สิ่งนี้อย่าลืมบันทึกภาษาเป็น SharedPreferences เมื่อคุณตั้งค่าภาษาด้วย setLocate(locale)

แก้ไข 7 เมษายน 2020

คุณอาจประสบปัญหาใน Android 6 และ 7 และสิ่งนี้เกิดขึ้นเนื่องจากปัญหาในไลบรารี androidx ขณะจัดการโหมดกลางคืน สำหรับสิ่งนี้คุณจะต้องลบล้างapplyOverrideConfigurationกิจกรรมพื้นฐานของคุณและอัปเดตโลแคลของการกำหนดค่าในกรณีที่มีการสร้างโลแคลใหม่ขึ้นมา

โค้ดตัวอย่าง:

@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
        // update overrideConfiguration with your locale  
        setLocale(overrideConfiguration) // you will need to implement this
    }
    super.applyOverrideConfiguration(overrideConfiguration);
} 

2
สิ่งนี้ใช้ได้กับกิจกรรม แต่มีวิธีอัปเดตบริบทของแอปพลิเคชันหรือไม่
alekop

3
หลังจากเปลี่ยนandroidx.appcompat:appcompat:รุ่นจาก1.0.2การ1.1.0ไม่ได้ทำงานเกี่ยวกับหุ่นยนต์ 7 แต่การทำงานเกี่ยวกับหุ่นยนต์ 9.
เบค

4
ปัญหาเดียวกันสำหรับฉันและ1.1.0androidx
Alexander Dadukin

2
ปัญหาเดียวกันสำหรับฉัน หลังจากที่ฉันเปลี่ยนเป็น androidx.appcompat: appcompat: 1.1.0 'lib
Rahul Jidge

4
ปัญหาappcompat:1.1.0สามารถแก้ไขได้ด้วยappcompat:1.2.0-alpha02และรหัสSet<Locale> set = new LinkedHashSet<>(); // bring the target locale to the front of the list set.add(locale); LocaleList all = LocaleList.getDefault(); for (int i = 0; i < all.size(); i++) { // append other locales supported by the user set.add(all.get(i)); } Locale[] locales = set.toArray(new Locale[0]); configuration.setLocales(new LocaleList(locales));ภายใน@TargetApi(Build.VERSION_CODES.N) updateResourcesLocale()
Vojtech Pohl

180

หวังว่าจะช่วยได้ (ใน onResume):

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

2
อย่างนี้ต้องตั้งค่าสำหรับทุกกิจกรรม?
Tobias

6
1. จำเป็นต้องใช้ getBaseContext () หรือควรใช้แอปพลิเคชัน contex ดีกว่า? 2. ควรเรียกรหัสนี้ในแต่ละกิจกรรม? ขอบคุณ.
พอล

10
ฉันวางรหัสนี้ไว้ใน onCreate () ของกิจกรรมตัวเรียกใช้งานของฉัน (และไม่มีที่ไหนเลย) และรู้สึกประหลาดใจอย่างมากที่เห็นว่าภาษานั้นใช้กับแอปทั้งหมด นี่คือในแอปที่กำหนดเป้าหมาย 4.3 ด้วย minSDK ที่ 14 (ICS)
IAmKale

8
ไม่จำเป็นต้องสร้างวัตถุ Configuration ใหม่ คุณสามารถใช้การกำหนดค่าปัจจุบันและอัปเดตได้: getResources (). getConfiguration ()
jmart

1
อย่าใช้การกำหนดค่าใหม่ (); มันเปลี่ยน textAppearance, fontSize
Jemshit Iskenderov

23

ฉันมีปัญหากับการตั้งค่าภาษาโดยใช้โปรแกรมกับอุปกรณ์ที่มีAndroid OS N ขึ้นไป สำหรับฉันวิธีแก้ปัญหาคือการเขียนโค้ดนี้ในกิจกรรมพื้นฐานของฉัน:

(หากคุณไม่มีกิจกรรมพื้นฐานคุณควรทำการเปลี่ยนแปลงเหล่านี้ในกิจกรรมทั้งหมดของคุณ)

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

โปรดทราบว่าที่นี่ไม่เพียงพอที่จะโทร

createConfigurationContext(configuration)

คุณต้องได้รับบริบทที่วิธีนี้ส่งคืนจากนั้นจึงจะตั้งค่าบริบทนี้ในattachBaseContextวิธีการ


นี่เป็นวิธีแก้ปัญหาที่ง่ายที่สุดและได้ผล! นี่ควรเป็นคำตอบที่ได้รับการยอมรับ
Prasad Pawar

3
รหัสนี้ใช้ได้ดีกับ Android ที่สูงกว่า 7 แต่ในเวอร์ชันที่ต่ำกว่า N ไม่ทำงาน คุณมีการแก้ปัญหาใด ๆ?
Matin Ashtiani

ไม่แน่ใจว่าเป็นเพราะทำงานให้ฉัน คุณต้องการส่งการใช้งานของคุณให้ฉันเพื่อให้ฉันดูไหม
CookieMonster

4
ไม่ทำงานในเวอร์ชันภายใต้ Android N เนื่องจากต้องเรียก resource.updateConfiguration ใน onCreate () แทนที่จะเป็น attachBaseContext ()
Chandler

@Chandler พูดถูก สำหรับ Android 6- ให้เรียกupdateBaseContextLocaleใช้วิธีการในonCreateกิจกรรมหลัก / ฐานของคุณ
Azizjon Kholmatov

22

เนื่องจากไม่มีคำตอบที่สมบูรณ์สำหรับวิธีแก้ปัญหานี้ในปัจจุบันฉันจึงพยายามให้คำแนะนำสำหรับวิธีแก้ปัญหาที่สมบูรณ์ โปรดแสดงความคิดเห็นหากมีบางอย่างขาดหายไปหรือสามารถทำได้ดีกว่านี้

ข้อมูลทั่วไป

ประการแรกมีห้องสมุดบางแห่งที่ต้องการแก้ปัญหา แต่ดูเหมือนว่าล้าสมัยหรือไม่มีคุณสมบัติบางอย่าง:

  • https://github.com/delight-im/Android-Languages
    • ล้าสมัย (ดูปัญหา )
    • เมื่อเลือกภาษาใน UI ภาษาจะแสดงทุกภาษาเสมอ (ฮาร์ดโค้ดในไลบรารี) ไม่ใช่เฉพาะภาษาที่มีการแปล
  • https://github.com/akexorcist/Android-LocalizationActivity
    • ดูเหมือนค่อนข้างซับซ้อนและอาจใช้แนวทางที่คล้ายกันดังที่แสดงด้านล่าง

นอกจากนี้ฉันคิดว่าการเขียนไลบรารีอาจไม่ใช่วิธีที่ดี / ง่ายในการแก้ปัญหานี้เนื่องจากไม่มีอะไรให้ทำมากนักและสิ่งที่ต้องทำคือการเปลี่ยนรหัสที่มีอยู่แทนที่จะใช้บางสิ่งที่แยกออกจากกันโดยสิ้นเชิง ดังนั้นฉันจึงรวบรวมคำแนะนำต่อไปนี้ที่ควรจะสมบูรณ์

วิธีแก้ปัญหาของฉันขึ้นอยู่กับhttps://github.com/gunhansancar/ChangeLanguageExampleเป็นหลัก (ตามที่เชื่อมโยงโดยlocalhost แล้ว ) เป็นรหัสที่ดีที่สุดที่ฉันพบในการวางแนว ข้อสังเกตบางประการ:

  • ตามความจำเป็นมันมีการใช้งานที่แตกต่างกันเพื่อเปลี่ยนภาษาสำหรับ Android N (ขึ้นไป) และต่ำกว่า
  • ใช้วิธีการupdateViews()ในแต่ละกิจกรรมเพื่ออัปเดตสตริงทั้งหมดด้วยตนเองหลังจากเปลี่ยนโลแคล (โดยใช้ปกติgetString(id)) ซึ่งไม่จำเป็นในวิธีการที่แสดงด้านล่าง
  • รองรับเฉพาะภาษาและไม่สมบูรณ์ (ซึ่งรวมถึงภูมิภาค (ประเทศ) และรหัสตัวแปร)

ฉันเปลี่ยนมันเล็กน้อยแยกส่วนที่ยังคงอยู่ในสถานที่ที่เลือกไว้ (เนื่องจากอาจต้องการแยกต่างหากตามที่แนะนำด้านล่าง)

วิธีการแก้

โซลูชันประกอบด้วยสองขั้นตอนต่อไปนี้:

  • เปลี่ยนภาษาอย่างถาวรที่จะใช้โดยแอป
  • ทำให้แอปใช้ชุดภาษาที่กำหนดเองโดยไม่ต้องรีสตาร์ท

ขั้นตอนที่ 1: เปลี่ยนภาษา

ใช้คลาสLocaleHelperตามLocaleHelper ของ gunhansancar :

  • เพิ่มListPreferenceในPreferenceFragmentภาษาที่มีให้ (ต้องดูแลเมื่อควรเพิ่มภาษาในภายหลัง)
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;
    }
}

สร้างสิ่งSettingsFragmentต่อไปนี้:

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

สร้างทรัพยากรที่แสดงรายการโลแคลlocales.xmlทั้งหมดพร้อมคำแปลที่มีด้วยวิธีต่อไปนี้ ( รายการรหัสสถานที่ ):

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

ในส่วนของPreferenceScreenคุณคุณสามารถใช้ส่วนต่อไปนี้เพื่อให้ผู้ใช้เลือกภาษาที่ใช้ได้:

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

ซึ่งใช้สตริงต่อไปนี้จาก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>

ขั้นตอนที่ 2: ทำให้แอปใช้ภาษาที่กำหนดเอง

ตอนนี้ตั้งค่าแต่ละกิจกรรมเพื่อใช้ชุดภาษาที่กำหนดเอง วิธีที่ง่ายที่สุดในการบรรลุเป้าหมายนี้คือการมีคลาสพื้นฐานร่วมกันสำหรับกิจกรรมทั้งหมดด้วยรหัสต่อไปนี้ (โดยที่รหัสสำคัญอยู่ในattachBaseContext(Context base)และ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();
        }
    }
}

สิ่งที่ทำคือ

  • แทนที่attachBaseContext(Context base)เพื่อใช้ภาษาที่มีอยู่ก่อนหน้านี้ด้วยLocaleHelper
  • ตรวจจับการเปลี่ยนแปลงของโลแคลและสร้างกิจกรรมใหม่เพื่ออัพเดตสตริง

หมายเหตุเกี่ยวกับโซลูชันนี้

  • การสร้างกิจกรรมใหม่จะไม่อัปเดตชื่อของ ActionBar (ตามที่สังเกตแล้วที่นี่: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 )

    • นี้สามารถทำได้โดยเพียงแค่มีsetTitle(R.string.mytitle)ในonCreate()วิธีการของแต่ละกิจกรรม
  • ช่วยให้ผู้ใช้เลือกภาษาเริ่มต้นของระบบตลอดจนภาษาเริ่มต้นของแอป (ซึ่งในกรณีนี้อาจตั้งชื่อเป็น "ภาษาอังกฤษ")

  • fr-rCAจนถึงขณะนี้รองรับเฉพาะรหัสภาษาเท่านั้นยังไม่มีการสนับสนุนภูมิภาค (ประเทศ) และรหัสตัวแปร (เช่น) เพื่อรองรับข้อมูลจำเพาะของภาษาแบบเต็มสามารถใช้ตัวแยกวิเคราะห์ที่คล้ายกับในไลบรารี Android-Languages (ซึ่งรองรับภูมิภาค แต่ไม่มีรหัสตัวแปร)

    • หากมีคนพบหรือเขียนโปรแกรมแยกวิเคราะห์ที่ดีให้เพิ่มความคิดเห็นเพื่อให้ฉันสามารถรวมไว้ในโซลูชัน

1
ยอดเยี่ยม แต่เป็นราชาแห่งฝันร้าย
Odys

1
ไม่เลยแอพของฉันซับซ้อนเกินไปแล้ววิธีนี้จะเป็นฝันร้ายที่จะรักษาในอนาคต
Josh

@ Josh ช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหม จริงๆแล้วต้องเพิ่มเพียงไม่กี่บรรทัดในคลาสฐานกิจกรรมแต่ละคลาสที่คุณใช้ ฉันเห็นว่าอาจเป็นไปไม่ได้ที่จะใช้คลาสฐานเดียวกันสำหรับกิจกรรมทั้งหมด แต่โครงการที่ใหญ่กว่านั้นก็น่าจะเข้ากันได้กับบางส่วน การเขียนโปรแกรมเชิง Aspect สามารถช่วยได้ที่นี่ แต่การจัดองค์ประกอบ (ย้ายโค้ดจากattachBaseContext(Context base)และonResume()ไปยังคลาสแยก) สามารถทำเคล็ดลับได้ จากนั้นสิ่งที่คุณต้องทำคือประกาศหนึ่งอ็อบเจ็กต์ในคลาสฐานกิจกรรมแต่ละคลาสและมอบหมายการโทรทั้งสองนั้น
user905686

หากผู้ใช้เปลี่ยนโลแคลสามารถเปลี่ยนโลแคลของเพจกิจกรรมก่อนหน้าทั้งหมดได้หรือไม่
Raju yourPepe

นี่คือคำตอบที่ดีที่สุดในประเด็นนี้ ขอบคุณครับมันใช้งานได้
Alok Gupta

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

เพียงใช้วิธีการตัวช่วยนี้เพื่อบังคับเฉพาะภาษา

UDPATE 22 สิงหาคม 2017 ที่ดีกว่าการใช้วิธีการนี้


4

เพิ่มคลาสตัวช่วยด้วยวิธีการต่อไปนี้:

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

    }
}

และเรียกมันในกิจกรรมเริ่มต้นของคุณเช่นMainActivity.java:

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

3

ง่ายและสะดวก

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

โดยที่ "en" คือรหัสภาษาและ "US" คือรหัสประเทศ


ตามที่ระบุไว้ในโพสต์ของฉันจะเลิกและยังเป็นconf.locale=locale; updateConfiguration
Ricardo

ง่ายมากและซับซ้อนน้อยลง :)
Ramkesh Yadav

3

ฉันพบว่าandroidx.appcompat:appcompat:1.1.0ข้อบกพร่องสามารถแก้ไขได้โดยเพียงแค่โทรgetResources()เข้ามาapplyOverrideConfiguration()

@Override public void
applyOverrideConfiguration(Configuration cfgOverride)
{
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
      Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    // add this to fix androidx.appcompat:appcompat 1.1.0 bug
    // which happens on Android 6.x ~ 7.x
    getResources();
  }

  super.applyOverrideConfiguration(cfgOverride);
}

3

ใช้ได้กับ API16 ถึง API28 เพียงวางเมธอดนี้ไว้ที่:

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;

ใส่รหัสนี้ในกิจกรรมทั้งหมดของคุณโดยใช้:

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

หรือเรียก localeUpdateResources บนแฟรกเมนต์อะแด็ปเตอร์ ฯลฯ ที่คุณต้องการบริบทใหม่

เครดิต: Yaroslav Berezanskyi


2

มีวิธีง่ายๆสุด ๆ

ใน BaseActivity กิจกรรมหรือ Fragment แทนที่ attachBaseContext

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

ส่วนขยาย

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

1
 /**
 * 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
    }
}

1

สำหรับผู้ที่พยายามทุกอย่าง แต่ไม่ได้ผล กรุณาตรวจสอบว่าถ้าคุณตั้งค่าdarkmodeด้วยAppCompatDelegate.setDefaultNightModeและระบบจะไม่มืดแล้วConfiguration.setLocaleจะไม่ทำงานข้างต้นAndorid 7.0

เพิ่มรหัสนี้ในทุกกิจกรรมของคุณเพื่อแก้ปัญหานี้:

override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
  if (overrideConfiguration != null) {
    val uiMode = overrideConfiguration.uiMode
    overrideConfiguration.setTo(baseContext.resources.configuration)
    overrideConfiguration.uiMode = uiMode
  }
  super.applyOverrideConfiguration(overrideConfiguration)
}

0

ในปี 2020 การจัดการภาษากลายเป็นเรื่องง่าย! สิ่งที่คุณต้องทำคือ:

  1. เรียกร้องให้ Activity.applyOverrideConfiguration
  2. แล้วโทรไปที่ Locale.setDefault

คุณต้องเรียกสิ่งเหล่านั้นจากตัวสร้างกิจกรรมเนื่องจากคุณสามารถโทรไปได้applyOverrideConfigurationเพียงครั้งเดียวและระบบเรียกสิ่งนี้ว่าค่อนข้างเร็ว

และระวังแอพบันเดิล Google จะแยก APK ของคุณตามทรัพยากรภาษาโดยอัตโนมัติเมื่อใช้แอพบันเดิล ตรวจสอบ API ใหม่และการแก้ปัญหาที่นี่


ฉันสร้างคลาสผู้ช่วยเพื่อช่วยคุณ ในการใช้งานของฉันG.appคือบริบทของแอปพลิเคชัน นอกจากนี้ฉันต้องเข้าถึงทรัพยากรจากบริบทของแอปดังนั้นฉันจึงใช้Resคลาสนี้อันนี้เป็นทางเลือก แต่ฉันให้รหัสด้วย

การใช้งาน

public BaseActivity(){
    LanguageUtility.init(this);
}

public void changeLanguage(Local local){
    // you must recreat your activity after you call this
    LanguageUtillity.setDefaultLanguage(local, this);
}

รหัสแหล่งที่มา

public class LanguageUtility {

    private static Configuration configuration;

    public static void setDefaultLanguage(Locale locale, Context context) {
        Locale.setDefault(locale);

        context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE)
                .edit()
                .putString("language", locale.getLanguage())
                .putString("country", locale.getCountry())
                .putString("variant", locale.getVariant())
                .apply();

        configuration = createConfiguration(context);
        Res.updateContext();
    }

    /**
     * Used to update your app context in case you cache it.
     */
    public static Context createConfigurationContext(Context context) {
        return context.createConfigurationContext(getConfiguration(context));
    }

    public static void init(Activity activity) {
        activity.applyOverrideConfiguration(LanguageUtility.getConfiguration(G.app));
        // you can't access sharedPrefferences from activity constructor 
        // with activity context, so I used the app context.
        Locale.setDefault(getLocale(G.app));
    }

    @NotNull
    private static Configuration getConfiguration(Context context) {
        if (configuration == null) {
            configuration = createConfiguration(context);
        }
        return configuration;
    }

    @NotNull
    private static Configuration createConfiguration(Context context) {
        Locale locale = getLocale(context);
        Configuration configuration = new Configuration();
        configuration.setLocale(locale);
        LanguageUtility.configuration = configuration;
        return configuration;
    }

    @NotNull
    private static Locale getLocale(Context context) {
        Locale aDefault = Locale.getDefault();
        SharedPreferences preferences =
                context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE);
        String language = preferences.getString("language", aDefault.getLanguage());
        String country = preferences.getString("country", aDefault.getCountry());
        String variant = preferences.getString("variant", aDefault.getVariant());
        return new Locale(language, country, variant);
    }
}

คลาส Res ที่เป็นทางเลือก

public class Res {

    @SuppressLint("StaticFieldLeak")
    public static Context appLocalContext = LanguageUtility.createConfigurationContext(G.app);

    public static void updateContext() {
        appLocalContext = LanguageUtility.createConfigurationContext(G.app);
    }

    public static String getString(@StringRes int id, Object... formatArgs) {
        return appLocalContext.getResources().getString(id, formatArgs);
    }

    public static int getColor(@ColorRes int id) {
        return G.app.getColor(id);
    }
}

-1

ใส่รหัสนี้ในกิจกรรมของคุณ

 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();
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.