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开发者组织身份验证(个人转组织)详解

相关推荐
非凡ghost1 小时前
LSPatch官方版:无Root Xposed框架,自由定制手机体验
android·智能手机·软件需求
_extraordinary_1 小时前
MySQL 库的操作 -- 增删改查,备份和恢复,系统编码
android·mysql·oracle
西瓜本瓜@3 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
似霰7 小时前
安卓adb shell串口基础指令
android·adb
fatiaozhang95279 小时前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO10 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师10 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师10 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫11 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白11 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度