
做网络会诊系统这些年了,经常有朋友问我一个问题:你们这个系统支持多少种语言?这个问题看似简单,但真正要做好多语言界面切换,其实门道挺多的。今天我就把这个话题聊透,说说我们声网在做这件事的时候是怎么思考的,又是怎么落地的。
先说个事儿吧。去年有个客户是东南亚那边的医院,他们想把自己的会诊系统推向周边几个国家。你知道那边什么情况吗?新加坡主要用英语,马来西亚英语和马来语都有,泰国是泰语,印尼是印尼语,还有越南语等等。客户一开始觉得,不就是界面文字换一下嘛,能有多难。结果真正做的时候才发现,这里面的工作量比他预想的大多了。
很多人觉得多语言嘛,不就是找几个翻译人员,把界面上的文字翻成不同的语言,然后存到文件里,用的时候调出来显示就行了。这个理解只能说是对的,但不够完整。翻译只是第一步,真正麻烦的是后面的适配工作。
你想啊,中文通常比英文短,一个按钮上写”确定”两个就够了,英文可能得写”Confirm”或者更长的表达方式。如果按钮大小是固定的,翻译成德语的时候怎么办?”Bestätigen”可比”Confirm”长多了。再比如阿拉伯语和希伯来语是从右往左读的,界面布局要不要镜像?日期格式怎么办?中国人习惯”2024年1月15日”,美国人习惯”01/15/2024″,欧洲人可能又不一样。这些细节加起来,才真正构成了多语言切换的核心工作。
声网在设计多语言方案的时候,把这些因素都考虑进去了。我们把它分成几个层面:语言资源管理、界面适配逻辑、用户偏好识别,还有运行时切换机制。每个层面都有讲究,且听我慢慢说。
先说语言资源管理这个部分。这相当于整个多语言系统的地基,地基不稳,上面再好也会出问题。

我们用的方案是资源文件分Keys和Values两部分。比如界面上有个”开始会诊”按钮,在中文资源文件里对应的是start_consultation = "开始会诊",英文里对应start_consultation = "Start Consultation"。程序里面不直接写”开始会诊”这几个字,而是写一个标识符start_consultation,显示的时候根据当前语言去对应的文件里取值。
这样做有什么好处呢?改动的时候只需要改资源文件,程序代码不用动。而且如果某个翻译不准确,修改起来也方便,不会在代码里东改西改容易漏掉。
资源文件的组织方式我们也是反复考量过的。最后采用的是按语言分目录的结构:每个语言一个子目录,里面放这个语言用到的所有资源。这样便于管理,新增语言只需要建个新目录把翻译放进去就行。翻译人员也只需要关注自己负责的语言文件,不会搞混。
对了,资源文件格式我们选的是标准的XML。为啥不用JSON呢?JSON虽然流行,但XML有它的好处。XML可以加注释,翻译人员看到注释就知道这句话在界面上大概什么地方、上下文是什么,翻译起来更准确。还有,XML的层级结构对复杂文本的组织更友好,比如有些语言需要区分正式和非正式说法,XML的结构表现起来更清晰。
刚才提到按钮文字长短不一样的问题,这就是界面布局适配要解决的。我们在这方面做了不少工作。
首先,按钮、文本框这些控件的大小不能写死。在我们的系统里,控件宽度默认是”自适应”,也就是根据里面文字的长度自动调整。文字长了,控件就自动变宽;文字短了,控件就自动变窄。这样就不用为每种语言专门调按钮大小了。
但是问题来了。如果一个界面上好几个按钮并列排在一起,每个都自适应宽度,结果可能是这一行是整齐的,下一行因为文字长短不一就歪了。这也不好看。我们的做法是给按钮设置一个最小宽度和最大宽度,最小宽度保证按钮不会太窄显示不全,最大宽度保证不会太宽占地方。在这两个范围之内自适应,超出了就显示省略号或者换行。
还有一个更复杂的情况是从右往左的语言。阿拉伯语、希伯来语这些,阅读顺序是从右到左,界面布局是不是要镜像?这个问题我们讨论了很久。最后的结论是:不一定全盘镜像。

你想啊,一个会诊界面,左边是患者信息,右边是视频画面。如果完全镜像,患者信息跑到右边,视频跑到左边,用户反而不习惯。我们的做法是”逻辑方向”和”视觉方向”分开。文字排版遵循从右到左的顺序,但整体布局框架保持稳定。只是把一些方向性的图标和提示文字做适当调整。比如返回按钮,正常情况下在左边,阿拉伯语界面就放到右边,这样用户找起来也符合直觉。
接下来聊聊用户偏好这个话题。用户第一次使用系统的时候,怎么知道该显示什么语言?总不能让用户自己去找设置入口吧,那体验也太差了。
我们设计的策略是多级优先级。第一级是用户账户里保存的偏好设置,这个优先级最高。用户要是之前选过法语,下次登录还是法语。第二级是浏览器或者操作系统的语言设置。浏览器在访问网页的时候会把当前语言偏好发过来,系统可以参考这个显示默认语言。第三级是IP地址定位。这个不太准确,但可以作为参考。比如检测到用户IP在韩国,默认先给韩文界面。这三级都没有的话,就用系统的默认语言,通常是英文。
用户改了语言设置之后,我们马上把所有界面文字换掉,连刷新页面都不用。这个是怎么做到的呢?其实背后有个语言切换的监听机制。一旦检测到语言变化,系统会发一个通知,所有正在显示的界面组件收到通知后就去重新读取对应语言的资源,更新显示内容。这个过程用户是感知不到的,看起来就是瞬间完成。
记忆功能也很重要。用户选的偏好要存下来,下次再来不用重新选。我们用的是本地存储加云端同步的方案。本地存储保证即使没登录也能记住偏好,云端同步保证用户换了个设备登录,偏好还能带过来。这两个配合起来,体验就比较完整了。
现在说说最核心的部分:运行时切换是怎么实现的。这部分技术含量稍微高一点,我尽量用大白话说清楚。
首先系统启动的时候,会根据前面说的优先级机制确定当前语言,然后去加载对应语言的资源文件。资源文件加载进来之后,会被解析成一个内存里的数据结构,程序随时可以去查这个结构获取文字。
语言切换的时候,程序不用重启,只需要做几件事:去加载新语言的资源文件,替换掉内存里旧的数据结构,然后通知所有界面组件刷新显示。这个过程在我们的测试里,十几种语言切换基本都在200毫秒以内完成,用户体验上就是”唰”一下就变了。
资源文件加载我们做了缓存。同一个语言的文件只加载一次,下次切换回来直接从缓存里取,不用再发网络请求。这对性能提升很明显,特别是网络不好的时候,切换也不会卡顿。
还有一点要考虑的是正在输入的内容怎么办。比如用户正在用中文写病历,突然切换到英文界面,输入到一半的文字怎么办?是清掉还是保持?我们选择的策略是保持原样,但界面提示文字变成英文。用户继续输入的话,就还是中文。这样避免辛辛苦苦打的内容突然没了。当然,如果用户继续编辑,保存的时候系统会自动识别这是中文内容,不会在英文报告里出现乱码。
除了界面文字,还有很多格式需要处理。这部分挺繁琐的,但不做又不行。
日期格式的差异我举几个例子你就明白了:
| 地区 | 日期格式示例 |
| 中国 | 2024年1月15日 |
| 美国 | January 15, 2024 |
| 欧洲很多国家 | 15.01.2024 |
| 日本 | 2024/01/15 |
同一个日期,不同地方写法完全不一样。我们的做法是提供一套格式化函数,传入日期和地区代码,自动返回符合当地习惯的字符串。程序里不用手动拼接日期,全部调用这个函数就行。
数字格式也有类似问题。英语里用逗号作千位分隔符,比如”1,234″,德语里用点,”1.234″,法国又可能用空格。这些我们在数字格式化工具里都封装好了,开发者调用时指定地区就行。
货币更复杂一些。同样是100美元和100欧元,虽然数字都是100,但符号和换算方式不同。我们是把货币代码和汇率信息也存在语言资源里,格式化的时候自动加上正确的货币符号。
说着说着想起一些做的时候踩过的坑,分享出来给大家参考。
第一个坑是翻译质量。最开始我们找的翻译公司翻的医疗术语惨不忍睹。”电子病历”翻成”Electronic Medical Record”是对的,但有些词他们翻得驴唇不对马嘴。后来我们意识到,必须找有医疗背景的翻译,或者至少让懂行的人审核一遍。现在我们建立了术语库,把医疗常用词的标准译法存起来,翻译和审核都参考这个,错误少多了。
第二个坑是字体支持。有些语言字符集大,比如中文、日文、韩文这些,需要特殊的字体文件。我们一开始没注意,用户切换到中文界面显示的是系统默认字体,丑不说,有些生僻字还显示成方块。后来专门处理了字体回退机制:优先用这个语言专用的字体,没有就用备用字体,确保能显示出来。
第三个坑是RTL语言的图标方向。RTL就是从右往左的语言,我们一开始把图标也镜像了,结果有些图标镜像之后就变味了。比如箭头图标,镜像之后指向反方向,用户以为是往左,其实是往右,非常误导人。后来我们学乖了,只有那些本身具有方向含义的图标才考虑镜像,其他的一律保持原样。
如果你正打算给自己的系统加多语言支持,我有几点建议。
多语言这事儿,看起来简单,做起来才知道里面的讲究。但只要方法对了,方案合理,后面的扩展和维护就顺理成章了。声网在这块积累了不少经验,有问题随时可以交流。
今天就聊到这儿吧,希望对你有帮助。
