EWeb4J 國際化支持出爐啦(有源碼看)

很榮幸也很開心地公佈,EWeb4J 支持國際化了。並且有着它獨特的味道。首先我們來看看最終的效果。


package test.i18n;

public class TestI18N {

@BeforeClass
public static void prepare() throws Exception {
String err = EWeb4JConfig.start("start.eweb.xml");
if (err != null){
System.out.println(">>>EWeb4J Start Error --> " + err);
System.exit(-1);
}
}

@Test
public void testVarable() throws Exception{
Map map = Props.getMap("Message");

Lang.set(Locale.US);
Assert.assertEquals("Welcome to eweb4j !", map.get("welcome"));

Lang.set(Locale.SIMPLIFIED_CHINESE);
Assert.assertEquals("歡迎使用 eweb4j 框架 !", map.get("welcome"));
}

}


PS:

Map map = Props.getMap("Message");
Lang.set(Locale.CHINESE);
map.get("welcome");
Lang.set(Locale.EN);
map.get("welcome");


[b][color=red]這個map獲取之後,每次更改 locale,而不需要重新獲取map,因爲 map.get("key") 已經被代理了[/color][/b]

看到了麼?還是使用之前版本的 Props 來獲取鍵值。使用上來說沒有任何需要重新學習的地方。當然,你可以看到有個設置國際化語言的代碼段:

Lang.set(Locale.US);
Lang.set(Locale.SIMPLIFIED_CHINESE);


怎麼樣?跟Play有點像?wow, 不不不。Play是這麼用的:Lang.set(""),它是設置一個字符串,而在這是設置一個標準的 java.util.Locale。

有人問,支持 web 訪問(瀏覽器語言設置等等)自動識別本地語言來進行國際化支持麼?
答案是: Yes !

看看這個HelloAction.


package org.eweb4j.crud;
import org.eweb4j.cache.Props;

public class HelloAction {

public String doWelcome(){
return Props.getMap("Message").get("welcome");
}
}


是吧,真的跟上個版本一樣調用的還是Props接口。

好了,在代碼層面上就到這兒了,我們看看配置。

[*] 1.首先,start.xml多了一個配置

<locales>
<locale language="en" country="US" />
<locale language="zh" country="CN" />
</locales>


PS:後面的 country 是可選的

[*] 2.其次,準備好你的 properties 文件,老規矩,在 start.xml:

<properties>
<file id="Message" path="message.properties" />
</properties>


注意,這裏的file的所有命名不需要跟國際化有任何關係。

[*] 3.雖然上面的配置文件裏不需要寫什麼國際化語言,但是在你的文件系統裏,你至少得準備好兩份文件:

message_en_US.properties//或者message_en.properties
message_zh_CN.properties//或者message_zh.properties

PS:這種命名法跟 Java 的國際化標準是一致的。國家country可以不填寫。

[*] 4.文件內容:
message_en.properties

#eweb4j last update 'framework' value
#Sat May 05 02:00:40 CST 2012
framework=eweb4j
welcome=Welcome to ${framework} !


message_zh_CN.properties

#eweb4j last update 'framework' value
#Sat May 05 02:00:40 CST 2012
framework=eweb4j
welcome=歡迎使用 ${framework} 框架 !


PS:你可以很放心的使用中文!!這些文件目錄也可以隨便放,不一定都放在ClassPath中,因爲EWeb4J沒有使用ResourceBundle來處理它們。

好了,大概就這麼多。下面稍微再說說:

[*] EWeb4J 框架會自動的識別加載到你的國際化文件,而不需要顯式的在配置文件指明,只需要指定一個BaseName即可。說到BaseName,還要說說Java國際化的內容,這裏不詳細說明,大概瞭解下,國際化資源文件命名規範:(優先級從上到下遞增)

[*] baseName.properties//例如 message.properties
[*] baseName_language.properties//例如 message_zh.properties
[*] baseName_language_country.properties//例如 message_zh_CN.properties

更加詳細的內容請查閱相關資料。

對於 EWeb4J 框架來說,只需要告訴它 baseName 即可。另外,這個 baseName.properties是否必須存在跟另外兩個文件有關係,如果另外兩個存在(一個或都存在),那這個 baseName.properties 就可以不存在,框架是不會自動生成的。否則,框架會自動生成。

最後發一下EWeb4J實現國際化的幾個關鍵源碼:


//代理類,在Props.java內部
private static class MapProxy<K, V> implements Map<K,V>{
private String id;

private Map<K,V> map(){
String _id = id+"_"+Lang.get().toString();
if (props.containsKey(_id))
return (Map<K, V>) props.get(_id);
else{
_id = id + "_" + Lang.get().getLanguage();
if (props.containsKey(_id))
return (Map<K, V>) props.get(_id);
}

return (Map<K, V>) props.get(id);
}

public MapProxy(String id){
this.id = id;
}

public V get(Object key) {
Map map = this.map();
return (V) (map == null ? null : map.get(key));
}
//......
}

//在獲取緩存的時候,判斷該ID是否擁有國際化資源文件,若有,使用代理
public static Map<String, String> getMap(String id) {
if (i18nIds.contains(id))
return new MapProxy<String, String>(id);

return props.get(id);
}

// 讀取properties的全部信息
public static synchronized String readProperties(Prop f, boolean isCreate)throws IOException {
if (f == null || f.getPath().length() == 0)
return null;

String id = f.getId();
String path = f.getPath();
ConfigBean cb = (ConfigBean) SingleBeanCache.get(ConfigBean.class);
I18N i18n = cb.getLocales();

final String sufPro = ".properties";

if (i18n != null){
for (Locale l : i18n.getLocale()){

String suffix1 = "_"+l.getLanguage()+"_"+l.getCountry();
String tmpPath1 = path.replace(sufPro, suffix1 + sufPro);
i18nIds.add(id);
if (FileUtil.exists(ConfigConstant.CONFIG_BASE_PATH+ tmpPath1)){
Prop p = new Prop();
p.setGlobal("false");
p.setId(id + suffix1 );
p.setPath(tmpPath1);
readProperties(p, false);//遞歸,把國際化文件內容加載僅緩存
isCreate = false;// 如果存在國際化文件,那麼默認的文件允許不存在
continue;
}

String suffix2 = "_"+l.getLanguage() ;
String tmpPath2 = path.replace(sufPro, suffix2 + sufPro);

if (FileUtil.exists(ConfigConstant.CONFIG_BASE_PATH+ tmpPath2)){
Prop p = new Prop();
p.setGlobal("false");
p.setId( id + suffix2 );
p.setPath( tmpPath2 );
readProperties(p, false);//遞歸,把國際化文件內容加載僅緩存
isCreate = false;// 如果存在國際化文件,那麼默認的文件允許不存在
continue;
}
}
}

//............
}


[color=red] !!正是因爲這個代理類 MapProxy 的存在,所以在使用 Props.getMap("Message") 的 get("welcome") 的時候纔會去找到匹配的國際化資源信息。這個很重要!![/color]


package org.eweb4j.i18n;

import java.util.List;
import java.util.Locale;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eweb4j.config.ConfigConstant;
import org.eweb4j.config.Log;
import org.eweb4j.config.LogFactory;
import org.eweb4j.config.bean.I18N;
import org.eweb4j.mvc.Context;
import org.eweb4j.mvc.MVC;
import org.eweb4j.mvc.MVCCons;


/**
* EWeb4j 國際化支持
* @author weiwei
*
*/
public class Lang {

private static Log log = LogFactory.getMVCLogger(Lang.class);

private static ThreadLocal<Locale> current = new ThreadLocal<Locale>();

public static Locale get(){

Locale locale = current.get();
if (locale == null){
Context ctx = MVC.current.get();
if (ctx != null){
HttpServletRequest req = ctx.getRequest();
HttpServletResponse res = ctx.getResponse();
if (req != null){
resolve(req, res);
}else{
setDefaultLocale();
}
}else{
setDefaultLocale();
}

locale = current.get();
}

return locale;
}

private static void resolve(HttpServletRequest req, HttpServletResponse res) {
Cookie[] cookies = req.getCookies();
if (cookies != null){
for (Cookie cookie : cookies){
if (MVCCons.COOKIE_KEY_LOCALE.equals(cookie.getName())){
String loc = cookie.getValue();
if (loc == null)
continue;

if (loc.indexOf("_") > 0){
String[] locs = loc.split("_");
if (set(new Locale(locs[0], locs[1]))){
return ;
}
}

if (set(new Locale(loc)))
return;
}
}
}

Locale locale = req.getLocale();

set(locale);

res.addCookie(new Cookie(MVCCons.COOKIE_KEY_LOCALE, locale.toString()));
}

public static boolean set(Locale locale){
if (I18N.get().contains(locale)){
log.debug("Locale is set -> %s", locale.toString());
current.set(locale);
return true;
}

log.warn("Locale %s is not defined in your %s > i18n", locale, ConfigConstant.START_FILE_NAME);
return false;
}

public static void clear(){
current.remove();
}

public static void change(Locale locale){
if (get() == null){
if (set(locale))
MVC.current.get().getResponse().addCookie(new Cookie(MVCCons.COOKIE_KEY_LOCALE, locale.toString()));
}else{
if (!get().equals(locale)){
if (set(locale))
MVC.current.get().getResponse().addCookie(new Cookie(MVCCons.COOKIE_KEY_LOCALE, locale.toString()));
}
}
}

private static void setDefaultLocale() {
List<org.eweb4j.config.bean.Locale> locales = I18N.get().getLocale();
if (locales == null || locales.isEmpty()){
set(Locale.getDefault());
} else{
set(new Locale(locales.get(0).getLanguage(), locales.get(0).getCountry()));
}
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章