최근에 context.getResources (). updateConfiguration () 은 Android API 25에서 더 이상 사용되지 않으며 컨텍스트를 사용하는 것이 좋습니다. 대신 createConfigurationContext () .
누구든지 createConfigurationContext 를 사용하여 Android 시스템 로케일을 재정의 하는 방법을 알고 있습니까?
이 작업을 수행하기 전에 :
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
답변
Calligraphy 에서 영감을 받아 컨텍스트 래퍼를 만들었습니다. 제 경우에는 앱 사용자에게 앱 언어 변경 옵션을 제공하기 위해 시스템 언어를 덮어 써야하지만 구현해야하는 모든 로직으로 사용자 지정할 수 있습니다.
import android.annotation.TargetApi;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Build;
import java.util.Locale;
public class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
super(base);
}
@SuppressWarnings("deprecation")
public static ContextWrapper wrap(Context context, String language) {
Configuration config = context.getResources().getConfiguration();
Locale sysLocale = null;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
sysLocale = getSystemLocale(config);
} else {
sysLocale = getSystemLocaleLegacy(config);
}
if (!language.equals("") && !sysLocale.getLanguage().equals(language)) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setSystemLocale(config, locale);
} else {
setSystemLocaleLegacy(config, locale);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context = context.createConfigurationContext(config);
} else {
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
return new MyContextWrapper(context);
}
@SuppressWarnings("deprecation")
public static Locale getSystemLocaleLegacy(Configuration config){
return config.locale;
}
@TargetApi(Build.VERSION_CODES.N)
public static Locale getSystemLocale(Configuration config){
return config.getLocales().get(0);
}
@SuppressWarnings("deprecation")
public static void setSystemLocaleLegacy(Configuration config, Locale locale){
config.locale = locale;
}
@TargetApi(Build.VERSION_CODES.N)
public static void setSystemLocale(Configuration config, Locale locale){
config.setLocale(locale);
}
}
래퍼를 삽입하려면 모든 활동에 다음 코드를 추가하십시오.
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(MyContextWrapper.wrap(newBase,"fr"));
}
UPDATE 23/09/2020
예를 들어 다크 모드를 적용하기 위해 앱 테마를 재정의하는 경우 ContextThemeWrapper는 언어 설정을 중단하므로 원하는 로케일을 재설정하기 위해 활동에 다음 코드를 추가합니다.
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
Locale locale = new Locale("fr");
overrideConfiguration.setLocale(locale);
super.applyOverrideConfiguration(overrideConfiguration);
}
업데이트 2018 년 10 월 19 일
때때로 방향 변경 또는 활동 일시 중지 / 재개 후 Configuration 개체가 기본 시스템 구성으로 재설정되고 결과적으로 프랑스어 “fr”로케일로 컨텍스트를 래핑하더라도 앱에 영어 “en”텍스트가 표시됩니다. . 따라서 좋은 방법으로 활동 또는 조각의 전역 변수에 Context / Activity 개체를 유지하지 마십시오.
또한 MyBaseFragment 또는 MyBaseActivity에서 다음을 만들고 사용합니다.
public Context getMyContext(){
return MyContextWrapper.wrap(getContext(),"fr");
}
이 연습은 100 % 버그없는 솔루션을 제공합니다.
답변
아마 다음과 같습니다.
Configuration overrideConfiguration = getBaseContext().getResources().getConfiguration();
overrideConfiguration.setLocales(LocaleList);
Context context = createConfigurationContext(overrideConfiguration);
Resources resources = context.getResources();
보너스 : createConfigurationContext ()를 사용하는 블로그 기사
답변
Calligraphy & Mourjan & 나 자신에게 영감을 받아 이것을 만들었습니다.
먼저 Application의 하위 클래스를 만들어야합니다.
public class MyApplication extends Application {
private Locale locale = null;
@Override
public void onCreate() {
super.onCreate();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
Configuration config = getBaseContext().getResources().getConfiguration();
String lang = preferences.getString(getString(R.string.pref_locale), "en");
String systemLocale = getSystemLocale(config).getLanguage();
if (!"".equals(lang) && !systemLocale.equals(lang)) {
locale = new Locale(lang);
Locale.setDefault(locale);
setSystemLocale(config, locale);
updateConfiguration(config);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (locale != null) {
setSystemLocale(newConfig, locale);
Locale.setDefault(locale);
updateConfiguration(newConfig);
}
}
@SuppressWarnings("deprecation")
private static Locale getSystemLocale(Configuration config) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return config.getLocales().get(0);
} else {
return config.locale;
}
}
@SuppressWarnings("deprecation")
private static void setSystemLocale(Configuration config, Locale locale) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale);
} else {
config.locale = locale;
}
}
@SuppressWarnings("deprecation")
private void updateConfiguration(Configuration config) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getBaseContext().createConfigurationContext(config);
} else {
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
}
}
}
그런 다음이를 AndroidManifest.xml 애플리케이션 태그로 설정해야합니다.
<application
...
android:name="path.to.your.package.MyApplication"
>
이를 AndroidManifest.xml 활동 태그에 추가하십시오.
<activity
...
android:configChanges="locale"
>
pref_locale은 다음과 같은 문자열 리소스입니다.
<string name="pref_locale">fa</string>
pref_locale이 설정되지 않은 경우 하드 코드 “en”은 기본 언어입니다.
답변
100 % 작동하는 솔루션은 없습니다. 당신은 모두를 사용해야 createConfigurationContext
하고 applyOverrideConfiguration
. 그렇지 않으면 baseContext
모든 활동을 새 구성으로 바꾸더라도 활동은 이전 로케일로 Resources
from ContextThemeWrapper
을 계속 사용 합니다.
그래서 여기에 API 29까지 작동하는 내 솔루션이 있습니다.
클래스를 다음 MainApplication
에서 하위 클래스로 분류하십시오 .
abstract class LocalApplication : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(
base.toLangIfDiff(
PreferenceManager
.getDefaultSharedPreferences(base)
.getString("langPref", "sys")!!
)
)
}
}
또한 모든 Activity
출처 :
abstract class LocalActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(
PreferenceManager
.getDefaultSharedPreferences(base)
.getString("langPref", "sys")!!
)
}
override fun applyOverrideConfiguration(overrideConfiguration: Configuration) {
super.applyOverrideConfiguration(baseContext.resources.configuration)
}
}
LocaleExt.kt
다음 확장 기능으로 추가 :
const val SYSTEM_LANG = "sys"
const val ZH_LANG = "zh"
const val SIMPLIFIED_CHINESE_SUFFIX = "rCN"
private fun Context.isAppLangDiff(prefLang: String): Boolean {
val appConfig: Configuration = this.resources.configuration
val sysConfig: Configuration = Resources.getSystem().configuration
val appLang: String = appConfig.localeCompat.language
val sysLang: String = sysConfig.localeCompat.language
return if (SYSTEM_LANG == prefLang) {
appLang != sysLang
} else {
appLang != prefLang
|| ZH_LANG == prefLang
}
}
fun Context.toLangIfDiff(lang: String): Context =
if (this.isAppLangDiff(lang)) {
this.toLang(lang)
} else {
this
}
@Suppress("DEPRECATION")
fun Context.toLang(toLang: String): Context {
val config = Configuration()
val toLocale = langToLocale(toLang)
Locale.setDefault(toLocale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(toLocale)
val localeList = LocaleList(toLocale)
LocaleList.setDefault(localeList)
config.setLocales(localeList)
} else {
config.locale = toLocale
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLayoutDirection(toLocale)
this.createConfigurationContext(config)
} else {
this.resources.updateConfiguration(config, this.resources.displayMetrics)
this
}
}
/**
* @param toLang - two character representation of language, could be "sys" - which represents system's locale
*/
fun langToLocale(toLang: String): Locale =
when {
toLang == SYSTEM_LANG ->
Resources.getSystem().configuration.localeCompat
toLang.contains(ZH_LANG) -> when {
toLang.contains(SIMPLIFIED_CHINESE_SUFFIX) ->
Locale.SIMPLIFIED_CHINESE
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
Locale(ZH_LANG, "Hant")
else ->
Locale.TRADITIONAL_CHINESE
}
else -> Locale(toLang)
}
@Suppress("DEPRECATION")
private val Configuration.localeCompat: Locale
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.locales.get(0)
} else {
this.locale
}
res/values/arrays.xml
지원되는 언어에 배열을 추가하십시오 .
<string-array name="lang_values" translatable="false">
<item>sys</item> <!-- System default -->
<item>ar</item>
<item>de</item>
<item>en</item>
<item>es</item>
<item>fa</item>
...
<item>zh</item> <!-- Traditional Chinese -->
<item>zh-rCN</item> <!-- Simplified Chinese -->
</string-array>
다음과 같이 언급하고 싶습니다.
config.setLayoutDirection(toLocale);
아랍어, 페르시아어 등과 같은 RTL 로케일을 사용할 때 레이아웃 방향을 변경하는 데 사용 합니다."sys"
코드에서 “시스템 기본 언어 상속”을 의미하는 값입니다.- 여기서 “langPref”는 사용자의 현재 언어를 입력하는 기본 설정 키입니다.
- 이미 필요한 로케일을 사용하는 경우 컨텍스트를 다시 작성할 필요가 없습니다.
ContextWraper
여기에 게시 된대로 필요하지 않습니다.createConfigurationContext
baseContext로 반환 된 새 컨텍스트를 설정 하기 만하면됩니다.- 이건 매우 중요합니다! 호출
createConfigurationContext
할 때 처음부터 생성 된 구성 을Locale
속성 세트 로만 전달해야합니다 . 이 구성에 설정된 다른 속성이 없어야합니다. 이 구성에 대해 다른 속성 ( 예 : 방향) 을 설정하면 해당 속성을 영원히 재정의 하고 화면을 회전하더라도 컨텍스트가 더 이상이 방향 속성을 변경하지 않기 때문 입니다. recreate
사용자가 다른 언어를 선택할 때만 활동하는 것만으로는 충분하지 않습니다. applicationContext는 이전 로케일로 유지되고 예상치 못한 동작을 제공 할 수 있기 때문입니다. 따라서 환경 설정 변경을 듣고 대신 전체 응용 프로그램 작업을 다시 시작하십시오.
fun Context.recreateTask() {
this.packageManager
.getLaunchIntentForPackage(context.packageName)
?.let { intent ->
val restartIntent = Intent.makeRestartActivityTask(intent.component)
this.startActivity(restartIntent)
Runtime.getRuntime().exit(0)
}
}
답변
다음은 약간의 kotlin 장점이있는 @ bassel-mourjan의 솔루션입니다. 🙂
import android.annotation.TargetApi
import android.content.ContextWrapper
import android.os.Build
import java.util.*
@Suppress("DEPRECATION")
fun ContextWrapper.wrap(language: String): ContextWrapper {
val config = baseContext.resources.configuration
val sysLocale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.getSystemLocale()
} else {
this.getSystemLocaleLegacy()
}
if (!language.isEmpty() && sysLocale.language != language) {
val locale = Locale(language)
Locale.setDefault(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.setSystemLocale(locale)
} else {
this.setSystemLocaleLegacy(locale)
}
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val context = baseContext.createConfigurationContext(config)
ContextWrapper(context)
} else {
baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics)
ContextWrapper(baseContext)
}
}
@Suppress("DEPRECATION")
fun ContextWrapper.getSystemLocaleLegacy(): Locale {
val config = baseContext.resources.configuration
return config.locale
}
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.getSystemLocale(): Locale {
val config = baseContext.resources.configuration
return config.locales[0]
}
@Suppress("DEPRECATION")
fun ContextWrapper.setSystemLocaleLegacy(locale: Locale) {
val config = baseContext.resources.configuration
config.locale = locale
}
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.setSystemLocale(locale: Locale) {
val config = baseContext.resources.configuration
config.setLocale(locale)
}
사용 방법은 다음과 같습니다.
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(ContextWrapper(newBase).wrap(defaultLocale.language))
}
답변
contextWrapper를 사용한 간단한 솔루션이 있습니다. Android N 프로그래밍 방식으로 언어 변경
recreate () 메서드에주의
답변
이 시도:
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.createConfigurationContext(config);
