1. 程式人生 > >利用Spring進行專案國際化(多語言)

利用Spring進行專案國際化(多語言)

1.Spring國際化概述

1)國際化基本規則國際化資訊”也稱為“本地化資訊”,一般需要兩個條件才可以確定一個特定型別的本地化資訊,它們分別是“語言型別”和“國家/地區的型別”。如中文字地化資訊既有中國大陸地區的中文,又有中國臺灣、中國香港地區的中文,還有新加坡地區的中文。Java通過java.util.Locale類表示一個本地化物件,它允許通過語言引數和國家/地區引數建立一個確定的本地化物件。語言引數使用ISO標準語言程式碼表示,這些程式碼是由ISO-639標準定義的,每一種語言由兩個小寫字母表示。在許多網站上都可以找到這些程式碼的完整列表,下面的網址是提供了標準語言程式碼的資訊:http://www.loc.gov/standards/iso639-2/php/English_list.php
2)基本實現流程

2.語言型別判斷

1)基於瀏覽器語言根據Request Headers中的Accept-language來判斷。2)基於客戶端傳參要求客戶端第一次(或者每次)傳遞的自定義引數值來判斷,如規定傳locale,值為zh-cn、en-us等內容,如果只在第一次傳入則local以及timeZone先關資訊要存入session或者cookie中,後面的請求語言方式則直接從兩者中取,其有效時間與session和cookie設定的生命週期關聯。3)基於預設配置當獲取語言型別時沒有找到對應型別時,會使用預設的語言型別。

3.語言型別儲存

<!-- 定義本地化變更攔截器 -->
<
bean
id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<!-- 定義註解URL對映處理器,所有的請求對映關聯本地化攔截器,或者也可自定義該攔截器路徑對映--> <bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name
="interceptors" ref=" localeChangeInterceptor " />
<property name="order" value="1"></property> </bean>

3.1基於url

該種方式需要每次都在請求的url上帶上local引數,指定該次需要的語言型別,並且該方式的local解析器需要配置,如下:
<a href="xxx.do?locale=zh_CN">中文</a><a href="xxx.do?locale=en">英文</a>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"/>
但在該配置下使用會拋Cannot change HTTP accept header - use a different locale resolution strategy異常,這是因為spring source做了限制,無法對本地的local賦值修改,解決辦法如下,新建一個類MyLocaleResolver繼承AcceptHeaderLocaleResolver,重寫resolveLocale和setLocale方法,並將上面的localeResolver的class指向如下MyLocaleResolver類:
public class MyLocaleResolver extends AcceptHeaderLocaleResolver {
 private Locale myLocal;

public Locale resolveLocale(HttpServletRequest request) {
 return myLocal == null ? request.getLocale() : myLocal;
}

public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
 myLocal = locale;
 }
}

3.2基於session

基於session的狀態儲存方式只需要在第一次請求的時候指定語言型別,localResolver會將該屬性儲存到session中,後面的請求直接從session中獲取該語言型別,該種方式的localResolver對應的類為SessionLocaleResolver,如下配置:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
該方式的原理圖如下圖所示:

3.3基於cookie

與session的機制類似,差異在於兩者的儲存和週期,鑑於安全、大小以及體驗等因素的影響,實際使用中使用者更傾向於前者,該種cookie儲存方式的localResolver為<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />。

4.文案資料來源

對於語言型別的資原始檔,需要開發者對文案進行蒐集整理,並翻譯成相應的語言確定關鍵字key,目前大多數情況是將這些資訊置於.properties檔案中,在使用的時候直接訪問獲取,當然也可置於資料庫中,但頻繁的文案獲取會影響伺服器效能及產品體驗,可結合資料字典以及快取工具使用。

4.1資料庫(示例)

1)spring 配置方式

<!-- 預設的註解對映的支援 -->
<mvc:annotation-driven validator="validator" conversion-service="conversionService" />
<!-- 資原始檔 -->
<bean id="propertiesMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 <property name="basenames">
 <list>
 <value>resource</value>
 <value>validation</value>
 </list>
 </property>
</bean>

<bean id="databaseMessageSource" class="com.obs2.util.MessageResource">
 <property name="parentMessageSource" ref="propertiesMessageSource"/>
</bean>

<bean id="messageInterpolator" class="com.obs2.util.MessageResourceInterpolator">
 <property name="messageResource" ref="databaseMessageSource"/>
</bean>

<!-- 驗證器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
 <property name="messageInterpolator" ref="messageInterpolator"/>
</bean>
這裡定義了一個propertiesMessageSource,一個databaseMessageSourcer,和一個messageInterpolator。propertiesMessageSource用於讀取properties檔案databaseMessageSourcer用於讀取資料庫的資料配置,其中,有一個屬性設定它的父MessageSource為propertiesMessageSource。意思是如果資料庫找不到對應的資料,到properties檔案當中查詢。messageInterpolator是個攔截器。

2)資料庫的POJO定義

@Entity
@SuppressWarnings("serial")
@Table(name="resource")

public class Resource implements Serializable {
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="resource_id")
 private long resourceId;

 @Column(name="name", length=50, nullable=false)
 private String name;

 @Column(name="text", length=1000, nullable=false)
 private String text;

 @Column(name="language", length=5, nullable=false)
 private String language;

 public long getResourceId() {
     return resourceId;
 }

 public void setResourceId(long resourceId) {
    this.resourceId = resourceId;
 }

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public String getText() {
     return text;
 }

 public void setText(String text) {
     this.text = text;
}

 public String getLanguage() {
     return language;
 }

 public void setLanguage(String language) {
     this.language = language;
 }
}
定義了一張表[resource],欄位有:[resource_id]、[name]、[text]、[language]。

3)讀取資料庫的MessageResource類

/**
* 取得資源資料
* @author Robin
*/
public class MessageResource extends AbstractMessageSource implements ResourceLoaderAware {

 @SuppressWarnings("unused")
 private ResourceLoader resourceLoader;

 @Resource
 private ResourceService resourceService;
 /**
 * Map切分字元
 */
 protected final String MAP_SPLIT_CODE = "|";
 protected final String DB_SPLIT_CODE = "_";

 private final Map<String, String> properties = new HashMap<String, String>();

 public MessageResource() {
     reload();
 }

 public void reload() {
     properties.clear();
     properties.putAll(loadTexts());
 }

 protected Map<String, String> loadTexts() {
     Map<String, String> mapResource = new HashMap<String, String>();
     List<com.obs2.service.bean.Resource> resources = resourceService.findAll();
     for (com.obs2.service.bean.Resource item : resources) {
     String code = item.getName() + MAP_SPLIT_CODE + item.getLanguage();
     mapResource.put(code, item.getText());
     }

     return mapResource;
 }

 private String getText(String code, Locale locale) {
     String localeCode = locale.getLanguage() + DB_SPLIT_CODE + locale.getCountry();
     String key = code + MAP_SPLIT_CODE + localeCode;
     String localeText = properties.get(key);
     String resourceText = code;
     if(localeText != null) {
         resourceText = localeText;
     }else {
         localeCode = Locale.ENGLISH.getLanguage();
         key = code + MAP_SPLIT_CODE + localeCode;
         localeText = properties.get(key);
         if(localeText != null) {
             resourceText = localeText;
         }else {
            try {
                 if(getParentMessageSource() != null) {
                     resourceText = getParentMessageSource().getMessage(code, null, locale);
                 }
             } catch (Exception e) {
                 logger.error("Cannot find message with code: " + code);
             }
         }
     }
     return resourceText;
 }

 @Override
 public void setResourceLoader(ResourceLoader resourceLoader) {
     this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
 }

 @Override
 protected MessageFormat resolveCode(String code, Locale locale) {
     String msg = getText(code, locale);
     MessageFormat result = createMessageFormat(msg, locale);
     return result;
 }

 @Override
 protected String resolveCodeWithoutArguments(String code, Locale locale) {
     String result = getText(code, locale);
     return result;
 }
}
主要是過載AbstractMessageSource和ResourceLoaderAware,以實現Spring MVC的MessageSource國際化呼叫。類中的reload()方法,我把它寫到了一個ServletListener當中,讓專案啟動時,自動載入資料到static的map中。

4)Listener

/**
* 系統啟動監聽
* @author Robin
*/
public class SystemListener implements ServletContextListener {
/**
* context初始化時激發
*/
@Override
public void contextInitialized(ServletContextEvent e) {
// 取得ServletContext
ServletContext context = e.getServletContext();
WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context);
// 設定國際化多語言
MessageResource messageSource = applicationContext.getBean(MessageResource.class);
 messageSource.reload();
}

/**
* context刪除時激發
*/
@Override
public void contextDestroyed(ServletContextEvent e) {
}

/**
* 建立一個 session時激發
* @param e
*/

public void sessionCreated(HttpSessionEvent e) {
}

/**
* 當一個 session失效時激發
* @param e
*
public void sessionDestroyed(HttpSessionEvent e) {

}

/**
* 設定 context的屬性,它將激發attributeReplaced或attributeAdded方法
* @param e
*/
public void setContext(HttpSessionEvent e) {
}

/**
* 增加一個新的屬性時激發
* @param e
*/
public void attributeAdded(ServletContextAttributeEvent e) {
}

/**
*刪除一個新的屬性時激發
* @param e
*/

public void attributeRemoved(ServletContextAttributeEvent e) {

}

/*
* 屬性被替代時激發
* @param e

*/
public void attributeReplaced(ServletContextAttributeEvent e) {
}
}
該Listener需要加入到web.xml當中:
<!-- 系統啟動監聽 -->
<listener>
    <listener-class>com.obs2.util.SystemListener</listener-class>
</listener>

5)Interceptor攔截器

/**
* 攔截Annotation驗證資訊
* @author Robin
*
*/

public class MessageResourceInterpolator implements MessageInterpolator {
@Resource
private MessageResource messageResource;

public void setMessageResource(MessageResource messageResource) {
 this.messageResource = messageResource;
}

@Override
public String interpolate(String messageTemplate, Context context) {

String messageTemp = null;
 if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
 messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
 }else {
 return messageTemplate;
 }

 String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");
 MessageBuilder builder = new MessageBuilder().code(messageTemp);

 if (params != null) {
 for (String param : params) {
 builder = builder.arg(param);
 }

 }

 String result = builder.build().resolveMessage(messageResource, Locale.ENGLISH).getText();
 return result;

 }

@Override
public String interpolate(String messageTemplate, Context context, Locale locale) {

 String messageTemp = null;
 if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
 messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
 }else {
 return messageTemplate;
 }

 String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");

 MessageBuilder builder = new MessageBuilder().code(messageTemp);
 if (params != null) {
 builder = builder.args(params);
 }

 String result = builder.build().resolveMessage(messageResource, locale).getText();

 return result
 }

}

4.2靜態資源

<!-- 資原始檔繫結器,檔名稱:messages.properties(沒有找到時的預設檔案), messages_en.properties(英文),messages_zh_CN.properties(中文),等等-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="config.messages.messages" />
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="basename" value="i18n.messages"/>
    <property name="useCodeAsDefaultMessage" value="true" />
</bean>

5.文案獲取

5.1資源獲取介面

5.1.1 MessageSource詳解

Spring定義了訪問國際化資訊的MessageSource介面,並提供了幾個易用的實現類。首先來了解一下該介面的幾個重要方法:1)String getMessage(String code, Object[] args, String defaultMessage, Locale locale) code表示國際化資源中的屬性名;args用於傳遞格式化串佔位符所用的執行期引數;當在資源找不到對應屬性名時,返回defaultMessage引數所指定的預設資訊;locale表示本地化物件;2)String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException與上面的方法類似,只不過在找不到資源中對應的屬性名時,直接丟擲NoSuchMessageException異常;3)String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException

相關推薦

利用Spring進行專案國際化語言

1.Spring國際化概述1)國際化基本規則國際化資訊”也稱為“本地化資訊”,一般需要兩個條件才可以確定一個特定型別的本地化資訊,它們分別是“語言型別”和“國家/地區的型別”。如中文字地化資訊既有中國大陸地區的中文,又有中國臺灣、中國香港地區的中文,還有新加坡地區的中文。Ja

android國際化語言

1.  很大程度上,為什麼我們能如此方便的實現國際化、解析度匹配等?     主要就是得益於 Android 中這種獨特的資源管理方式。程式設計師的程式碼可以不直接和資源發生關係。Android 中,我們通常通過 R 檔案提供的索引來間接的引用某一個資源。而如何維護資源索引

Python機器學習庫sklearn裡利用感知機進行三分類分類的原理

from IPython.display import Image %matplotlib inline # Added version check for recent scikit-learn 0.18 checks from distutils.vers

android國際化語言,圖片

1.  很大程度上,為什麼我們能如此方便的實現國際化、解析度匹配等?     主要就是得益於 Android 中這種獨特的資源管理方式。程式設計師的程式碼可以不直接和資源發生關係。Android 中,我們通常通過 R 檔案提供的索引來間接的引用某一個資源。而如何維護資源索引

Python機器學習庫sklearn裡利用LR模型進行三分類分類的原理

首先,LR將線性模型利用sigmoid函式進一步做了非線性對映。 將分類超平面兩側的正負樣本點,通過壓縮函式轉化成了以0.5為分解的兩類:類別0和類別1。 這個轉化過程見下圖: 上圖給出的是線性邊界與LR分佈函式(即sigmoid函式)的對映對應關係;同樣,對於非線

利用python進行資料分析第二版 pdf下載

適讀人群 :適合剛學Python的資料分析師或剛學資料科學以及科學計算的Python程式設計者。 閱讀本書可以獲得一份關於在Python下操作、處理、清洗、規整資料集的完整說明。本書第二版針對Python 3.6進行了更新,並增加實際案例向你展示如何高效地解決一系列資料分析問題。你將在閱讀

【轉】Python+opencv利用sobel進行邊緣檢測細節講解

#! usr/bin/env python # coding:utf-8 # 2018年7月2日06:48:35 # 2018年7月2日23:11:59 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2

利用 Python 進行資料分析十二pandas:資料合併

原文地址 pandas 提供了三種主要方法可以對資料進行合併: pandas.merge()方法:資料庫風格的合併;pandas.concat()方法:軸向連線,即沿著一條軸將多個物件堆疊到一起;例項方法combine_first()方法:合併重疊資料。 pandas

IntelliJ IDEA 2017.3 配置Tomcat執行web專案教程

小白一枚,借鑑了好多人的部落格,然後自己總結了一些圖,儘量的詳細。在配置的過程中,有許多疑問。如果讀者看到後能給我解答的,請留言。Idea請各位自己安裝好,還需要安裝Maven和Tomcat,各自配置好環境變數。 我配置的是一個多模組的web專案,配合Maven和Tomca

【vue 進階指南 一】語言國際化中英文切換

vue + element-ui + vue-i18n 多語言國際化 1.element-ui 並不支援最新版的vue-i8n 外掛,開發時注意i18n的版本,當然本示例成功解決版本不相容的問題,詳細說明請看element官方文件:http://eleme

jenkins 自動化部署 spring boot 專案

前置條件 jenkins與部署專案所用伺服器為同一臺 將下載好的war包,放在tomcat的webapps目錄下,啟動tomcat 預設 8080 埠 wget http://mi

自定義實現帶checkbox的listView用於專案中的語言實現介面

我感覺自定義listview最主要的原因就是系統自帶的一些佈局不能夠滿足我們的專案需求。 我這裡實現的自定義listview是用來實現之前部落格中提到的多語言切換的,在展示應用支援的語言的同時要讓使用者知道當前使用的語言是哪一種。 public static class

在idea中利用Spring進行面向切面程式設計AOP的一個例子

(1)在idea中新建立一個maven專案aopAspectj,,編寫POM檔案,匯入jar包: <dependencies> <!--aspectj依賴--> <dependency> <grou

tp3.2中利用分頁類進行分頁條件搜尋

HTML頁面 使用者名稱:<input type="text" name="name" id="username"> 方案id:<input type="text" name="" id="programid"> 配資狀態:<select na

VUE實現Studio管理後臺:支援語言國際化vue-i18n

RXEditor的第一版本是英文版,有些朋友看起來覺得不習慣,後來因為惰性,不願意再修改舊程式碼加入中文版,這次提前就把這個問題解決了,克服惰性最好的方式,就是想到就儘快去做,避免拖延。 本來計劃在介面的右上角,做一個下拉選單選擇語言。下拉選單的工作量稍稍有些大,並且很少有隨時切換介面語言的需求。最終確定獲取

利用樸素貝葉斯Navie Bayes進行垃圾郵件分類

判斷 ase create numpy water 向量 not in imp img 貝葉斯公式描寫敘述的是一組條件概率之間相互轉化的關系。 在機器學習中。貝葉斯公式能夠應用在分類問題上。這篇文章是基於自己的學習所整理。並利用一個垃圾郵件分類的樣例來加深對於理論的理解

利用Apache Bench進行壓力測試安裝篇

選擇 服務器 文章 分享 ner log 應該 spa 單獨 前言 API性能測試是企業級開發中非常重要的一環,API性能測試個人總結起來比較關註的兩個點為: API在並發下是否會產生數據異常,比如秒殺系統的超買超賣 API極限情況下的QPS/TPS 目前比較流行的性能

C# Winform下一個熱插拔的MIS/MRP/ERP框架語言方案

文件加載 全局 查詢 分享 技術 變量 支持 對象 style 個別時候,我們需要一種多語種切換方案。   我的想方案是這樣的: 1、使用文本文本存儲多語言元素,應用程序啟動時加載到內存表中; 2、應用程序啟動時從配置文件加載語種定義; 3、所有窗體繼承自一個Base

Java Spring MVC專案搭建——專案配置

文章轉載自:https://www.cnblogs.com/eczhou/p/6287876.html 1、站點配置檔案web.xml 每一個Spring MVC 專案都必須有一個站點配置檔案web.xml,他的主要功能嗎....有一位大哥已經整理的很好,我借來了,大家看看: 引用部落格

Java Spring MVC專案搭建——Spring MVC框架整合

轉自:https://www.cnblogs.com/eczhou/p/6287852.html 1、Java JDK及Tomcat安裝 我這裡安裝的是JDK 1.8 及 Tomcat 8,安裝步驟詳見:http://www.cnblogs.com/eczhou/p/6285248.html