方案1

所有有中文的列都增加一个英文列(或其他语言),优点是稳定可控,翻译内容可以管理端随时调整,缺点是每增加一种语言就增加一个列,改动的地方太多,改动太大,加翻译需要修改代码和重新部署,除非是文本字段是富文本或超长文本类型,否则不推荐使用。

总结:加列实现多语言
应用场景:
用户协议表、文章表等超长文本类型,或者表中需要翻译的字段(列)小于3个

方案2

数据库只存中文,在展示层(前端)国际化成英文(或其他语言),优点是简单,改动的地方少改动小,缺点是,不可控不稳定,很容易存在数据库录入的数据前端没有配置对应的翻译,加翻译需要修改代码和重新发布前端或APP,不推荐使用。

总结:加前端翻译配置实现多语言
应用场景:
短文本类型

方案3

使用两套完全隔离的数据库,优点是简单,改动的地方少改动小,且稳定可控,缺点是需部署不同语言的服务器和数据库,费钱,运营麻烦,而且还有用户切换语言后,账号和数据之间不互通问题(切换语言后需重新注册登录),根据自身需求情况可以使用。

总结:加数据库实现多语言
应用场景:
有国内和国外不同国家并分开部署和运营的APP、网站,数据和账号完全隔离的情况

方案4

将方案2改良,增加一个翻译字典表,前端在翻译时从翻译字典表查数据,这样就不会有遗漏的没被翻译的问题。优点:改动较少(相对方案1而言),稳定可控,翻译内容可以管理端随时调整,加翻译不需要修改代码和重新部署,推荐使用。

总结:加表实现多语言,前端初始化时查出该表作为翻译配置
应用场景:
短文本类型

表结构设计如下:

CREATE TABLE `i18n` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `code` varchar(500) NOT NULL COMMENT '编码,同一段文字的不同语言翻译的code必须相同,code可以为中文',
  `lang` varchar(20) NOT NULL COMMENT '语言',
  `value` varchar(500) NOT NULL COMMENT '翻译值',
  `create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '录入时间',
  `update_by` bigint(20) DEFAULT NULL COMMENT '修改人',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`,`lang`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='翻译字典表';
--INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('简介', 'zh_CN', '简介');如果将中文作为code则可以不录入中文数据
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('简介', 'en_US', 'Introduction');
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('简介', 'ja_JP', 'プロフィール');

INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('my_name', 'zh_CN', '我的名字');
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('my_name', 'en_US', 'My name');
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('my_name', 'ja_JP', '私の名前です');

INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('v.age', 'zh_CN', '年龄必须大于{ageN}岁');
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('v.age', 'en_US', 'Age must be greater than {ageN} years old');
INSERT INTO `i18n` (`code`, `lang`, `value`) VALUES ('v.age', 'ja_JP', '年齢は{ageN}歳より大きくなければなりません');

方案4 Plus

将方案4改良,依旧不动原数据表结构,增加一个翻译字典表但不需要传给前端,只需要在后台通过AOP编程统一翻译(使用翻译字典表来翻译)后再给前端。

总结:加表实现多语言,后台切面编程统一使用该表翻译
应用场景:
短文本类型

java代码I18nUtils .java如下

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class I18nUtils {

    private static final I18nMapper i18nMapper= SpringUtils.getBean(I18nMapper.class);

	public static String trans(String code, Object... args) {
		String lang=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Content-Language");
		return trans(lang,code,args);
	}
    public static String trans(String lang,String code, Object... args) {
        try{
	        LambdaQueryWrapper<I18n> lqw = Wrappers.lambdaQuery();
		    lqw.eq(I18n::getLang, lang);
	        lqw.eq(I18n::getCode, code);
	        I18n i18n= i18nMapper.selectOne(lqw);
            String[] s = lang.contains("_")?lang.split("_"):(lang.contains("-")?lang.split("-"):new String[]{lang,""});
            MessageFormat format=new MessageFormat(i18n.getValue(), new Locale(s[0].toLowerCase(),s[1].toUpperCase()));
            String newValue=format.format(args);
            return newValue;
        }catch (Exception e){
            return code;
        }
    }
}

方案5

所有有中文的表都增加一个lang列,每个语言都录入不同的数据。优点是稳定可控,翻译内容可以管理端随时调整,加翻译不需要修改代码和重新部署。缺点是改动量较多,运营麻烦(需录入和管理多个版本数据,即使大部分字段内容是不需要翻译的也需要录入两次),根据自身需求情况可以使用。

总结:加行实现多语言
应用场景:
用户协议表、文章表等超长文本类型,或者表中不需要翻译字段(列)小于3个的情况下使用

其他:

可根据上述方案组合使用。如超过100个字符的,可采用方案1或方案5;小于100个字符的,可采用方案4;小于100个字符并且需要翻译的字段少于3个的,可采用方案1;小于100个字符并且需要翻译的字段大于3个的,可采用方案5。

国际化遵循的原则

谁污染谁治理:
1.前端定义的字符串,前端(前端包括web、app、小程序等)自己处理(前端的翻译字典配置可以存储在数据库)。
2.后台定义的字符串,后台必须翻译后再传给前端(后台的翻译字典配置可以存储在数据库)。
3.数据库录入的字符串,必须由数据库配置不同版本的数据或者翻译。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐