【发布时间】:2020-05-06 09:47:29
【问题描述】:
无论设备操作系统语言如何,我都需要强制应用程序区域设置满足我的需求,即:翻译的字符串,以及每个应用程序变体的布局 rtl/ltr。
我想知道如何正确地做到这一点。
正如我们所看到的,网络上有很多混乱,并且没有正式的答案。
【问题讨论】:
标签: android
无论设备操作系统语言如何,我都需要强制应用程序区域设置满足我的需求,即:翻译的字符串,以及每个应用程序变体的布局 rtl/ltr。
我想知道如何正确地做到这一点。
正如我们所看到的,网络上有很多混乱,并且没有正式的答案。
【问题讨论】:
标签: android
这样做最正确的方法是首先理解以下概念:
请注意: [Context.getResources] 层有 3 种类型:
因此,更改 Application 层不会影响 Activity 层。
还有:
如果您希望支持 Android 6,您应该使用 .apk 文件
而不是 .aab(Android 应用程序包)。
这是因为在 Android 6 中,您只能在设置中选择一种默认语言环境,然后 .aab 将仅下载所需的配置资源:
假设我们使用英文的 strings.xml 作为我们的默认语言,并使用其他 strings-iw.xml 将其翻译成希伯来语
如果 Android 6 设备配置为英语作为其主要且唯一的语言,.aab 将仅提供常规英语 strings.xml 资源。
或者:
如果可能,您可以只使用一个默认的 strings.xml 和所需的语言。
在您的 BaseActivity 和您的 Application 类中:
abstract class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
val constrainedBaseCtx = LocaleUtil.constrainConfigurationLocale(newBase)
super.attachBaseContext(constrainedBaseCtx)
}
override fun onConfigurationChanged(newConfig: Configuration) {
val constrainedConfiguration = LocaleUtil.constrainConfigurationLocale(newConfig)
super.onConfigurationChanged(constrainedConfiguration)
}
override fun createConfigurationContext(overrideConfiguration: Configuration): Context {
val constrainedConf = LocaleUtil.constrainConfigurationLocale(overrideConfiguration)
return super.createConfigurationContext(constrainedConf)
}
}
使用您的区域设置工具:
/**
* Helps to change locales configuration for [Context] objects.
* Remember that there are 3 types of [Context.getResources] layers:
*
* 1. Top-level resources (ex: manifest activity name)
*
* 2. Application resources
*
* 3. Activity resources
*
* So, changing the Application layer won't affect the activity layer.
*/
object LocaleUtil {
const val DEFAULT_LANGUAGE = "iw"
const val DEFAULT_COUNTRY = "il"
/**
* Constraint This [context] Locale.
* @param constrainedCountry - the country to match the activity for
* @param constrainedLanguage - the language inside that country to match the activity for
* @return new / same instance configured [Context]. (depends on Android OS version)
*/
fun constrainConfigurationLocale(
context: Context,
constrainedCountry: String = DEFAULT_COUNTRY,
constrainedLanguage: String = DEFAULT_LANGUAGE
) : Context {
val newConf = constrainConfigurationLocale(
context.resources.configuration,
constrainedCountry,
constrainedLanguage
)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
context.createConfigurationContext(newConf)
else
context
}
/**
* Constraint This [configuration] Locale.
* @param constrainedCountry - the country to match the activity for
* @param constrainedLanguage - the language inside that country to match the activity for
* @return new / same instance of [Configuration]. (depends on Android OS version)
*/
fun constrainConfigurationLocale(
currentConfiguration: Configuration,
constrainedCountry: String = DEFAULT_COUNTRY,
constrainedLanguage: String = DEFAULT_LANGUAGE
) : Configuration {
val configuration = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
Configuration(currentConfiguration)
else
currentConfiguration
val synthesizedLocale = Locale(constrainedLanguage, constrainedCountry)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val oldLocales = configuration.locales
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
if (!oldLocales.isEmpty) {
if (!oldLocales[0].language!!.contentEquals(synthesizedLocale.language)) {
var newLocales = arrayOfNulls<Locale>(oldLocales.size() + 1)
newLocales[0] = synthesizedLocale // first locale determines layout direction
var deductionCount = 0
for (i in 0 until oldLocales.size()) {
if (newLocales[0]?.language?.contentEquals(oldLocales[i].language) != true) // add only different locale if not null
newLocales[i + 1 - deductionCount] = oldLocales[i]
else {
val temp = arrayOfNulls<Locale>(newLocales.size - 1)
for (j in 0..i) {
temp[j] = newLocales[j]
}
newLocales = temp
deductionCount++
}
}
configuration.locales = LocaleList(*newLocales)
}
} else {
configuration.locales = LocaleList(synthesizedLocale)
}
} else {
@Suppress("DEPRECATION", "UNNECESSARY_NOT_NULL_ASSERTION")
if (configuration.locale == null || !synthesizedLocale.language!!.contentEquals(configuration.locale.language!!))
configuration.setLocale(synthesizedLocale)
}
return configuration
}
}
【讨论】: