Fork me on GitHub

Programming Design Notes

ZK I18N Advanced

| Comments

以前也已經講解過 ZK 轉換語言的方法: ZK I18N

因為考慮到 ZK 頁面可能會由數個 ZK 元件組合出來,如果每一個元件也要加入 ZK Controller 來設定語言會很麻煩,所以想了一個方法去解決這個問題。

在這裡我會在 index.zul 使用 Border Layout,使以會有 5 個元件頁面,分別是 North.zul, East.zul, Center.zul, West.zul, South.zul。而 Center.zul 將會放一個控制語言的元件用作轉換其他元件的語言。

首先在新增一個 component folderWEB-INF 內。

再新增 East.zulcomponent folder內。
/WEB-INF/component/East.zul:
<window id="East" border="none">
<label id="pageName" value="East" />
<button id="btn" label="Button" />
</window>

要記住每一個 zul 的頁面的 Root Element id 要跟 zul 頁面名稱一樣,之後用到時比較好分辨。

/WEB-INF/component/North.zul:
<window id="North" border="none">
<label id="pageName" value="North" />
<button id="btn" label="Button" />
</window>

/WEB-INF/component/South.zul:
<window id="South" border="none">
<label id="pageName" value="South" />
<button id="btn" label="Button" />
</window>

/WEB-INF/component/West.zul:
<window id="West" border="none">
<label id="pageName" value="West" />
<button id="btn" label="Button" />
</window>

現在新增 index.zul 在根目錄。
/index.zul:
<?page title="Index Page"?>
<zk>
<borderlayout>
<north><include src="/WEB-INF/component/North.zul" /></north>
<east><include src="/WEB-INF/component/East.zul" /></east>
<center></center>
<west><include src="/WEB-INF/component/West.zul" /></west>
<south><include src="/WEB-INF/component/South.zul" /></south>
</borderlayout>
</zk>

打開 index.zul 頂面看看是否這個樣子:

現在新增一個 Java Class 是轉換語言的核心元件。只是很簡單的程式碼,看不明再問我吧。
com.ctlok.pro.zk.language.I18N:
package com.ctlok.pro.zk.language;

import java.util.List;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zul.Button;
import org.zkoss.zul.Label;

public class I18N {

@SuppressWarnings("unchecked")
public static void setLanguage(Component rootComponent) {
List<Component> components = (List<Component>) rootComponent
.getChildren();
for (int i = 0; i < components.size(); i++) {
Component component = components.get(i);
setComponentLanguage(component);
if (component.getChildren().size() > 0)
setLanguage(component);
}
}

private static void setComponentLanguage(Component component) {
IdSpace owner = component.getSpaceOwner();
if (owner instanceof Component) {
String ownerId = ((Component)owner).getId();
String i18n = Labels.getLabel(ownerId + "." + component.getId());
if (i18n != null) {
if (component instanceof Label)
((Label) component).setValue(i18n);
else if (component instanceof Button)
((Button) component).setLabel(i18n);
}
}
}
}

有了核心元件,也要有語言檔案才行。
/WEB-INF/i3-label.properties:
#i18n
North.pageName=North
North.btn=Button

East.pageName=East
East.btn=Button

South.pageName=South
South.btn=Button

West.pageName=West
West.btn=Button

/WEB-INF/i3-label_zh.properties:
#i18n
North.pageName=北
North.btn=按鈕

East.pageName=東
East.btn=按鈕

South.pageName=南
South.btn=按鈕

West.pageName=西
West.btn=按鈕

/WEB-INF/i3-label_en.properties:
#i18n
North.pageName=North
North.btn=Button

East.pageName=East
East.btn=Button

South.pageName=South
South.btn=Button

West.pageName=West
West.btn=Button

/WEB-INF/i3-label_ja.properties:
#i18n
North.pageName=北
North.btn=ボタン

East.pageName=東
East.btn=ボタン

South.pageName=南
South.btn=ボタン

West.pageName=西
West.btn=ボタン

一共有 4 個語言檔,而 i3-label.properties 是預設的語言檔案,當找不到合適的語言時便會用到。

North.pageName=North 前面的 North 就是剛才提到的頁面 Root Element id,這樣便可以分到那一個元件是那一個 zul 頁面了,也不會怕和其他頁面的元件 ID 相撞。

有了這些還不夠,還要有一個元件去結用戶去選擇語言才行。

新增一個 com.ctlok.pro.zk.component.LanguageCombobox:
package com.ctlok.pro.zk.component;

import java.util.Locale;

import org.zkoss.util.Locales;
import org.zkoss.web.Attributes;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.SimpleListModel;

import com.ctlok.pro.zk.language.I18N;

public class LanguageCombobox extends Combobox {

private static final long serialVersionUID = 1L;

public LanguageCombobox() {
Locale[] model = new Locale[3];
model[0] = Locale.ENGLISH;
model[1] = Locale.CHINESE;
model[2] = Locale.JAPANESE;

setModel(new SimpleListModel(model));
}

public void onInitRenderLater() throws InterruptedException {
Locale locale = Locales.getThreadLocal();
ListModel languages = this.getModel();
setSelectedIndex(0);
for (int i = 0; i < languages.getSize(); i++) {
if (locale.getDisplayLanguage().equals(
((Locale) languages.getElementAt(i)).getDisplayLanguage())) {
setSelectedIndex(i);
return;
}
}
}

public void onCreate() {
I18N.setLanguage(getRoot());
}

public void onChange() {
Locale locale = (Locale) this.getModel().getElementAt(
this.getSelectedIndex());
setDefaultLanguage(locale);
I18N.setLanguage(getRoot());
}

public void setDefaultLanguage(Locale locale) {
Session session = Sessions.getCurrent();
session.setAttribute(Attributes.PREFERRED_LOCALE, locale);
Locales.setThreadLocal(locale);
}

}
在 Constructor 內設定了 3 種語言選擇。

onInitRenderLater() 選取用戶現在正在使用的語言。

onCreate() 將頁面的語言轉換為用戶正在使用的語言,如果還用戶還未選擇語言,跟瀏覽器的語言來設定。

onChange() 用戶轉換語言時會將預設的語言設定為用戶所選擇語言。

setDefaultLanguage() 設定用戶預設語言。

加入此元件的頁面也會自動設定該頁面的語言,很方便吧。

現在元件已經有了,那放到 zul 頁面顯示出來。

新增 /WEB-INF/component/Center.zul:
<window id="Center" border="none">
<combobox use="com.ctlok.pro.zk.component.LanguageCombobox" />
</window>
/index.zul 頁面亦要改一改。 index.zul:
<?page title="Index Page"?>
<zk>
<borderlayout>
<north><include src="/WEB-INF/component/North.zul" /></north>
<east><include src="/WEB-INF/component/East.zul" /></east>
<center><include src="/WEB-INF/component/Center.zul" /></center>
<west><include src="/WEB-INF/component/West.zul" /></west>
<south><include src="/WEB-INF/component/South.zul" /></south>
</borderlayout>
</zk>

以下是轉換各種語言的樣子:




範例: zk-i18n-advanced.zip (Eclipse Project)
**** 需要自行加上 ZK 5.01 Library ****

相關書籍: ZK: Ajax without the Javascript FrameworkZK Developer's Guide: Developing responsive user interfaces for web applications using Ajax, XUL, and the open source ZK rich web client development frameworkHead First Java, 2nd Edition