专栏首页HLQ_StruggleAndroid 国际化之多语言适配小记

Android 国际化之多语言适配小记

害,乱糟糟,总要去梳理.

面对未知的一切,陌生感突突的.

甲方要求实现 App 国际化多语言,正好抽个时间弄了下,害,被自己蠢到死,特意记录下.

如有不对,欢迎指正,一起交流~

搞起来

简单说下需要注意的:

  • 国际化,多语言目录创建,资源配置;
  • Locale 资源获取以及本地缓存,缓存的目的是为了下次重新打开 App 依然是上次选择的语言;
  • Android 系统间不同的差异,例如 7.0 后不再是唯一默认语言,而是多种语言配置,具体差别如下所示:

好啦,直接上码~

网上看到大家再讨论这个 androidx 包下 appcompat 问题,这里也把我使用的版本贴出来:

  • implementation 'androidx.appcompat:appcompat:1.2.0'

一、创建对应的资源文件

方式有两种.如下:

方式一:

右键 「res」,选择 「New」,「Android Resource File」:

按如下图进行选择配置语言表:

方式二:

Android Studio 左侧选择「Resource Manager」,随后选择小地图 + 的标志,最后在列表中选择对应兼容的国家即可.

随后会为我们创建选择的国家的 values 目录以及 strings 文件,如下所示:

好了,到现在,基本的语言目录以及文件都已经创建好了,剩下的就是会有专人负责提供对应的翻译词.

当然,我司一贯的原则是,自己动手,丰衣足食.

提供了部分常用的、不错的在线翻译地址,如下:

  • www.deepl.com/translator
  • translate.google.cn/

二、贴心附上过程中使用的 MMKV Utils

记得去引用 MMKV 依赖以及初始化,地址如下:

  • github.com/Tencent/MMK…

个人使用的版本如下:

  • implementation 'com.tencent:mmkv:1.0.17'
/**
 * @author:HLQ_Struggle
 * @date:2020/4/13
 * @desc:基础数据缓存
 */

class MMKVPro<T>(
    private val mmkv: MMKV,
    private val key: String,
    private val defValue: T
) {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        // 本地加密存储并支持多进程访问
        return mmkv.run {
            when (defValue) {
                is String -> getString(key, defValue)
                is Boolean -> getBoolean(key, defValue)
                is Long -> getLong(key, defValue)
                is Int -> getInt(key, defValue)
                is Float -> getFloat(key, defValue)
                else -> Unit
            }
        } as T
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        return mmkv.run {
            when (value) {
                is String -> putString(key, value)
                is Boolean -> putBoolean(key, value)
                is Long -> putLong(key, value)
                is Int -> putInt(key, value)
                is Float -> putFloat(key, value)
                else -> Unit
            }
        }
    }
}

/**
 * 移除 key
 */
fun removeKey(key: String) {
    MMKV.mmkvWithID(F_APP_CACHE, MMKV.MULTI_PROCESS_MODE, K_ENCRYPT).run {
        remove(key)
    }
}

三、准备多语言 utils

/**
 * @author HLQ_Struggle
 * @date 2021/02/26
 * @desc
 */

/**
 * Activity 更新语言资源
 */
fun getAttachBaseContext(context: Context): Context {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return setAppLanguageApi24(context)
    } else {
        setAppLanguage(context)
    }
    return context
}

/**
 * 设置应用语言
 */
@Suppress("DEPRECATION")
fun setAppLanguage(context: Context) {
    val resources = context.resources
    val displayMetrics = resources.displayMetrics
    val configuration = resources.configuration
    // 获取当前系统语言,默认设置跟随系统
    val locale = getAppLocale()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        configuration.setLocale(locale);
    } else {
        configuration.locale = locale;
    }
    resources.updateConfiguration(configuration, displayMetrics)
}

/**
 * 兼容 7.0 及以上
 */
@TargetApi(Build.VERSION_CODES.N)
private fun setAppLanguageApi24(context: Context): Context {
    val locale = getAppLocale()
    val resource = context.resources
    val configuration = resource.configuration
    configuration.setLocale(locale)
    configuration.setLocales(LocaleList(locale))
    return context.createConfigurationContext(configuration)
}

/**
 * 获取 App 当前语言
 */
private fun getAppLocale() = when (LocalDataStorage().multilingual) {
    0 -> { // 跟随系统
        getSystemLocale()
    }
    1 -> { // 中文
        Locale.CHINA
    }
    2 -> { // 英文
        Locale.ENGLISH
    }
    else -> Locale.ENGLISH
}

/**
 * 获取当前系统语言,如未包含则默认英文
 */
private fun getSystemLocale(): Locale {
    val systemLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        LocaleList.getDefault()[0]
    } else {
        Locale.getDefault()
    }
    return when (systemLocale.language) {
        Locale.CHINA.language -> {
            Locale.CHINA
        }
        Locale.ENGLISH.language -> {
            Locale.ENGLISH
        }
        else -> {
            Locale.ENGLISH
        }
    }
}

四、在选择多语言页面进行处理

当然这里我的思路是,本地缓存语言列表索引,然后后续根据 id 直接获取对应的语言即可.

点击确认时,进行缓存当前选择的

override fun onClick(v: View?) {
    when (v?.id) {
        R.id.tvDone -> {
            // 更新选择状态
            LocalDataStorage().multilingual = mAfterPosition
            setAppLanguage(this)
            reStartActivity()
        }
    }
}

private fun reStartActivity() {
    val intent = Intent(mSelfActivity, MainActivity::class.java)
    intent.flags = FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    startActivity(intent)
    // 取消其专场动画
    overridePendingTransition(0, 0)
}

五、Application 中 Configuration 处理

override fun onConfigurationChanged(newConfig: Configuration?) {
    super.onConfigurationChanged(newConfig)
    // ...
    setAppLanguage(this)
}

六、BaseActivity 处理

由于需要重建 Activity 去处理对应资源,所以这里个人是把它放在 BaseActivity 中去处理:

override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(newBase?.let { getAttachBaseContext(it) })
}

七、优化项,资源文件更新

大家千万记得更新这个,如果做过 Apk 大小优化,八成都会限制 resConfigs 内容,避免打包时多处一些无用内容增加 Apk 大小.

好了,到此结束,当然,Android 不得不面对的多机型适配...

这里后续遇到在更新把~

多语言遇到的一些问题

1. 布局问题

这个的确让人蛮头疼的,尤其对于我们基建不完整的情况,能做的只能说是保证大部分的效果,尽量使用短称英文或者非中文.

同时这个也提醒我,如何在开发的过程中尽可能兼容后续呢?

可能也是经验把,慢慢努力.

2.TabLayout 英文模式下大写

切换后效果如下:

目前使用的 TabLayout 版本如下:

  • implementation 'com.google.android.material:material:1.2.1'

喏,设置个样式就好:

<style name="TabLayoutTextStyle" parent="TextAppearance.Design.Tab">
    <item name="android:textSize">@dimen/sp_18</item>
    <item name="textAllCaps">false</item>
</style>

后续遇到再补充吧.

参考资料

  • 本地化您的应用
  • Unicode 和国际化支持
  • 语言和语言区域解析概览
  • Android(国际化)多语言的实现和切换
  • Android多语言切换(兼容安卓9、10)

本文分享自微信公众号 - 贺利权(hlq_struggle),作者:贺利权

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-03-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 快速适配 Flutter 之语言国际化

    如果你希望你的APP走出海外,那么就需要你在编写代码时考虑支持不同的语言环境,设置一些“本地化”的值,例如文本/布局。Flutter本身是具备国际化的,在适配方...

    出其东门
  • iOS开发之多国语言国际化适配经验浅谈

    Bison
  • Android 国际化(多语言)兼容8.0

    Android中实现国际化相对来说还是简单的,因为Android有很独特的资源管理方式,我们可以很轻松的创建资源支持不同语言.

    用户1269200
  • Android支持国际化多语言那点事(支持8.0+)

    我们在开发app可能会拓展国外市场,那么对包含英语在内的其它语言支持就很有必要了。

    砸漏
  • Android App 国际化

    internationalization ( 国际化 )简称 i18n,因为在 i 和 n 之间还有 18 个字符,localization (本地化),简称L...

    QQ音乐技术团队
  • 利用WebView实现网页的i18n

    软件如果想在全球获得更多的用户,国际化与本地化(internationalization and localization 简称:i18n 和L10n)是非常必...

    技术小黑屋
  • Android 适配各国语言、屏幕尺寸、系统版本及常见适配方法总结

    全世界安卓智能手机语言不相同,由于各厂家生产出的系统、屏幕也各异,随着SDK不断更新不同版本也有区别,android适配技能日益成为必不可少的一项专业技能。根据...

    Android技术干货分享
  • 干货 | 国际化探索之路-Trip.com如何走进阿拉伯市场

    随着国际化之路的进一步推进,Trip.com已经在全球多个国家开设了站点,今天的主角是阿拉伯世界。

    携程技术
  • Android APP 终极瘦身指南

    APK瘦身即是对APK大小进行压缩策略,减小APK安装包大小,更小的安装包更有助于吸引用户安装;虽然说APK瘦身对于Android对应用可分配内存的限制影响不大...

    Android技术干货分享
  • Android 项目开发填坑记 - 获取系统语言(兼容7.0)

    获取系统当前语言是一个比较常用的功能,在 Android 7.0 系统上旧函数获取到的当前系统语言并不正确,或者说从 Android 7.0 起,Android...

    他叫自己MR.张
  • 干货 | 前端跨端业务整合的探索与实践

    Jeff,携程前端开发经理,对前端自动化技术感兴趣,推动了团队使用cucumber进行UI自动化测试。Harry,携程前端开发工程师,秉持“Don’t make...

    携程技术
  • Android面试指南:在市场初级开发饱和环境下,如何登上BAT这座山成为巅峰者年薪50W+?

    在我刚加入现在这个团队时,那会儿才毕业大半年,我就在思考这个问题,只不过当时更专注于Android开发领域:Android开发的核心竞争力是什么?我有一个专门的...

    Android技术干货分享
  • 用新技术 “派生” 的旋律把耳朵叫醒

    不同于其他手机内存里的常客,音乐类应用更多的时候是在手机熄屏的状态下工作着。享受音乐,最理想的状态便是沉浸而不易中断,开发者们也在为此不断努力。

    Android 开发者
  • 微信热传的 100+ 经典技术文章

    阿炬
  • 关于 Android N 那些你不知道的事儿

    今年3月,Google 破天荒提前半年发布了 Android N 开发者预览版。当然,作为一个不合格的谷粉并没有第一时间体验安装,因为至今仍然能够回忆起来去年今...

    腾讯Bugly
  • Android开发笔记(八十三)多语言支持

    app中有许多场景要对汉字排序,例如通讯录姓名、商品名称、城市名称等等,这些汉字词汇通常是按照拼音排序,所以产生了把汉字转换为拼音的需求。

    用户4464237
  • 有关 Android 应用桌面角标 (BadgeNumber) 实现的探讨

    怎么在 Android 系统下让自家的应用图标像 iOS 系统那样支持数字角标的显示? 在网上找不到现成的解决方案的情况下,该如何去寻找问题的突破口? 一种简洁...

    非著名程序员
  • 关于Android N的那些事

    今年3月,Google破天荒提前半年发布了Android N开发者预览版。当然,作为一个不合格的谷粉并没有第一时间体验安装,因为至今仍然能够回忆起来去年今日此门...

    微信终端开发团队
  • TensorFlow 1.x最后一更、Android 10最新特性,这是谷歌开发者日

    2019 谷歌开发者大会于 9 月 10 日和 11 日在上海举办,大会将分享众多开发经验与工具。在第一天的 KeyNote 中,谷歌发布了很多开发工具新特性,...

    机器之心

扫码关注云+社区

领取腾讯云代金券