SmartI18n插件:Android国际化与多语言自动化翻译实践

Android国际化SmartI18nstrings.xml
于 2026-10-01 08:22:26 修改
·本内容遵循CC 4.0 BY-SA版权协议

开头

做Android开发的朋友应该都有过这种经历:一个功能写完了,产品说需要支持英文,于是你把所有硬编码的"确定"、"取消"、"保存成功"逐个改成@string/引用,再创建values-en目录,打开翻译网站,一个词一个词地翻译、核对、粘贴。三天后产品又说过两天还要支持日文和韩文,你看着那几十条、上百条字符串,感觉整个人都不太好了。

这种活儿我干了五年,从最初的纯手工复制粘贴,到后来自己写脚本半自动化,直到去年在项目里引入了SmartI18n这个Android Studio插件,才算是真正把国际化的效率提上来了。这篇文章就围绕SmartI18n插件,聊聊它是怎么解决国际化过程中的各种麻烦的,包括安装配置、实际使用、团队协作里的一些心得,以及我踩过的那些坑。不管你是刚接触国际化的新人,还是被多语言维护折磨已久的老手,这篇文章里应该都有你能直接拿去用的东西。

1. Android国际化的核心机制与手动操作的痛点

1.1 Android国际化的底层逻辑:values目录与资源匹配

要理解SmartI18n做了什么,先得把Android国际化的底层机制搞清楚。Android系统通过资源目录的限定符机制来实现国际化,核心就是values目录下的strings.xml文件。默认的values/strings.xml存放基准语言(通常是中文或英文),想要支持某种语言,就创建一个带语言限定符的目录,比如values-en表示英文、values-ja表示日文,系统会依据用户设备的语言设置自动选择对应目录下的字符串资源。

这个机制本身设计得很优雅,开发者只需要在布局文件里写@string/xxx,在Java代码里写context.getString(R.string.xxx),剩下的匹配逻辑全交给系统。但它带来的问题也很明显:每新增一种语言,就等于要把整个strings.xml文件完整地复制一份,然后逐条翻译里面的每一个词条。项目大的时候,一个strings.xml有几百上千条词条很正常,纯靠手动维护,工作量非常大。

1.2 手动国际化最折磨人的几个场景

我这些年主持和参与过的项目,少说也有七八个做过国际化,手动维护的痛点几乎都集中在这几个地方:

  • 漏翻译和漏key:新增功能时经常忘记把字符串抽到strings.xml里,或者抽了但只加了中文,忘了同步其他语言文件。这种问题编译期不会报错,只有到测试阶段切语言环境才能发现,修复成本很高。
  • key命名混乱:团队里不同的人对同一个字符串起了不同的key,比如有人写confirm,有人写btn_confirm,还有人写ok_text,造成大量重复词条,维护起来一头雾水。
  • 复制粘贴容易错位:多语言文件的行数一旦多了,手动维护时经常出现某几条漏加、错位、格式符不匹配的问题,比如中文用的是%1$s,到了英文版本却写成了%s,运行时直接崩溃。
  • 复数形式处理:英文有单复数之分,"1 item"和"2 items"完全不一样,Android提供了plurals标签,但很多人不熟悉,或者嫌麻烦直接绕过,导致英文展示错误。
  • 格式化参数:String.format里的%1$s、%2$d这些占位符,翻译时必须原样保留,如果翻译人员在翻译时手误删除或调换了顺序,程序运行就会闪退。

1.3 为什么会需要一个自动化的国际化插件

手动方式的问题在于:它把"资源管理"和"语言翻译"这两件事强行绑定在一起,而这两件事恰好都是容易被忽视却又出错的环节。自动化插件的思路,就是把"翻译"这个机械性工作从整个流程里剥离出去,让开发者只需要维护一份基准语言的strings.xml,其他语言的版本由插件去生成和同步。

SmartI18n的核心价值有两个:一是自动扫描,把Android项目里所有硬编码的字符串、已经存在的strings.xml词条、甚至包括plurals复数结构都提取出来;二是自动翻译,通过调用第三方翻译服务的API,把基准语言翻译成目标语言,自动生成或更新对应的values-xx文件。这样一来,整个国际化流程从原来的"人工逐词翻译"变成了"一键触发,插件生成,人工复核",效率提升是数量级的。

2. SmartI18n插件是如何设计这套自动化流程的

2.1 整体工作流拆解:从扫描到生成

SmartI18n的工作流程,本质上是一套"扫描-翻译-写入"的流水线。理解了这条流水线,后面配置和使用时你才能知道每一步在做什么、出了问题应该去哪里排查。

第一步是扫描与提取。插件会遍历当前模块的res目录(也可以配置指定目录),读取基准语言(默认是values)下的strings.xml,把每条字符串的key、value、还有注释(<string>标签上方的<!-- -->)都读取出来。这一步还会顺带扫描Java/Kotlin代码和布局XML,检测那些已经硬编码但还没抽到资源里的字符串,提醒你处理,这个功能虽然不直接参与翻译,但规避了"漏抽key"的问题,很实用。

第二步是翻译映射。插件会把提取出来的一系列词条去重、分片,然后批量提交给翻译服务API。为什么要分片?因为翻译服务一般有单次请求的长度限制(比如每次最多5000字符),词条多了就得切分成多段请求。每段请求返回后,插件把翻译结果与原始词条做一一对应,组装成一个"key-translatedValue"的映射表。

第三步是写入生成。拿到映射表后,插件会在res目录下创建或更新目标语言对应的values-xx文件夹,把翻译结果写入strings.xml。写入时会注意保留原始词条的注释、转义字符和格式化参数,尽量避免把%1$s这种占位符破坏掉。

2.2 为什么翻译服务的选型用"可配置"而不是写死

SmartI18n在设计上有一个很关键的决定:翻译服务不写死,而是提供一个配置入口,让你填入自己的API Key、接口地址、以及对应的语言代码映射关系。我当时看到这个设计就觉得靠谱,原因很简单:

  • 不同公司可能有自己的内部翻译平台,或者有长期合作的外部翻译供应商,人家有统一的术语库和翻译记忆库,翻译质量比自己对接公开API稳定得多。
  • 翻译API大多有免费额度和付费额度,写死某一家的服务,相当于把成本、稳定性都绑在了这一家上。可配置的话,项目里可以随时切换供应商,甚至可以在本地搭一个代理服务再对接各家大模型,灵活度非常高。
  • 如果API Key泄漏了,换一个Key就行,不用升级插件版本。插件本身不保存你的密钥,只存在IDE的配置里,安全性也更好一些。

实际配置的时候,你只需要填入服务方提供的API地址、秘钥、目标语言代码,再指定一个术语词典文件(可选),翻译质量就能有一个基本的保障。

2.3 与传统"外包翻译+手动合入"相比的优势

可能有人会说,我们公司财大气粗,都是把strings.xml发给翻译公司,让人家翻译完再导入。这个流程我也走过,跟自动化插件相比,差距非常明显:

  • 时效性差异:外包翻译的周期以天计算,插件几秒钟就能出结果。如果只是开发阶段要临时加一门语言给海外测试用,插件完胜。
  • 成本差异:外包翻译按字数收费,插件调API的费用通常要低一个数量级。而且日常迭代中改动几个词条,为了几行英文去找外包,沟通成本都比翻译费高。
  • 可重复性差异:翻译公司交付的文件,还得人工核对key是否对齐、格式符是否完整;插件生成的格式基本是固定的,出错概率小得多。
  • 迭代友好性差异:国际化不是一次性工作,每个版本都在增量修改。手动模式下每次发版都是一轮新的"整理+翻译+合入+校验",而插件模式下你只需要改基准语言,再点一次同步即可。

我自己是在一个内测版本里先试用了这套流程,跑通之后才在正式项目里推广的。刚开始团队成员也有顾虑,怕翻译质量不行。实际用下来,对于"确定""取消""收藏"这类通用词条,机器翻译完全够用;对于带有业务语境的文案,配合术语词典和人工复核,效率和质量都能兼顾。

3. SmartI18n的安装配置与实操演示

3.1 插件安装与环境准备

我用的Android Studio版本是2024.1以后的新版本(也就是新命名规则下的版本),安装插件的路径跟以前一样:打开Android Studio,进入File -> Settings -> Plugins,在Marketplace标签页搜索"SmartI18n",点击Install安装,重启IDE即可。如果你公司内网环境不能直接访问插件市场,也可以去JetBrains插件仓库下载安装包,然后通过Install Plugin from Disk离线安装。

环境配置上,除了Android Studio本身,你需要准备以下几样东西:

  • 一个可用的翻译服务账号,用于获取API Key。国内的翻译服务开放平台和国外的都有免费额度,个人学习和测试完全够用。
  • 确认项目使用的Gradle与AGP版本。SmartI18n对项目本身没有侵入性,它只操作res目录下的文件,理论上不挑版本,但建议AGP版本不要太老,否则Android Studio的API兼容性可能出问题。
  • 确认Java环境。这个一般不用操心,能跑Android Studio的机器Java环境必然没问题。

3.2 基础配置:接入翻译服务与语言映射

安装完成后,第一步配置在File -> Settings -> Other Settings -> SmartI18n。这个界面主要做几个事:设置翻译服务的平台的名称或类型、填API Key、选择基准语言、设置目标语言列表、配置词典文件路径。

拿我项目里的配置来举例。基准语言是简体中文(values),目标语言增加了英文(values-en)、日文(values-ja)、韩文(values-ko)。在配置界面里,语言代码的映射要填对,比如简体中文对应zh-CN,英文对应en,日文对应ja,这些代码必须遵循BCP 47标准,否则生成的目录名Android系统不认识。

提示:目标语言不是越多越好。每增加一门语言,后续每次迭代同步的成本都会增加,而且翻译质量参差不齐。建议在产品和研发确认清楚,这一版到底要覆盖哪些国家和地区,再在插件里做对应配置。

API Key的获取方式每个翻译服务商都不太一样,但基本流程都是:注册账号、创建应用、开通翻译服务、拿到密钥。有些服务商要求配置域名白名单,本地调试时可能需要在个人中心里把localhost加进去,这个在首次调用报错时再排查也行。

3.3 一键生成多语言文件的操作流程

配置完成后,日常使用就非常简单了。接下来说说我在实际项目里的标准操作流程:

  1. 在values/strings.xml里正常维护中文词条,该写注释写注释,该用plurals用plurals。
  2. 新功能开发完、自测通过之后,右键点击res目录(或者直接右键strings.xml文件),在弹出的菜单里选择SmartI18n -> Translate。
  3. 插件弹出确认框,显示这次会翻译多少条词条、生成哪几种语言。确认后点OK。
  4. 几秒钟后,res目录下就会出现或更新values-en、values-ja、values-ko目录,里面是翻译好的strings.xml文件。
  5. 检查一下生成结果,重点看格式化参数有没有保留、特殊字符有没有转义。没有问题就提交代码。

这里有几个细节值得注意。第一,插件的扫描范围是递归的,values-night、values-v21这类带修饰符的目录也可能被扫描到,如果不想让插件动这些目录,需要在配置里排除。第二,如果基准语言里新增了词条,翻译时会走增量逻辑——之前已经翻译过的词条不会重复请求API,只有新增和修改的部分才会再翻译。这样既省钱又省时间。第三,插件生成的strings.xml会尽量保持与基准文件相同的key顺序,方便后续diff和Code Review。

3.4 进阶配置:术语词典与复数/格式化模板

用了一段时间后,我发现只要配置得当,SmartI18n的翻译质量能再上一个台阶,关键就在两个地方:术语词典和特殊模板处理。

术语词典是我比较推荐的一个功能。项目里总会有一些专有名词、品牌名、特殊缩写,比如App名字、某个功能模块的英文名,这些是不需要翻译的,或者有官方指定的译法。你可以在项目里建一个translation_dictionary.properties文件,每行一个映射关系,比如:

PROPERTIES
我的应用=My App
会员中心=VIP Center
工单=Work Order

配置好词典路径后,翻译请求发出前,插件会先把这些词条用词典内容替换掉,翻译完成后再替换回来,或者直接冻结这些词条不去翻译,确保专有名词不会被机器翻译成奇怪的表达。这个对维护品牌一致性非常关键。

关于复数和多格式模板的翻译,Android的plurals资源内部包含<item quantity="one">、<item quantity="other">等子标签,普通翻译服务不一定能正确理解这种结构。SmartI18n的处理方式是逐条提取plurals里的每一个子item分别翻译,再按原结构写回。对于%1$d这类参数,插件会做占位符保护,翻译引擎输出的文字中如果丢失了占位符,插件会给出警告,你可以在确认时手动修正。

4. 使用SmartI18n过程中的常见问题与排查记录

4.1 翻译请求报错排查:密钥、配额与网络

插件接入网络翻译服务后,最常见的错误就是请求失败或返回异常。我从实际使用里遇到的报错场景,总结成了一张排查表:

报错现象 可能原因 排查思路
401 Unauthorized API Key填错或已失效 检查Settings里填写的Key前后有没有空格,去服务商控制台重新复制一遍
429 Too Many Requests 超出免费配额或QPS限制 看服务商控制台的用量统计,等配额刷新再试,或调大插件的请求间隔
超时/连接失败 网络不通或服务商接口被墙 先确认电脑能正常访问服务商官网;局域网内若有代理,检查IDE的代理设置是否匹配
返回结果为空 词条内容不支持翻译或语言代码错误 检查目标语言代码是否符合标准,比如中文代码不能写成cn,要写zh-CN

需要特别提醒的是,不同翻译服务的语言代码标准有时不一致,有些用zh,有些用zh-CN,有些甚至要求用cht表示繁体中文。配置目标语言之前,先去服务商文档里确认一下它们的语言代码格式,否则翻译请求能发出去,但返回的可能是原始文本。

4.2 生成结果异常:占位符丢失与字符转义问题

第一次跑通自动翻译之后,我Full Check了一遍生成的所有values-en文件,还真发现了几个问题。最典型的是格式化参数错位:

XML
<!-- 中文原文 -->
<string name="order_count">共%1$s件商品</string>
 
<!-- 被翻译成英文的错误结果 -->
<string name="order_count">%1$s items</string>
原文里如果把"商品"翻译成"items",顺序没问题;但有的语言语序不同,翻译引擎可能会把`%1$s`挪到句尾,这个其实是合理的。真正的坑是翻译引擎在某些情况下会直接丢掉`%1$s`占位符,或者在翻译"100%"这种内容时把百分号误转义成其他内容。

插件的占位符保护机制能拦截一部分这类问题,但保险起见,每次批量翻译之后,我都会跑一个简单的脚本做校验:检查每个<string>的value里,占位符数量与基准文件是不是一致。这个脚本可以用Python写,本身不复杂,几分钟就能搞定。

另一个常见问题是XML特殊字符的转义。如果原文里包含&号,在strings.xml里必须写成&amp;,翻译引擎处理不好就容易出错。我见过一个例子:英文原文"Tom & Jerry",翻译之后&变成了and,虽然意思没变,但在资源文件里如果不转义,XML解析器会直接报错。所以每次翻译完,我习惯用Android Studio自带的Lint检查一遍资源文件,它能自动发现未转义的特殊字符。

4.3 增量同步与多语言合并时的冲突处理

项目进入维护期后,真正的麻烦往往不是翻译本身,而是多语言与主干代码之间的同步问题。举一个典型的场景:你在values/strings.xml里新增了5条字符串,同时在values-en里改动了一个旧词条,这时候执行自动翻译,插件会怎么处理?

我实测下来的行为是:插件会把values目录下的所有词条作为基准,把目标语言目录里已有的词条与基准做diff。已存在的词条如果基准里也没变,就跳过;基准里变了,就重新翻译;基准里新增的,进行翻译补充。如果目标语言目录里有基准已经删除的词条,不同版本的插件策略可能不同,有的会忽略,有的会同步删除。这个行为建议在配置项里确认一下,我的习惯是选择"同步删除失效词条",避免旧词条一直堆积在values-en里,到时候切语言环境蹦出个莫名其妙的老文本。

合并冲突通常出现在多人协作的场景。比如同事A在改values/strings.xml,同事B在改values-en/strings.xml,两个人同时提交代码,Git就会出现冲突。这种冲突没有完美的自动解法,我的建议是:国际化文件的修改尽量跟对应的业务MR放在一起,由同一个人提交,从源头上减少冲突概率。如果真冲突了,由于插件生成的文件有固定的key顺序和格式,手动解决起来也不会太费劲。

4.4 我踩过的一个低级但高发的坑:中文文本被写进了默认values

有段时间我发现某些手机上部分界面还是显示中文,查来查去发现原因很搞笑:有几个词条不小心写到了values-en/strings.xml里,但内容还是中文原文。原因是翻译请求超时了,插件把原文当作翻译结果写进去,当时我没仔细看,就直接提交了。

这种情况怎么避免?两个办法。第一,每次翻译后不要立刻提交,花30秒扫一眼新生成的目标语言文件,看到中文内容马上改掉或重新翻译。第二,在项目的CI流水线里加一个简单的脚本检查:遍历所有values-*目录下的strings.xml,统计是否包含中文字符(目标语言不包含中文时),包含就报错。这个检查脚本很简单,用正则就能搞定,但对质量保障特别有效。

5. SmartI18n在团队协作与项目落地中的建议

5.1 借助插件的"代码审查辅助"提升协作体验

SmartI18n不只帮我们生成了多语言文件,也在代码审查环节帮了不小的忙。以前做国际化,Code Review的时候很难查漏,因为多语言文件是纯手工维护的,没有统一的diff基准。用了插件之后,values-en等文件跟values文件保持了一一对应的关系,评审者只需要看values下改了什么,再快速扫一眼对应的翻译文件有没有跟上,基本就能确认国际化有没有做全。

为了更方便评审,我建议在MR描述里约定写明本次涉及的国际化工单,并让开发者在提交时说明哪些语言是插件自动生成的、哪些是人工复核过的。这样评审时可以重点关注人工复核的部分,自动生成的部分通常抽查即可,效率高很多。

5.2 翻译质量保障体系:术语库与人工抽查结合

插件解决了"有没有"的问题,但"翻译得对不对、好不好"仍然需要人来兜底。我的经验是搭建一个轻量级的质量保障体系:

  • 建立项目术语表:上面提到的translation_dictionary.properties,由团队里的语言负责人维护,每次迭代更新,保证品牌词和业务专有名词的译法统一。
  • 发布前人工抽查核心路径:比如注册登录流程、支付流程、首屏文案,这些对用户体验影响最大的界面,发布前人工切到对应语言环境,逐屏检查一遍。
  • 收集用户反馈通道:海外的用户如果发现翻译错误,可以通过App内的反馈入口提交,定期整理到术语表或词典里,下一轮翻译自然就修正了。

这套体系搭建起来并不复杂,但对翻译质量的提升效果非常明显。机器翻译负责"快",术语库负责"稳",人工抽查负责"准",三层配合才能既快又好。

5.3 与CI/CD脚本搭配,实现国际化零人工

如果你的团队有CI基础设施,我强烈建议把SmartI18n的检查思路融入到自动化流水线里,实现国际化的"零人工"保障。具体来说可以做这几件事:

  • 资源完整性检查:写一个Shell或Python脚本,在构建时比对基准values目录和目标语言目录的key集合是否完全一致,不一致直接让构建失败。
  • 占位符一致性检查:遍历所有语言文件,检查每条字符串的%s、%1$d等占位符是否与基准文件匹配。
  • 未翻译文本检查:检测目标语言文件里是否残留基准语言的关键字符(比如中文字符)。
  • 提交前自动翻译:有些团队会在Git Hook里配置,当检测到values/strings.xml有变更时,自动执行SmartI18n的翻译命令,把翻译结果一起带入提交。但这个操作要谨慎,因为机器翻译结果未经过Review,直接进代码库有风险。

我把前三条脚本都加到了公司的CI流水线里,从那之后,因为翻译问题引起的线上事故基本绝迹了。脚本本身花不了多少时间,却能让整个团队对国际化这件事彻底放心。

5.4 关于"是否需要人工翻译兜底"的思考

最后聊聊一个很多人关心的问题:用了SmartI18n自动翻译之后,还需要找专业翻译公司做人工翻译吗?我的看法是分场景判断。

如果你做的是工具类App,文案以"确定""取消""保存""删除"这类通用术语为主,机器翻译的质量完全够用,配个术语词典就差不多了。如果你做的是内容类、社交类、电商类App,文案里带有大量营销语气、双关语、文化隐喻,那机器翻译在语感、本地化适配方面确实不如专业译者,这时候可以把插件作为"初稿生成工具",把机器翻译结果整理好之后发给专业译者润色,成本比直接让译者从头翻译低得多。

我现在负责的项目就是这么操作的:日常迭代用SmartI18n快速生成翻译,自动合入代码跑通流程;每个大版本发版前,把核心模块的文案整理出来,交给本地化服务商做一次精翻和审核。两套流程并行,既不阻塞开发节奏,又能保证出海产品的文案水准。说实话,刚用上SmartI18n那会儿,我对翻译质量也有过担心,甚至一度觉得"这不就是把活推给机器吗",但实际用了大半年之后,我自己的体会是:工具解决的是效率和一致性问题,人需要做的反而是把更多精力放到术语规范、审校流程和文化适配上去。至少在我这边,这样的分工让整个团队都轻松了不少,国际化的迭代速度也从以前的一个星期缩短到了几小时。