이 문제를 해결하는 현재 방법에 대한 답변이 완료되지 않았으므로 완벽한 솔루션에 대한 지침을 제공하려고합니다. 빠진 것이 있거나 더 잘할 수 있으면 의견을 말하십시오.
일반 정보
먼저, 문제를 해결하고자하는 라이브러리가 있지만 모두 구식이거나 일부 기능이 없습니다.
또한 라이브러리를 작성하는 것이이 문제를 해결하는 좋은 방법이 아닐 수 있다고 생각합니다. 할 일이 많지 않기 때문에해야 할 일은 완전히 분리 된 것을 사용하는 것보다 기존 코드를 변경하는 것입니다. 따라서 다음 지침을 완성해야합니다.
내 솔루션은 주로 https://github.com/gunhansancar/ChangeLanguageExample ( localhost 에 의해 이미 연결된)을 기반으로 합니다. 내가 찾은 최고의 코드입니다. 일부 비고 :
- 필요에 따라 Android N (이상) 이하의 로케일을 변경하기 위해 다양한 구현을 제공합니다.
- 방법을 사용합니다
updateViews()
각 액티비티 를 사용하여 로케일을 변경 한 후 (보통 사용) 모든 문자열을 수동으로 업데이트 getString(id)
합니다.
- 언어 만 지원하며 지역 (국가) 및 변형 코드도 포함하는 완전한 로캘은 지원하지 않습니다.
선택한 로케일을 유지하는 부분을 분리하여 조금 변경했습니다 (아래 제안 된대로 별도로 수행 할 수도 있음).
해결책
솔루션은 다음 두 단계로 구성됩니다.
- 앱에서 사용할 로캘을 영구적으로 변경
- 앱을 다시 시작하지 않고 사용자 지정 로캘 세트를 사용하도록 설정
1 단계 : 로캘 변경
gunhansancar의 LocaleHelper를LocaleHelper
기반으로 클래스를 사용하십시오 .
- 사용 가능한 언어로
ListPreference
a 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 라이브러리 와 유사한 파서를 사용할 수 있습니다 (지역 코드는 지원하지만 변형 코드는 지원하지 않음).
- 누군가가 좋은 파서를 찾거나 작성한 경우 솔루션에 주석을 포함시킬 수 있도록 주석을 추가하십시오.