WEB网站国际化的一种解决方案

场景

这里简单用一个详情页面开始我们的国际化讲解

国际化页面布局.png

这个场景中,logo需要国际化,tab以及面包屑的文字需要国际化,详情需要国际化,动态详情以及动态详情中的文字需要国际化。
经过总结,国际化数据会有四种类型

  • 后台管理配置的动态数据
  • 服务端渲染的静态的文字
  • js渲染的静态文字
  • 静态的图片

后台管理配置的动态数据

场景图中详情数据是通过一个后台管理系统管理,数据通过语言隔离的方式管理,接口设计大致如下
语言管理

创建语种 [POST] /m/languages
更新语种 [PUT] /m/languages/{id}
查询语种 [GET] /c/languages/{id}
查询语种列表 [GET] /c/languages?$offset=偏移量&$limit=数量&$count=true&state=1

数据管理

创精文章 [POST] /m/languages/{language_id}/articles
更新文章 [PUT] /m/languages/articles/{article_id}
删除文章 [DELETE] /m/languages/articles/{article_id}
查询文章 [GET] /c/languages/articles/{article_id}
查询文章列表 [GET]/c/languages/{language_id}/articles?$offset=偏移量&$limit=数量&$count=true

服务端渲染的静态的文字

如场景图中面包屑,比如面包屑文字首页->详情, 像这种固定的文字不适合在后台管理由运维人员配置。用文件统一存储这些国际化的文件比较合适。这里采用SpringMVC自带的国际化解决方案。
配置ResourceBundleMessageSource

@Configuration
public class I18nConfig {
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("i18n");
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

基于Cookie的国际化实现

 @Bean
 public CookieLocaleResolver localeResolver() {
   CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
   cookieLocaleResolver.setCookieName("lang");
   return cookieLocaleResolver;
 }

SpringMVC国际化的的resolver有很多,用法也很多样,可以参考这篇博客,这里就不造轮子了()

国际化语言拦截器

@Component
public class I18nInterceptor extends HandlerInterceptorAdapter {
    public static final String DEFAULT_PARAM_NAME = "lang";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws ServletException {

        String newLocale = request.getParameter(DEFAULT_PARAM_NAME);
        if (newLocale != null) {
            LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
            if (localeResolver == null) {
                throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
            }
            localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale));
        }
        // Proceed in any case.
        return true;
    }
}

这里拦截器显示多语言路由的实现是通过改变参数来实现语言切换

http://i18n.example.com?lang=en
http://i18n.example.com?lang=cn

费些功夫通过改写这个拦截器的实现以及路由的设计,可以把国际化的路由改写成这样子

http://i18n.example.com/en
http://i18n.example.com/cn

在resources中配置配置国际化语言文件

i18n.properties    默认
i18n_en.properties 英文
i18n_cn.properties 中文

添加el表达式

public static String i18n(String key) {
  RequestContext requestContext = new RequestContext(SpringUtil.getRequest());
  return requestContext.getMessage(key);
}
在tld配置
<function>
    <description>国际化</description>
    <name>i18n</name>
    <function-class>com.example.utils.ElFuncUtil
    </function-class>
    <function-signature>java.lang.String i18n(java.lang.String))</function-signature>
    <example>${elf:i18n(key)}</example>
  </function>

使用

<span>${elf:i18n("详情")}</span>

很遗憾,Spring自带的国际化方案有一个缺陷:配置文件是内置在项目代码中的,无法剥离到后台统一管理,这样子如果新增一种语言,就必须动到项目代码。
有能力的同学可以尝试改写ResourceBundleMessageSource,能支持读取远端的配置文件,这样就完美了。

js渲染的静态的文字

场景图中动态推荐部分是用js渲染出来的,其中会出现全部等一些静态的文本。这些文本也需要国际化。我在项目中使用现在最受欢迎的js框架vue。
配合使用(vue-i18n)[http://kazupon.github.io/vue-i18n/en/started.html]进行国际化。
引入vue-i18n

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from './message.json'
import { langCode } from '../lang'

Vue.use(VueI18n)

export default new VueI18n({
  locale: langCode,
  messages
})

message.json

"cn": {
  "全部": "全部"
},
"en": {
  "全部": "All"
}

使用

vueI18n.t('全部')
// 或者,在template模板中
<p>{{ $t("全部") }}</p>

静态的图片

场景图中的logo根据不同语言也需要不同,这样的图片需要直接其访问其图片路径,我们约定图片的访问路径规则。

<img src="/images/logo_${lang}.svg" onerror='this.src="${ctx}/images/logo_en.svg"'/>

抽取配置的自动化脚本

国际化的文本处于各个代码文件中,手动拷贝到配置文件是一件效率相当底下且容易出错的事情。故写个自动化脚本溜起来。
抽取国际化文本成json文件的自动化脚本实现
抽取国际化文本成properties文件的自动化脚本实现
把properties中的文件转移到xlsx文件中的自动化脚本实现

结语

随着咱国家的国力不断强盛,国际影响力不断扩大。会有更多更好的产品走向世界,国际化已经成为势不可挡的趋势。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容