尚矽谷學習筆記----國際化
在程式設計領域, 把在無需改寫原始碼即可讓開發出來的應用程式能夠支援多種語言和資料格式的技術稱為國際化.與國際化對應的是本地化, 指讓一個具備國際化支援的應用程式支援某個特定的地區。
Struts2實現國際化的步驟
1、建立國際化資原始檔
建立國際化資原始檔,就是建立多個properties檔案,每個檔案通過檔名不同對應一個國家的語音環境。不同的properties檔案內定義了很多key-value鍵值對,key是程式中訪問的名稱,而value值不同的properties檔案分別對應該國家的訊息內容。
properties檔案命名規則一般為:
預設的:basename.properties
美國的:basename_en_US.properties
中國的:basename_zh_CN.properties
其中basename可以是任意名字。有的程式設計師習慣命名為i18n。
2、配置資原始檔
配置資原始檔,分為在action範圍、包範圍和全域性範圍三種方式:
I. Action 範圍資原始檔
在Action類檔案所在的路徑建立名為 ActionName_language_country.properties 的檔案
例:InputAction_zh_CN.properties。
II. 包範圍資原始檔
在包的根路徑下建立檔名為 package_language_country.properties 的屬性檔案,一旦建立,處於該包下的所有 Action 都可以訪問該資原始檔。注意:包範圍資原始檔的 baseName 就是package,不是Action所在的包名。
例:package_zh_CN.properties。
III. 全域性資原始檔
在classes路徑下,及src下,建立檔名basename_language_country.properties,並在struts.xml中配置常量<constant name="struts.custom.i18n.resources" value="baseName"/>
例:建立檔案—- i18n_zh_CN.properties。
在struts.xml配置<constant name="struts.custom.i18n.resources" value="i18n"/>
IV國際化資原始檔載入的順序如何呢
原則:離當前 Action 較近的將被優先載入. 載入順序(瞭解即可):
假設我們在某個 ChildAction 中呼叫了getText(“username”):
(1) 載入和 ChildAction 的類檔案在同一個包下的系列資原始檔 ChildAction.properties
(2) 載入 ChildAction 實現的介面 IChild,且和 IChildn 在同一個包下 IChild.properties 系列資原始檔。
(3) 載入 ChildAction 父類 Parent,且和 Parent 在同一個包下的 baseName 為Parent.properties 系列資原始檔。
(4) 若 ChildAction 實現 ModelDriven介面,則對於getModel()方法返回的model 物件,重新執行第(1)步操作。
(5) 查詢當前包下package.properties 系列資原始檔。
(6) 沿著當前包上溯,直到最頂層包來查詢 package.properties的系列資原始檔。
(7) 查詢 struts.custom.i18n.resources 常量指定 baseName 的系列資原始檔。
(8) 直接輸出該key的字串值。
3、在action 或JSP頁面中使用本地化後的訊息
I 在action中使用本地化的訊息
第一步:action要實現TextProvider介面(可通過繼承(擴充套件)ActionSupport方式)
第二步:通過getText()方法訪問value;
- (1)如果在properties檔案中定義的key-value鍵值對的value為普通字串,可以直接通過this.getText(“key”)方法直接訪問value。
- (2)如果在properties檔案中定義的key-value鍵值對的value為佔位符,如{0},需要通過this.getText(“key”,args)方法去訪問,其中args引數根據程式需要傳入相應的值。如果args傳入的是一個list則{0}對應list[0]的值,{1}對應list[1]的值。
II 在jsp中使用本地化的訊息
(1)simple主題:
- a、通過
<s:text name="username">
來訪問,其中“username”就是properties中定義的key。 - b、通過強制解析ognl訪問:
<s:submit name="submit" value="%{getText('submit')}"></s:submit>
%getText(‘submit’)}中的submit就是key。
(2)非simple主題:直接通過key屬性訪問
<s:textfield name="username" key="username"></s:textfield>
4、Struts2 框架是如何確定 Local 物件的?
要想知道Struts2 框架是如何確定 Local 物件的,就需要閱讀I18N 攔截器的原始碼。 通過struts-default.xml我們知道預設的I18N攔截器是com.opensymphony.xwork2.interceptor.I18nInterceptor
程式碼0:閱讀原始碼,找到intercept方法:
public String intercept(ActionInvocation invocation) throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("intercept '#0/#1' {",
invocation.getProxy().getNamespace(), invocation.getProxy().getActionName());
}
//這localeFinder是一個內部類,這個類主要是用來返回從request的引數中獲取locale資訊以及是否要將locale資訊儲存在session中。見程式碼1。
LocaleFinder localeFinder = new LocaleFinder(invocation);
//返回從request中獲取到的locale(可能為null),見程式碼3和2
Locale locale = getLocaleFromParam(localeFinder.getRequestedLocale());
//呼叫storeLocale方法,如果從request中獲取到了locale,則儲存到session中,如果沒有嘗試分別從session和CurrentInvocation獲取local資訊。session中有的話,就是session中,沒有的化讀取系統預設的值。
locale = storeLocale(invocation, locale, localeFinder.getStorage());
//將locale資訊儲存到invocation
saveLocale(invocation, locale);
if (LOG.isDebugEnabled()) {
LOG.debug("before Locale=#0", invocation.getStack().findValue("locale"));
}
final String result = invocation.invoke();
if (LOG.isDebugEnabled()) {
LOG.debug("after Locale=#0", invocation.getStack().findValue("locale"));
LOG.debug("intercept } ");
}
return result;
}
程式碼1:內部類localeFinder的程式碼:
protected class LocaleFinder {
//用來記錄是否要在ssession中儲存locale資訊
protected String storage = Storage.SESSION.toString();
protected Object requestedLocale = null;
protected ActionInvocation actionInvocation = null;
//建構函式初始化
protected LocaleFinder(ActionInvocation invocation) {
actionInvocation = invocation;
//查詢request是否帶有locale的引數
find();
}
protected void find() {
//get requested locale
//獲取請求引數集
Map<String, Object> params = actionInvocation.getInvocationContext().getParameters();
//預設為儲存到session中。
storage = Storage.SESSION.toString();
//呼叫外部類的findLocaleParameter方法,獲取請求引數中是否有引數名為request_locale的引數。原始碼見程式碼2。
//parameterName的值初始化為request_locale
requestedLocale = findLocaleParameter(params, parameterName);
//如果獲取到了則直接返回
if (requestedLocale != null) {
return;
}
//requestOnlyParameterNam的值初始化為request_only_locale
//呼叫外部類的findLocaleParameter方法,獲取請求引數中是否有引數名為request_only_locale的引數
requestedLocale = findLocaleParameter(params, requestOnlyParameterName);
//如果獲取到,將storage設定為不在session中儲存。
if (requestedLocale != null) {
storage = Storage.NONE.toString();
}
}
//返回是否在session中儲存的標記
public String getStorage() {
return storage;
}
//返回從request中獲取到的locale,可能為null,也可能獲取到了
public Object getRequestedLocale() {
return requestedLocale;
}
}
程式碼2:findLocaleParameter方法原始碼。
protected Object findLocaleParameter(Map<String, Object> params, String parameterName) {
//嘗試從params,中獲取“*request_locale*”的引數,
Object requestedLocale = params.remove(parameterName);
//如果獲得的requestedLocale是個陣列的話,返回第一個。
if (requestedLocale != null && requestedLocale.getClass().isArray()
&& ((Object[]) requestedLocale).length > 0) {
requestedLocale = ((Object[]) requestedLocale)[0];
if (LOG.isDebugEnabled()) {
LOG.debug("requested_locale=#0", requestedLocale);
}
}
return requestedLocale;
}
程式碼3:getLocaleFromParam方法,從param中獲取locale
protected Locale getLocaleFromParam(Object requestedLocale) {
Locale locale = null;
//如果從request中獲取到了的locale資訊,則解析該資訊,並賦值給locale。
if (requestedLocale != null) {
locale = (requestedLocale instanceof Locale) ?
(Locale) requestedLocale :
LocalizedTextUtil.localeFromString(requestedLocale.toString(), null);
if (locale != null && LOG.isDebugEnabled()) {
LOG.debug("applied request locale=#0", locale);
}
}
//如果locale資訊不被java支援則獲得預設locale
if (locale != null && !Arrays.asList(Locale.getAvailableLocales()).contains(locale)) {
locale = Locale.getDefault();
}
//返回從request中獲取到的locale(可能為null)
return locale;
}
程式碼4:storeLocale方法原始碼,儲存locale到session中
protected Locale storeLocale(ActionInvocation invocation, Locale locale, String storage) {
//save it in session
//獲取session
Map<String, Object> session = invocation.getInvocationContext().getSession();
//如果session不為null,執行同步鎖定session操作
if (session != null) {
synchronized (session) {
//如果前面沒有通過request獲得到locale,則把storage設定為不儲存,並嘗試讀取儲存的locale(分別從session和CurrentInvocation)。
if (locale == null) {
storage = Storage.NONE.toString();
locale = readStoredLocale(invocation, session);
}
//如果storage為儲存,說明通過request_locale獲得到了locale,則在session中儲存
if (Storage.SESSION.toString().equals(storage)) {
session.put(attributeName, locale);
}
}
}
return locale;
}
具體確定 Locale 物件的過程:
- Struts2 使用 i18n 攔截器 處理國際化,並且將其註冊在預設的攔截器棧中,i18n攔截器在執行Action方法前,自動查詢請求中一個名為 request_locale 的引數。
- 如果該引數存在,攔截器就將其作為引數,轉換成Locale物件,並將其設為使用者預設的Locale(代表國家/語言環境)。並把其設定為 session 的 WW_TRANS_I18N_LOCALE 屬性
若 request 沒有名為request_locale 的引數,則 i18n 攔截器會從 Session 中獲取 WW_TRANS_I18N_LOCALE
的屬性值,若該值不為空,則將該屬性值設定為瀏覽者的預設Locale。若 session 中的 WW_TRANS_I18N_LOCALE 的屬性值為空,則從 ActionContext 中獲取 Locale 物件。