เนื่องจากไม่มีคำตอบที่สมบูรณ์สำหรับวิธีแก้ปัญหานี้ในปัจจุบันฉันจึงพยายามให้คำแนะนำสำหรับวิธีแก้ปัญหาที่สมบูรณ์ โปรดแสดงความคิดเห็นหากมีบางอย่างขาดหายไปหรือสามารถทำได้ดีกว่านี้
ข้อมูลทั่วไป
ประการแรกมีห้องสมุดบางแห่งที่ต้องการแก้ปัญหา แต่ดูเหมือนว่าล้าสมัยหรือไม่มีคุณสมบัติบางอย่าง:
นอกจากนี้ฉันคิดว่าการเขียนไลบรารีอาจไม่ใช่วิธีที่ดี / ง่ายในการแก้ปัญหานี้เนื่องจากไม่มีอะไรให้ทำมากนักและสิ่งที่ต้องทำคือการเปลี่ยนรหัสที่มีอยู่แทนที่จะใช้บางสิ่งที่แยกออกจากกันโดยสิ้นเชิง ดังนั้นฉันจึงรวบรวมคำแนะนำต่อไปนี้ที่ควรจะสมบูรณ์
วิธีแก้ปัญหาของฉันขึ้นอยู่กับ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;
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, "");
    }
    
    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 {
                
                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;
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_LANGUAGE = "pref_key_language";
    public SettingsFragment() {
        
    }
    @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(); 
                break;
        }
    }
    @Override
    public void onResume() {
        super.onResume();
        
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }
    @Override
    public void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}
สร้างทรัพยากรที่แสดงรายการโลแคลlocales.xmlทั้งหมดพร้อมคำแปลที่มีด้วยวิธีต่อไปนี้ ( รายการรหัสสถานที่ ):
<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> 
        <item>@string/default_locale</item> 
        <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>
<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;
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 (ซึ่งรองรับภูมิภาค แต่ไม่มีรหัสตัวแปร)
 - 
- หากมีคนพบหรือเขียนโปรแกรมแยกวิเคราะห์ที่ดีให้เพิ่มความคิดเห็นเพื่อให้ฉันสามารถรวมไว้ในโซลูชัน