Karena tidak ada jawaban yang lengkap untuk cara saat ini untuk menyelesaikan masalah ini, saya mencoba memberikan instruksi untuk solusi yang lengkap. Berikan komentar jika ada yang hilang atau bisa dilakukan dengan lebih baik.
Informasi Umum
Pertama, ada beberapa perpustakaan yang ingin menyelesaikan masalah tetapi mereka semua tampaknya sudah ketinggalan zaman atau kehilangan beberapa fitur:
Lebih lanjut saya pikir menulis perpustakaan mungkin bukan cara yang baik / mudah untuk memecahkan masalah ini karena tidak banyak yang harus dilakukan, dan apa yang harus dilakukan adalah mengubah kode yang ada daripada menggunakan sesuatu yang sepenuhnya dipisahkan. Karena itu saya menyusun instruksi berikut yang harus lengkap.
Solusi saya terutama didasarkan pada https://github.com/gunhansancar/ChangeLanguageExample (sebagaimana telah ditautkan oleh localhost ). Ini adalah kode terbaik yang saya temukan untuk orientasi. Beberapa komentar:
- Sebagaimana diperlukan, ia menyediakan implementasi yang berbeda untuk mengubah lokal untuk Android N (dan di atas) dan di bawah
- Ini menggunakan metode
updateViews()
di setiap Aktivitas untuk memperbarui semua string secara manual setelah mengubah lokal (menggunakan yang biasagetString(id)
) yang tidak diperlukan dalam pendekatan yang ditunjukkan di bawah ini
- Ini hanya mendukung bahasa dan tidak menyelesaikan lokal (yang juga mencakup wilayah (negara) dan kode varian)
Saya mengubahnya sedikit, memisahkan bagian yang tetap ada lokal yang dipilih (karena orang mungkin ingin melakukannya secara terpisah, seperti yang disarankan di bawah).
Larutan
Solusinya terdiri dari dua langkah berikut:
- Ubah secara permanen lokal yang akan digunakan oleh aplikasi
- Buat aplikasi menggunakan set lokal kustom, tanpa memulai ulang
Langkah 1: Ubah lokal
Gunakan kelas LocaleHelper
, berdasarkan LocaleHelper gunhansancar :
- Tambahkan
ListPreference
dalam PreferenceFragment
dengan bahasa yang tersedia (harus dipertahankan ketika bahasa harus ditambahkan kemudian)
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;
}
}
Buat SettingsFragment
seperti berikut:
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);
}
}
Buat sumber daya yang locales.xml
mencantumkan semua lokal dengan terjemahan yang tersedia dengan cara berikut ( daftar kode lokal ):
<!-- 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>
Di Anda, PreferenceScreen
Anda dapat menggunakan bagian berikut untuk membiarkan pengguna memilih bahasa yang tersedia:
<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>
yang menggunakan string berikut dari 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>
Langkah 2: Buat aplikasi menggunakan lokal kustom
Sekarang atur setiap Aktivitas untuk menggunakan set lokal kustom. Cara termudah untuk mencapai ini adalah memiliki kelas dasar yang sama untuk semua kegiatan dengan kode berikut (di mana kode penting berada di attachBaseContext(Context base)
dan 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();
}
}
}
Apa yang dilakukannya adalah
- Override
attachBaseContext(Context base)
untuk menggunakan lokal yang sebelumnya bertahanLocaleHelper
- Mendeteksi perubahan lokal dan menciptakan kembali Aktivitas untuk memperbarui string-nya
Catatan tentang solusi ini
Menciptakan sebuah Aktivitas tidak memperbarui judul ActionBar (seperti yang telah diamati di sini: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).
- Hal ini dapat dicapai dengan hanya memiliki
setTitle(R.string.mytitle)
dalam onCreate()
metode masing-masing kegiatan.
Ini memungkinkan pengguna memilih lokal default sistem, serta lokal default aplikasi (yang dapat dinamai, dalam hal ini "Bahasa Inggris").
Hanya kode bahasa, tidak ada wilayah (negara) dan kode varian (seperti fr-rCA
) yang didukung sejauh ini. Untuk mendukung spesifikasi lokal lengkap, parser yang mirip dengan yang ada di pustaka Android-Bahasa dapat digunakan (yang mendukung wilayah tetapi tidak ada kode varian).
- Jika seseorang menemukan atau telah menulis parser yang bagus, tambahkan komentar sehingga saya dapat memasukkannya dalam solusi.