一个极端的前端国际化方法
来源:互联网 发布:内存整型数据是什么 编辑:程序博客网 时间:2024/04/30 23:14
最近一直在做整个页面的国际化,相信很多小伙伴们都做过,前端主要采用的是Angularjs,后端使用的是Spring来做国际化,那么他们的优点,缺点现在一起来总结一下。其实无论用哪种语言做国际化,感觉都是千篇一律,只不过实现的方式不同而已。
1.定义国际化配置(什么CN啊EN啊之类的)
2.读取国际化配置
3.定义自己的国际化方式(是通过切面也好,工具类也好)
AngularJs国际化
1.定义国际化配置;
在自己指定的目录下定义国家化配置文件吧,大部分只要求实现中文和英文,所以定义两套,以key和value的形式。
en.json 文件内容如下:
{"100001":"Login","100002":"Register"}
cn.json 文件内容如下:
{"100001":"登录","100002":"注册"}2.读取国际化配置
AngularJs读取国际化配置的时候要引入Angularjs的相关Js。
//然后在页面引用进去<script src="/vender/angular-1.3.8.js"></script><script src="/vender/bower-angular-translate-2.4.2/angular-translate.min.js"></script><script src="/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.min.js"></script>
第一个文件 angular-1.3.8.js 就不用多说了.你懂的.第二个文件 angular-translate.min.js 是angular官方提供的国际化模块第三个文件 angular-translate-loader-static-files.min.js 模块是用来读取本地文件的模块,因为我们的翻译内容都是独立的 json 文件.
接下来通过注入依赖来读取配置文件:
var app = angular.module('myApp', ['pascalprecht.translate']).config(['$translateProvider',function($translateProvider){ var lang = window.localStorage.lang||'cn'; $translateProvider.preferredLanguage(lang); $translateProvider.useStaticFilesLoader({ prefix: '/i18n/', suffix: '.json' });}]);
分解的看下上面的代码:
var app = angular.module('myApp', ['pascalprecht.translate']);
这一句就是告诉我们已经把 angular-translate 模块以一个依赖项加载进来.
.config(['$translateProvider',function($translateProvider)
config 函数用 $translateProvider 服务配置 $translate 服务实现.
我们上面使用了 localStorage.lang 来存储用户上一次选择的语言,如果用户是第一次范围,默认显示中文(及 加载 cn.json 文件来翻译)
$translateProvider.preferredLanguage(lang)
这一句告诉 angular.js 哪种语言是默认已注册的语言.
$translateProvider.useStaticFilesLoader({ prefix: '/i18n/', suffix: '.json' });
上面的语句告诉我们 angular.js 应该加载本地那些国际化语言配置文件.
prefix : 指定文件前缀.
suffix: 指定文件后缀.
定义自己的国际化实现方式:现在我们先准备在html页面里做国际化,首先想到做一个过滤器,在html页面使用起来是最方便的. /filters/ 目录下创建 T.js 过滤器
angular.module("myApp").filter("T", ['$translate', function($translate) { return function(key) { if(key){ return $translate.instant(key); } };}]);过滤器也有两种实现方式,一种是通过双向绑定,在后台去过滤页面值,找到自己定义的国际化信息,并且输出。
假如登录的控制器 LoginCtrl.js 有一个登录标签需要做国际化:
angular.module('myApp').controller('LgoinCtrl', ['$scope','T', function($scope,T) { $scope.login=T.T(100001); }]);那么这意味着,页面的元素也需要绑定一个ng-model。
<span ng-model="login_title"></span>
那么使用另外一种,就直接在页面进行过滤了。
<span translate="login_title"></span>
Spring国际化
在网上找到了一篇写的不错的文章,这里转载一下,就懒得自己写了,这里说明一下为什么要做后台国际化,主要是对业务异常的信息提示,来做国际化的,这一块也会用到。
原文地址:http://a123159521.iteye.com/blog/1323317
覆写ResourceBundleMessage.
Spring留接口,其设计给力啊,很容易扩展。
- package org.frame.base.message;
- import java.io.UnsupportedEncodingException;
- import java.text.MessageFormat;
- import java.util.Locale;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- import org.springframework.context.support.ResourceBundleMessageSource;
- /**
- *
- * 扩展Spring的resourceBundleMessageSource
- * 使其支持中文,因为properties文件天生不支持中文,如果要其支持,需要转码,麻烦!
- *
- * 于是扩展一下,实现自动转码
- */
- public class ResourceBundleMessageSourceExtend extends
- ResourceBundleMessageSource {
- private static final String ENCODING = "GBK";// 注意属性文件使用GBK编码
- private static final String NULL = "null";
- /** cache the encoding key value * */
- Map<String, String> encodingCache = new ConcurrentHashMap<String, String>(
- 20);
- /**
- * resolve no argus
- */
- protected String resolveCodeWithoutArguments(String code, Locale locale) {
- String message = super.resolveCodeWithoutArguments(code, locale);
- return decodeString(message, ENCODING);
- }
- /**
- * resolve args
- * @see resolveCode(String code, Locale locale)
- */
- protected MessageFormat createMessageFormat(String msg, Locale locale) {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating MessageFormat for pattern [" + msg
- + "] and locale '" + locale + "'");
- }
- msg = decodeString(msg, ENCODING);
- return new MessageFormat((msg != null ? msg : ""), locale);
- }
- /**
- * 转码
- * @param msg
- * @param encode
- * @return
- */
- private String decodeString(String message, String encode) {
- String encodMessage = encodingCache.get(message);
- if (encodMessage == null) {
- try {
- encodMessage = new String(message.getBytes("ISO8859-1"), encode);
- if (message != null) {
- encodingCache.put(message, encodMessage);
- } else {
- encodingCache.put(message, NULL);
- // log the code is not exist in properties
- }
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- return encodMessage;
- }
- }
配置文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd ">
- <beans default-autowire="byName">
- <!-- 国际化资源文件 -->
- <bean id="messageSource" class="org.frame.base.message.ResourceBundleMessageSourceExtend">
- <property name="basenames">
- <list>
- <value>message/message</value>
- <value>message/error</value>
- </list>
- </property>
- </bean>
- </beans>
配置文件内容
- message.user.username = 用户名 !
- message.user.password = 密码 !
- message.user.context = 内容{0}真好 !
- message.user.username = user name !
- message.user.password = password !
- package org.frame.base.message;
- import java.util.Locale;
- import org.springframework.context.MessageSource;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class MessageSourceTest {
- public static void main(String[] args) {
- MessageSource messageSource = new ClassPathXmlApplicationContext("message/testMessage.xml");
- String message1 = messageSource.getMessage("message.user.username", null, Locale.CHINESE);
- //String message3 = messageSource.getMessage("message.user.username", null, Locale.CHINESE);
- String message4 = messageSource.getMessage("message.user.context", new Object[]{"ycl"}, Locale.CHINESE);
- String message2 = messageSource.getMessage("error.user.validName", null, Locale.CHINESE);
- System.out.println(message1);
- System.out.println(message2);
- System.out.println(message4);
- }
- }
配置文件使用GBK编码,不需要转码,就能够自动转码了
国际化其实很简单,调用语言自带的功能,实现起来还是很快的,就是每个地方都需要使用国际化,都要去定义模板,感觉好麻烦,很繁琐。
这里说说国际化的缺点;
1.定位错误不方便。使用了国际化之后我们的页面都是一个一个的KEY,你能保证你会记住每个key的含义吗,如果页面中某个字段报错了。我们肯定是根据这个字段查询到这个属性,然后再排查错误,现在页面都是key了,你要先去配置文件里面,找到这个字段,然后再根据配置文件里面的key,去找到页面的key。
2.耦合度太高,页面国际化在我看来应该是无关编程语言的,而我们无论是使用spring还是angularjs,还是jquery的i18N。都需要去引入相应的文件,然后在去定义响应的配置文件,响应的国际化方式。像angularjs这种,一旦哪一天像换个什么前端框架,这套国际化直接报废。
3.兼容性太差,我这里不是指浏览器的兼容,而是指各个JS框架之间的兼容,因为前端没有完全统一的关系。
4.效率低,先解析配置文件,然后再去匹配配置文件,从一堆JSON里面找出自己的那一个,效率确实会有影响,虽然很低。
其实无论使用上面的哪种方法都是把页面上的静态字段,定义成一个变量,然后在后台进行比对匹配,最后在预先定义的模板里面进行匹配,找出到那个显示给用户的字段。
我们都知道在HTML里面一个静态字段从来都不会很突兀的放在那里,它必然会跟有这样的标签;
我们来看下W3C对于这个标签的解释
浏览器支持
所有主流浏览器都支持 <span> 标签。
标签定义及使用说明
<span> 用于对文档中的行内元素进行组合。
<span> 标签没有固定的格式表现。当对它应用样式时,它才会产生视觉上的变化。如果不对 <span> 应用样式,那么 <span> 元素中的文本与其他文本不会任何视觉上的差异。
<span> 标签提供了一种将文本的一部分或者文档的一部分独立出来的方式。
提示和注释
提示:被 <span> 元素包含的文本,您可以使用 CSS 对它定义样式,或者使用 JavaScript 对它进行操作。
来了,重点来了,我现在要说一个极端的前端国际化方法。
首先假设我们所有的文本都被<span>标签所包裹着,当然还有更极端的就是自己去定义一个国际化标签,这个等会在讲。
然后我们的页面就这样去定义。注意span里面的内容。
<label for="disabledSelect"><span>联系人|linkman</span><!-- 联系人 --></label>
<label for="disabledSelect"><span>手机|phone</span><!-- 手机 --></label>这个"|",我把它当成是国际化标签分隔符,我认为我左边的是中文,右边的是英文,如果还要加韩语怎么办,我可以在添加一个"|",然后写上韩语。
接着写我们的JS方法,首先要记住一点,那就是在登陆的时候,肯定会去选择使用哪种语言。选择好了之后,记得保存起来,存到哪都可以,我这里是存到window里面去了,以后肯定是要读出来的。
var index=0;if(window.localStorage.lang=="cn"){ //匹配国际化的下标index=0;}if(window.localStorage.lang=="en"){index=1;} var aSpan=document.getElementById("registerDiv").getElementsByTagName("span"); //找到所有的span属性 for(var i=0;i<aSpan.length;i++){ var value=aSpan[i].innerText; var values=value.split("|"); //字符分割 aSpan[i].innerText=values[index]; }
来,是不是很简单,首先找到我们的国际化下标,这里通过window.localStorage.lang找到用户选择的语言,然后给它指定国际化下标,如果是中文的话,我们默认在第一个"|"前面,如果是英文的话,那就是在第一个"|"后面,以后在添加什么语言,只要改一行代码。然后在页面上加上我们的"|"属性就好了。来看看效果。
中文效果:
切换成英文的效果:
决定在加个韩语试试
<label for="disabledSelect"><span>联系人|linkman|담당자</span><!-- 联系人 --></label>
<label for="disabledSelect"><span>手机|phone|핸드폰</span><!-- 手机 --></label>
var index=0;if(window.localStorage.lang=="cn"){ //匹配国际化的下标index=0;}if(window.localStorage.lang=="en"){index=1;}if(window.localStorage.lang=="hy"){ //匹配韩语index=2;}
基本不需要配置啊,只需要大家去遵循这个语法就可以了,而且加载速度也是很快,根本不怎么影响。感兴趣的,可以在自己的项目里面试一试。
- 一个极端的前端国际化方法
- 一个内存泄露的极端例子
- 工厂方法模式的两个极端
- 搭建一个包含 redux、router、国际化的前端项目框架
- 从一个极端到另一个极端?
- PHP 接入SSL后的一个极端个例
- web前端的国际化 i18n
- 单进程浏览器解决物理资源居高不下的极端方法。
- 馋,极端的欲望
- 算法的极端情况
- 前端国际化
- 前端国际化
- 基于JavaScript纯前端的国际化解决方案
- Struts做的一个国际化
- 【转】恐怖的C++语言——C++语言操作符重载的一个极端例子
- 函数调用的极端形式
- eclipse对话框没下拉框时,在极端情况下遇到的一个小问题
- displaytag的国际化的方法
- JSP脚本元素、指令元素、动作元素
- windows下pytesseract识别验证码遇到的WindowsError: [Error 2] 的解决方法
- hdu5723 最小生成树 树形dp
- Apache 安全配置方法
- Linux下逻辑地址-线性地址-物理地址图解
- 一个极端的前端国际化方法
- 曲线拟合
- android小技巧
- 全新破解第一季
- Headstrong Student
- 5-19 求链式线性表的倒数第K项 (20分)
- rand函数
- 健康管理、运动以及认知技术的结合
- Python异常类的继承关系