Оскільки жодна відповідь не є повною для поточного способу вирішення цієї проблеми, я намагаюся дати інструкції для повного рішення. Будь ласка, прокоментуйте, якщо щось не вистачає чи можна зробити краще.
Загальна інформація
По-перше, існують деякі бібліотеки, які хочуть вирішити проблему, але всі вони здаються застарілими або не мають деяких функцій:
Далі я думаю, що написання бібліотеки може бути не дуже хорошим / простим способом вирішити цю проблему, тому що робити не дуже багато, і що потрібно зробити, це скоріше змінити існуючий код, ніж використовувати щось зовсім нерозв'язне. Тому я склав наступні інструкції, які мають бути повними.
Моє рішення в основному базується на 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/isissue/1 ).
- Цього можна досягти, просто
setTitle(R.string.mytitle)
використовуючи onCreate()
метод кожного виду діяльності.
Це дозволяє користувачеві вибрати локальну локальну систему системи, а також локальну програму за замовчуванням (яку можна назвати, в даному випадку "англійською").
fr-rCA
Наразі підтримуються лише мовні коди, жоден регіон (країна) та варіанти (подібні ). Для підтримки повних специфікацій локалі може бути використаний аналізатор, подібний до бібліотеки Android-мов (який підтримує регіони, але не має варіантів коду).
- Якщо хтось знайде або написав хороший аналізатор, додайте коментар, щоб я міг включити його у рішення.