Android 无Bug版 多语言设计方案!

出海业务为什么要做多语言?

1.市场扩大与本地化需求:

通过支持多种语言,出海项目可以触及更广泛的国际用户群体,进而扩大其市场份额。

本地化是吸引国际用户的重要策略之一,而语言本地化是其中的核心。使用用户的母语能够提供更好的用户体验,并增加用户黏性。

2.文化敏感性和尊重:

不同的语言往往代表着不同的文化和习惯。通过提供多语言支持,出海项目能够表现出对目标市场文化的敏感性和尊重,从而建立更好的品牌形象。

3.提高用户满意度和参与度:

使用用户的母语进行交流可以消除语言障碍,使用户更容易理解和使用产品。

这有助于提高用户的满意度和参与度,进而增加用户留存和转化率。

所以,出海应用适配多语言是业务开展过程中的重中之重,建议大家App都要实现多语言功能,感受其带来的好处。

多语言常见问题

  1. Android N版本适配问题

  2. 切换系统导航,更改深色模式导致多语言无法适配问题

  3. 系统授权弹窗导致ApplicationContext中的Local被还原

  4. 切换语言,系统通知栏显示不是当前设置的语言

  5. Service服务中Toast不适配

  6. 如何正确获取系统当前语言

  7. WebView第一次加载多语言不适配

功能虽不太难,但遇到的问题还是比较多的,下面给大家分享我们在用的多语言方案,有问题还希望大家积极指出。

无Bug版 多语言设计方案

1.多语言方案基本原理:

实现多国语言的原理是根据用户选择的语言或者手机系统设置的语言来加载相应的语言资源文件。当用户切换语言时,应用程序会重新加载对应语言的资源文件,从而显示相应的文本内容。

2.无Bug版 多语言设计方案实践

如果您应用内内置了所有的语言文本,则只需要根据系统语言切换即可,实现方式也较为简单。如果您只兼容几种语言,要根据用户选择来切换App语言,就较为复杂一些,下面我们分别来讲一下。

(1) 跟随系统切换

通过Android Studio,(当然如果知道语言的缩写可以直接在res目录下建立不同名称的values文件例如中文values-zh),右击res->New->Android Resource File 会打开一个新的弹窗,在File name 输入框填写strings,如下图在红色圈中区域找到Local 点击>> 后就可以选择相关的国语言,点击OK会在res目录下生成一个新的values-xx目录,接下来就可以在该目录的strings.xml 添加相关语言的文案即可

打开手机的设置,切换系统语言(例如你项目的默认语言是中文,你上一步添加的是values-en目录,切换语言到英文),这是重新打开App,你看到App上的显示的就是英文了。

(2) App 内设置切换语言

以上2个步骤就可以简单的实现了系统切换多语言功能,但是产品为了让用户更好的使用产品,App内会有一个设置切换语言的功能,让用户自己选择App内的语言(往往我们无法支持所有的语言)。

对于这种需求我们如何设计呢?

主要代码如下,文章末尾会给出github地址:

‍基类复写attachBaseContext设置上下文

abstract class BaseForLanguageActivity : Activity() {  override fun attachBaseContext(newBase: Context) {    super.attachBaseContext(LanguageUtil.wrap(newBase, LanguageUtil.getLocaleByLanguage()))}}

获取用户设置的默认语言,如果没有设置获取本地设备的默认语言(Android 7.0以及以上需要获取本地语言列表)

private fun getLanguage(): String {  val defaultLanguage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {    Resources.getSystem().configuration.locales[0].language  } else {    Locale.getDefault().language  }    return getInstance()[KEY_LANGUAGE, defaultLanguage]}

通过第二步获取的语言来判断设置的Local类型​​​​​​​

fun getLocaleByLanguage(): Locale {  val locale = if (getLanguage().equals(LanguageType.ENGLISH.language, ignoreCase = true)) {    Locale.ENGLISH  } else if (getLanguage().equals(LanguageType.BANGLADESH.language, ignoreCase = true)) {    Locale(LanguageType.BANGLADESH.language)  } else {    Locale(LanguageType.DEFAULT.language)  }  return locale}

根据Locale(Android 7.0以及以上需要设置本地语言列表)重新包装上下文Context,在Activity的attachBaseContext方法会用到​​​​​​​

fun wrap(context: Context, newLocale: Locale): Context {
  var context = context  val res = context.resources  val configuration = res.configuration  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {    configuration.setLocale(newLocale)    val localeList = LocaleList(newLocale)    LocaleList.setDefault(localeList)    configuration.setLocales(localeList)    context = context.createConfigurationContext(configuration)  } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {    configuration.setLocale(newLocale)    context = context.createConfigurationContext(configuration)  }  return ContextWrapper(context)}

保存用户设置的语言​​​​​​​

private fun setLanguage(language: String?) {  getInstance().put(KEY_LANGUAGE, language)}
fun saveChange(language: String) {  setLanguage(language)}

应用内获取string资源使用以下方法getString,确保每次获取的上下文里的Local是当前应该显示的语言​​​​​​​

object Extentions {  fun getString(@StringRes resId: Int) = getContext().getString(resId)    private fun getContext(): Context {    return LanguageUtil.getLanguageContext()  }}

LanguageContext是一个枚举类 通过这个可以获取相关语言的上下文,并获取相关语言下的资源文件​​​​​​​

enum class LanguageContext(val languageType: LanguageType) {  DEFAULT(LanguageType.DEFAULT) {    private var context: Context = LanguageUtil.wrap(Extentions.getApp(), Locale(languageType.language))        override fun getContext(): Context {                return context              }    },        ENGLISH(LanguageType.ENGLISH) {        private var context: Context = LanguageUtil.wrap(Extentions.getApp(), Locale(languageType.language))                override fun getContext(): Context {                return context              }    },        BANGLADESH(LanguageType.BANGLADESH) {        private var context: Context = LanguageUtil.wrap(Extentions.getApp(), Locale(languageType.language))                override fun getContext(): Context {                return context              }    };        abstract fun getContext(): Context        }}

讲到这里,出海项目的多语言适配就结束了,本Demo解决了开头提出的所有问题。Gitgub地址如下,欢迎大家下载体验:https://github.com/loveAndroidAndroid/OveraeaDemos

推荐阅读

GooglePlay账号关联审查机制详解

另类封号!别让你的Google老账号为你的粗心买单

Google Play开发者组织身份验证(个人转组织)详解

相关推荐
长亭外的少年4 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿6 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神7 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛8 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法8 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter9 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快10 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl11 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江11 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
Dnelic-11 小时前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记