一个极端的前端国际化方法

3676人阅读 评论(3) 收藏 举报

最近一直在做整个页面的国际化,相信很多小伙伴们都做过,前端主要采用的是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留接口,其设计给力啊,很容易扩展。 

Java代码  收藏代码
  1. package org.frame.base.message;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.text.MessageFormat;  
  5. import java.util.Locale;  
  6. import java.util.Map;  
  7. import java.util.concurrent.ConcurrentHashMap;  
  8.   
  9. import org.springframework.context.support.ResourceBundleMessageSource;  
  10.   
  11. /** 
  12.  *  
  13.  * 扩展Spring的resourceBundleMessageSource 
  14.  * 使其支持中文,因为properties文件天生不支持中文,如果要其支持,需要转码,麻烦! 
  15.  *  
  16.  * 于是扩展一下,实现自动转码 
  17.  */  
  18. public class ResourceBundleMessageSourceExtend extends  
  19.         ResourceBundleMessageSource {  
  20.   
  21.     private static final String ENCODING = "GBK";// 注意属性文件使用GBK编码  
  22.     private static final String NULL = "null";  
  23.   
  24.     /** cache the encoding key value * */  
  25.     Map<String, String> encodingCache = new ConcurrentHashMap<String, String>(  
  26.             20);  
  27.   
  28.     /** 
  29.      * resolve no argus 
  30.      */  
  31.     protected String resolveCodeWithoutArguments(String code, Locale locale) {  
  32.         String message = super.resolveCodeWithoutArguments(code, locale);  
  33.         return decodeString(message, ENCODING);  
  34.   
  35.     }  
  36.   
  37.     /** 
  38.      * resolve args 
  39.      * @see resolveCode(String code, Locale locale) 
  40.      */  
  41.     protected MessageFormat createMessageFormat(String msg, Locale locale) {  
  42.         if (logger.isDebugEnabled()) {  
  43.             logger.debug("Creating MessageFormat for pattern [" + msg  
  44.                     + "] and locale '" + locale + "'");  
  45.         }  
  46.         msg = decodeString(msg, ENCODING);  
  47.         return new MessageFormat((msg != null ? msg : ""), locale);  
  48.     }  
  49.   
  50.     /** 
  51.      * 转码  
  52.      * @param msg 
  53.      * @param encode 
  54.      * @return 
  55.      */  
  56.     private String decodeString(String message, String encode) {  
  57.         String encodMessage = encodingCache.get(message);  
  58.         if (encodMessage == null) {  
  59.             try {  
  60.                 encodMessage = new String(message.getBytes("ISO8859-1"), encode);  
  61.                 if (message != null) {  
  62.                     encodingCache.put(message, encodMessage);  
  63.                 } else {  
  64.                     encodingCache.put(message, NULL);  
  65.                     // log the code is not exist in properties  
  66.                 }  
  67.             } catch (UnsupportedEncodingException e) {  
  68.                 e.printStackTrace();  
  69.             }  
  70.         }  
  71.         return encodMessage;  
  72.     }  
  73.   
  74. }  


配置文件如下: 
Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd ">  
  3. <beans default-autowire="byName">  
  4.    
  5.       
  6.     <!-- 国际化资源文件 -->  
  7.     <bean id="messageSource" class="org.frame.base.message.ResourceBundleMessageSourceExtend">    
  8.            <property name="basenames">    
  9.                 <list>    
  10.                    <value>message/message</value>    
  11.                    <value>message/error</value>     
  12.                </list>    
  13.            </property>    
  14.     </bean>    
  15.        
  16. </beans>  


配置文件内容 
Java代码  收藏代码
  1. message.user.username = 用户名 !  
  2. message.user.password = 密码 !  
  3.   
  4. message.user.context = 内容{0}真好 !  


Java代码  收藏代码
  1. message.user.username = user name !  
  2. message.user.password = password !  


Java代码  收藏代码
  1. package org.frame.base.message;  
  2.   
  3. import java.util.Locale;  
  4.   
  5. import org.springframework.context.MessageSource;  
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  7.   
  8. public class MessageSourceTest {  
  9.    
  10.     public static void main(String[] args) {   
  11.            MessageSource messageSource = new ClassPathXmlApplicationContext("message/testMessage.xml");    
  12.            String message1 = messageSource.getMessage("message.user.username"null, Locale.CHINESE);  
  13.            //String message3 = messageSource.getMessage("message.user.username", null, Locale.CHINESE);  
  14.            String message4 = messageSource.getMessage("message.user.context"new Object[]{"ycl"}, Locale.CHINESE);  
  15.            String message2 = messageSource.getMessage("error.user.validName"null, Locale.CHINESE);  
  16.            System.out.println(message1);  
  17.            System.out.println(message2);  
  18.            System.out.println(message4);  
  19.     }  
  20.   
  21. }  


配置文件使用GBK编码,不需要转码,就能够自动转码了 

国际化其实很简单,调用语言自带的功能,实现起来还是很快的,就是每个地方都需要使用国际化,都要去定义模板,感觉好麻烦,很繁琐。

这里说说国际化的缺点;

1.定位错误不方便。使用了国际化之后我们的页面都是一个一个的KEY,你能保证你会记住每个key的含义吗,如果页面中某个字段报错了。我们肯定是根据这个字段查询到这个属性,然后再排查错误,现在页面都是key了,你要先去配置文件里面,找到这个字段,然后再根据配置文件里面的key,去找到页面的key。

2.耦合度太高,页面国际化在我看来应该是无关编程语言的,而我们无论是使用spring还是angularjs,还是jquery的i18N。都需要去引入相应的文件,然后在去定义响应的配置文件,响应的国际化方式。像angularjs这种,一旦哪一天像换个什么前端框架,这套国际化直接报废。

3.兼容性太差,我这里不是指浏览器的兼容,而是指各个JS框架之间的兼容,因为前端没有完全统一的关系。

4.效率低,先解析配置文件,然后再去匹配配置文件,从一堆JSON里面找出自己的那一个,效率确实会有影响,虽然很低。

其实无论使用上面的哪种方法都是把页面上的静态字段,定义成一个变量,然后在后台进行比对匹配,最后在预先定义的模板里面进行匹配,找出到那个显示给用户的字段。

我们都知道在HTML里面一个静态字段从来都不会很突兀的放在那里,它必然会跟有这样的标签;

我们来看下W3C对于这个标签的解释

浏览器支持

Internet ExplorerFirefoxOperaGoogle ChromeSafari

所有主流浏览器都支持 <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里面去了,以后肯定是要读出来的。



然后写我们的JS方法。

	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;
	}


基本不需要配置啊,只需要大家去遵循这个语法就可以了,而且加载速度也是很快,根本不怎么影响。感兴趣的,可以在自己的项目里面试一试。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:239221次
    • 积分:4166
    • 等级:
    • 排名:第7389名
    • 原创:149篇
    • 转载:37篇
    • 译文:8篇
    • 评论:81条
    博客专栏
    最新评论
    spring学习之路
    开始重新学习spring了,记录自己的点点滴滴,加油吧,少年。