这里简单用一个详情页面开始我们的国际化讲解
这个 场景中,logo需要国际化,tab以及面包屑的文字需要国际化,详情需要国际化,动态详情以及动态详情中的文字需要国际化。 经过总结,国际化数据会有四种类型
场景图中 详情
数据是通过一个后台管理系统管理,数据通过语言隔离的方式管理,接口设计大致如下 语言管理
创建语种 [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; } }
@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
i18n.properties 默认 i18n_en.properties 英文 i18n_cn.properties 中文
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框架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 })
"cn": { "全部": "全部" }, "en": { "全部": "All" }
vueI18n.t('全部') // 或者,在template模板中 <p>{{ $t("全部") }}</p>
场景图中的 logo
根据不同语言也需要不同,这样的图片需要直接其访问其图片路径,我们约定图片的访问路径规则。
<img src="/images/logo_${lang}.svg" onerror='this.src="${ctx}/images/logo_en.svg"'/>