`

一个i18n的方案请人拍砖

阅读更多
长久以前做18n一直靠Strurts的resource bundler的方法,从properties里读取一个个key的值来对应显示正确的语言文字,对于大部分场景这都是满足的。

但是对于有些情况,Resource bundler就不一定适合,比说产品的名称,一个很简单的例子,我的产品中文叫“钢笔,铅笔”,英文叫“pen,pencil",在搜索场景中,国内客户输入的就是“笔”--他想要钢笔铅笔的报价,国外用户可能输入“pen"--他只要钢笔的价格。那么resource bundler在这种情景下就有点力不从心。---可能方案,parser对应的properties,对应line的key读出来,然后读数据库,取信息....(也许luncene有更好的方案,不过偶不知道)。

因此我的初步解决方法是,将各国语言都存储到数据库中去,利用java 5.0的annotation标注其适用的field,例子如下。

//POJO 在hibernate mapping 是field不是properties
public class Major implements Serializable {
	@DocumentId
	private int id = 0;
	@SuppressWarnings("unused")
	@Field(name="subject",index = Index.TOKENIZED, store = Store.NO)
	@Localization(language = "en", country = "US", acquiescence = true)
	private String subject_english = StringUtils.EMPTY;
	@SuppressWarnings("unused")
	@Field(name="subject",index = Index.TOKENIZED, store = Store.NO)
	@Localization(language = "zh", country = "CN")
	private String subject_chinese = StringUtils.EMPTY;
        
        // get/set Id 从略

          public String getSubject(){
            return Translator.translate(this, CurrentUser.getLocale());
        }

        public void setSubject(String subject){

        }
         
}



Localization是个简单的annotation标签,它有四个参数,前面三个分别是langauge,country和variant,于java.util.Locale的构造参数是一样的,用于构造一个可比较的locale变量,第四个参数acquiescence用于指定某个field是否为默认显示。(default被java给占用了)。

无论我们指定了多少种语言的field,默认暴露出来的就只有subject一个属性,在getSubject中,我写了一个简单的Translator来parser匹配当前locale的field. CurrentUser是个辅助类,从ServletContext中读当前locale,如果用Spring的话,可以直接wrap
LocaleContextHolder.getLocale();


接下来是Translator的内容,并未做什么太多事情

import java.util.Locale;
import java.lang.reflect.Field;
import org.apache.commons.lang.StringUtils;

public class Translator {

	public static String translate(Object object, Locale locale)
			throws IllegalAccessException {
		String result = StringUtils.EMPTY;
		for (Field field : object.getClass().getFields()) {
			field.setAccessible(true);
			Localization i18n = (Localization) field
					.getAnnotation(Localization.class);
			// construct a local for compare
			if (locale.equals(new Locale(i18n.language(), i18n.country(), i18n
					.variant()))) {
				result = field.get(object).toString();
				break;
			}

			// set a default value if no found
			if (i18n.acquiescence() && StringUtils.isEmpty(result))
				result = field.get(object).toString();
		}
		return result;
	}
}


利用了反射读每一个field的标签,找到适合的值返回而已。

这个方案好处是,对搜索是透明的,无须干预后台生成索引,也无须干预hibernate search的搜索过程,理论上可以满足前面提出的应用场景,请大家拍砖。
分享到:
评论
8 楼 yunhaifeiwu 2008-10-31  
http://cache.baidu.com/c?m=9d78d513d99d1aee1eadc33f505197374e04c73f6a8987027fa5cb1bd7374c41367195be30511013a2b66b1677bb0e1cb4ff6c34714137b6e8d5951a83e6c73f2fff71692c4bc30005d36efe9619389262c304b4f34cfae9b063c9f59684c25451c85418788aed8b5d1d13ca6bf0103af4a6ee1b541d5a&p=882a9642919c16fd0be2932a5747&user=baidu

这是一个简单介绍!
7 楼 yunhaifeiwu 2008-10-31  
 public class ProgramResources_de extends ListResourceBundle{
    public Object [][] getContents() {return contents; }
    private static final Object[][] contents    {
        {"button", "Rechnen"}, 
        {"backColor", Color.black},
        {"defaultSie", double[] {210, 297}}
    }
}

public class ProgramResources_en_US extends ListResourceBundle{
    public Object [][] getContents() {return contents; }

    private static final Object[][] contents {
        {"button", "Compute"}, 
        {"backColor", Color.blue},
        {"defaultSie", double[] {216, 279}}
    }
}



-------------------------------------
这是大体一个ListResourceBundle。没有测试,自已查阅相关资料吧!



回到你的苹果问题上:

   如果要显示中国的文字,要把汉字资料“苹果“读入到 ProgramResources_ch_ZH 中,

   如果要显示美国的文字,要把英文资料”apple",读到 ProgramResources_en_US 中。

   (至于你的文字资料,要放在哪个数据库,随你!只要你按要求读入到相应的类对象中就行了。
一种合理的方式时,在加载资料时,要先得例用者的地区信息,然后只加载该地区的资料!)

   同时你的Pojo 的属性,要引用ListResourceBundle的key (估计不错的法,

java允许注释方式引用。 )。

   这样,java会自动根据用户所在的地区,给你的POJO注入相应地区的数据。

  即:
   中国,你的pojo 的id="苹果"
   在美国,你的pojo 的id="apple"

   但你的 pojo却是同一个!
6 楼 yunhaifeiwu 2008-10-31  
ray_linn 写道
yunhaifeiwu 写道
java在支持国际化时,除了属性文件化,还可用list;

你还不如直接把数据库的资料,读到list中。

汗!!!!!!!!!!

这是部份代码,仅供抛砖引玉用

        //获取系统的区域与语言默认设置
        Locale locale = Locale.getDefault();
       

        String ret;
        ListResourceBundle  aa=new ListResourceBundle () {
             Object[][] contents ={{"key","hello"},{"key1","你好"}};
            @Override
            protected Object[][] getContents() {return contents; }
        };
        ret=  aa.getString("key1");





============================


你这不是又回到了我最早的问题吗? 我输入"hello"和"你好",如何得到同一个POJO,因为他们就是同一个POJO啊。
这就好比“苹果”和apple都可以返回产品清单中的" id =2".



ListResourceBundle  与struts2属性文件一样,自动根所设定地区代号,选择是哪一类别的资料。

如果你是中国,就会得到 苹果!
如果你是英国,就会得到 apple!
但Pojo却是同一个.

注意:java对国际化支持,
一是属性文件(struts就是利用这个)
二是list性,相关处理类是 ListResourceBundle.
list一样会成相同的键,而对应的语言不同存放资料,然后自动根据地区选择语言。

如何使用ListResourceBundle  请查阅相关资料!


5 楼 ray_linn 2008-10-31  
yunhaifeiwu 写道
java在支持国际化时,除了属性文件化,还可用list;

你还不如直接把数据库的资料,读到list中。

汗!!!!!!!!!!

这是部份代码,仅供抛砖引玉用

        //获取系统的区域与语言默认设置
        Locale locale = Locale.getDefault();
       

        String ret;
        ListResourceBundle  aa=new ListResourceBundle () {
             Object[][] contents ={{"key","hello"},{"key1","你好"}};
            @Override
            protected Object[][] getContents() {return contents; }
        };
        ret=  aa.getString("key1");





============================


你这不是又回到了我最早的问题吗? 我输入"hello"和"你好",如何得到同一个POJO,因为他们就是同一个POJO啊。
这就好比“苹果”和apple都可以返回产品清单中的" id =2".
4 楼 yunhaifeiwu 2008-10-31  
java在支持国际化时,除了属性文件化,还可用list;

你还不如直接把数据库的资料,读到list中。

汗!!!!!!!!!!

这是部份代码,仅供抛砖引玉用

        //获取系统的区域与语言默认设置
        Locale locale = Locale.getDefault();
       

        String ret;
        ListResourceBundle  aa=new ListResourceBundle () {
             Object[][] contents ={{"key","hello"},{"key1","你好"}};
            @Override
            protected Object[][] getContents() {return contents; }
        };
        ret=  aa.getString("key1");





============================
3 楼 sorphi 2008-10-31  
Hibernate Search倒是支持一个property映射到不同Field,在文档中也没有你那种例子,我想了一下好像也不可行。
分词的问题我倒是没有考虑,只想到中文分词器一般可以用于按空格隔开单词的语种中。
2 楼 ray_linn 2008-10-31  
sorphi 写道
在一个pojo中定义多个相同name的@Field,没见过这种用法,不知道是否可行?这样如何存储到Document中?存储进去才能被搜索得到。

就这个简单的例子来讲,你的方案复杂了,直接就是定义一个@Field(name="keyword"),把所有语种的关键词都存储起来即可。

#   public String getSubject(){ 
#             return Translator.translate(this, CurrentUser.getLocale()); 
#         } 
索引的时候,没法构造CurrentUser.getLocale()这个环境了。这个只能用于特定用户显示结果用。



1。 同name的field应该是可实现的,详细我可以做个试验看看。因为我是利用hibernate Search,存储是的时候会直到被POJO持久化。

2. 我分成不同的field,是为了配合不同的分词器.hibernate search 3.1似乎可以支持不同分词器,目前似乎还没有万国分词器啊。

3.  我只写了getSubject,setSubject的locale变量不来自于CurrentUser,而是来自于某个Setting,一般场景是后台输入人员选择了语言的种类,因此详细我就没在写出来。


1 楼 sorphi 2008-10-31  
在一个pojo中定义多个相同name的@Field,没见过这种用法,不知道是否可行?这样如何存储到Document中?存储进去才能被搜索得到。

就这个简单的例子来讲,你的方案复杂了,直接就是定义一个@Field(name="keyword"),把所有语种的关键词都存储起来即可。

#   public String getSubject(){ 
#             return Translator.translate(this, CurrentUser.getLocale()); 
#         } 
索引的时候,没法构造CurrentUser.getLocale()这个环境了。这个只能用于特定用户显示结果用。

相关推荐

Global site tag (gtag.js) - Google Analytics