Vì không có câu trả lời nào cho cách giải quyết vấn đề hiện tại, tôi cố gắng đưa ra hướng dẫn cho một giải pháp hoàn chỉnh. Hãy bình luận nếu thiếu một cái gì đó hoặc có thể được thực hiện tốt hơn.
Thông tin chung
Đầu tiên, tồn tại một số thư viện muốn giải quyết vấn đề nhưng tất cả chúng dường như đã lỗi thời hoặc thiếu một số tính năng:
Hơn nữa tôi nghĩ rằng viết thư viện có thể không phải là một cách tốt / dễ dàng để giải quyết vấn đề này bởi vì không có nhiều việc phải làm, và những gì phải làm là thay đổi mã hiện có hơn là sử dụng một cái gì đó hoàn toàn tách rời. Vì vậy, tôi đã soạn các hướng dẫn sau đây cần được hoàn thành.
Giải pháp của tôi chủ yếu dựa trên https://github.com/gunhansancar/ChangeL LanguageExample (như đã được liên kết bởi localhost ). Đó là mã tốt nhất tôi tìm thấy để định hướng tại. Một số nhận xét:
- Khi cần thiết, nó cung cấp các triển khai khác nhau để thay đổi ngôn ngữ cho Android N (trở lên) trở xuống
- Nó sử dụng một phương thức
updateViews()
trong mỗi Hoạt động để cập nhật thủ công tất cả các chuỗi sau khi thay đổi ngôn ngữ (sử dụng thông thường getString(id)
) không cần thiết trong cách tiếp cận được hiển thị bên dưới
- Nó chỉ hỗ trợ các ngôn ngữ và không hoàn thành các ngôn ngữ (bao gồm cả mã vùng (quốc gia) và mã biến thể)
Tôi đã thay đổi nó một chút, tách rời phần vẫn tồn tại miền địa phương đã chọn (như người ta có thể muốn làm điều đó một cách riêng biệt, như được đề xuất dưới đây).
Giải pháp
Giải pháp bao gồm hai bước sau:
- Thay đổi vĩnh viễn ngôn ngữ được sử dụng bởi ứng dụng
- Làm cho ứng dụng sử dụng bộ ngôn ngữ tùy chỉnh mà không cần khởi động lại
Bước 1: Thay đổi ngôn ngữ
Sử dụng lớp LocaleHelper
, dựa trên LocaleHelper của gunhansancar :
- Thêm một
ListPreference
trong một PreferenceFragment
với các ngôn ngữ có sẵn (phải được duy trì khi ngôn ngữ nên được thêm vào sau)
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;
}
}
Tạo một cái SettingsFragment
như sau:
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);
}
}
Tạo tài nguyên locales.xml
liệt kê tất cả các địa điểm với các bản dịch có sẵn theo cách sau ( danh sách mã địa phương ):
<!-- 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>
Trong phần của PreferenceScreen
bạn, bạn có thể sử dụng phần sau để cho phép người dùng chọn ngôn ngữ có sẵn:
<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>
trong đó sử dụng các chuỗi sau từ 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>
Bước 2: Làm cho ứng dụng sử dụng ngôn ngữ tùy chỉnh
Bây giờ hãy thiết lập từng Hoạt động để sử dụng bộ ngôn ngữ tùy chỉnh. Cách dễ nhất để thực hiện điều này là có một lớp cơ sở chung cho tất cả các hoạt động với mã sau (nơi chứa mã quan trọng attachBaseContext(Context base)
và 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();
}
}
}
Những gì nó làm là
- Ghi đè
attachBaseContext(Context base)
để sử dụng ngôn ngữ đã tồn tại trước đó vớiLocaleHelper
- Phát hiện thay đổi ngôn ngữ và tạo lại Activity để cập nhật chuỗi của nó
Ghi chú về giải pháp này
Tái tạo một Hoạt động không cập nhật tiêu đề của ActionBar (như đã thấy ở đây: https://github.com/gunhansancar/ChangeL LanguageExample /issues / 1 ).
- Điều này có thể đạt được bằng cách đơn giản là có một
setTitle(R.string.mytitle)
trong các onCreate()
phương pháp của từng hoạt động.
Nó cho phép người dùng chọn ngôn ngữ mặc định của hệ thống, cũng như miền địa phương mặc định của ứng dụng (có thể được đặt tên, trong trường hợp này là "Tiếng Anh").
Chỉ có mã ngôn ngữ, không có vùng (quốc gia) và mã biến thể (như fr-rCA
) được hỗ trợ cho đến nay. Để hỗ trợ các đặc tả địa phương đầy đủ, có thể sử dụng trình phân tích cú pháp tương tự như trong thư viện Ngôn ngữ Android (hỗ trợ vùng nhưng không có mã biến thể).
- Nếu ai đó tìm thấy hoặc đã viết một trình phân tích cú pháp tốt, hãy thêm một nhận xét để tôi có thể đưa nó vào giải pháp.