亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

Java Spring項目國際化(i18n)詳細方法與實例

瀏覽:3日期:2022-09-04 09:44:00
Spring國際化概述國際化基本規則

國際化信息”也稱為“本地化信息”,一般需要兩個條件才可以確定一個特定類型的本地化信息,它們分別是“語言類型”和“國家/地區的類型”。如中文本地化信息既有中國大陸地區的中文,又有中國臺灣、中國香港地區的中文,還有新加坡地區的中文。Java通過java.util.Locale類表示一個本地化對象,它允許通過語言參數和國家/地區參數創建一個確定的本地化對象。

語言參數使用ISO標準語言代碼表示,這些代碼是由ISO-639標準定義的,每一種語言由兩個小寫字母表示。在許多網站上都可以找到這些代碼的完整列表,下面的網址是提供了標準語言代碼的信息:http://www.loc.gov/standards/iso639-2/php/English_list.php。

國家/地區參數也由標準的ISO國家/地區代碼表示,這些代碼是由ISO-3166標準定義的,每個國家/地區由兩個大寫字母表示。用戶可以從以下網址查看ISO-3166的標準代碼:http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html,部分語言和國家/地區的標準代碼如下所示:

語言 簡稱 簡體中文(中國) zh_CN 繁體中文(中國臺灣) zh_TW 繁體中文(中國香港) zh_HK 英語(中國香港) en_HK 英語(美國) en_US 英語(英國) en_GB 英語(全球) en_WW 英語(加拿大) en_CA 英語(澳大利亞) en_AU 英語(愛爾蘭) en_IE 英語(芬蘭) en_FI 芬蘭語(芬蘭) fi_FI 英語(丹麥) en_DK 丹麥語(丹麥) da_DK 英語(以色列) en_IL 希伯來語(以色列) he_IL 英語(南非) en_ZA 英語(印度) en_IN 英語(挪威) en_NO 英語(新加坡) en_SG 英語(新西蘭) en_NZ 英語(印度尼西亞) en_ID 英語(菲律賓) en_PH 英語(泰國) en_TH 英語(馬來西亞) en_MY 英語(阿拉伯) en_XA 韓文(韓國) ko_KR 日語(日本) ja_JP 荷蘭語(荷蘭) nl_NL 荷蘭語(比利時) nl_BE 葡萄牙語(葡萄牙) pt_PT 葡萄牙語(巴西) pt_BR 法語(法國) fr_FR 法語(盧森堡) fr_LU 法語(瑞士) fr_CH 法語(比利時) fr_BE 法語(加拿大) fr_CA 西班牙語(拉丁美洲) es_LA 西班牙語(西班牙) es_ES 西班牙語(阿根廷) es_AR 西班牙語(美國) es_US 西班牙語(墨西哥) es_MX 西班牙語(哥倫比亞) es_CO 西班牙語(波多黎各) es_PR 德語(德國) de_DE 德語(奧地利) de_AT 德語(瑞士) de_CH 俄語(俄羅斯) ru_RU 意大利語(意大利) it_IT 希臘語(希臘) el_GR 挪威語(挪威) no_NO 匈牙利語(匈牙利) hu_HU 土耳其語(土耳其) tr_TR 捷克語(捷克共和國) cs_CZ 斯洛文尼亞語 sl_SL 波蘭語(波蘭) pl_PL 瑞典語(瑞典) sv_SE 西班牙語(智利) es_CL 語言類型判斷1)基于瀏覽器語言

根據Request Headers中的Accept-language來判斷。

2)基于客戶端傳參

要求客戶端第一次(或者每次)傳遞的自定義參數值來判斷,如規定傳locale,值為zh-cn、en-us等內容,如果只在第一次傳入則local以及timeZone先關信息要存入session或者cookie中,后面的請求語言方式則直接從兩者中取,其有效時間與session和cookie設置的生命周期關聯。

3)基于默認配置

當獲取語言類型時沒有找到對應類型時,會使用默認的語言類型。

語言類型保存

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

該種方式需要每次都在請求的url上帶上local參數,指定該次需要的語言類型,并且該方式的local解析器需要配置,如下:

<a href='http://www.aoyou183.cn/bcjs/xxx.do?locale=zh_CN' rel='external nofollow' >中文</a>或<a href='http://www.aoyou183.cn/bcjs/xxx.do?locale=en' rel='external nofollow' >英文</a><bean />

但在該配置下使用會拋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; }}基于session

基于session的狀態保存方式只需要在第一次請求的時候指定語言類型,localResolver會將該屬性保存到session中,后面的請求直接從session中獲取該語言類型,該種方式的localResolver對應的類為SessionLocaleResolver,如下配置:

<bean />基于cookie

與session的機制類似,差異在于兩者的存儲和周期,鑒于安全、大小以及體驗等因素的影響,實際使用中使用者更傾向于前者,該種cookie保存方式的localResolver為

<bean />文案數據來源

對于語言類型的資源文件,需要開發者對文案進行搜集整理,并翻譯成相應的語言確定關鍵字key,目前大多數情況是將這些信息置于.properties文件中,在使用的時候直接訪問獲取,當然也可置于數據庫中,但頻繁的文案獲取會影響服務器性能及產品體驗,可結合數據字典以及緩存工具使用。

數據庫1)spring 配置方式

<!-- 默認的注解映射的支持 --><mvc:annotation-driven validator='validator' conversion-service='conversionService' /><!-- 資源文件 --><bean class='org.springframework.context.support.ResourceBundleMessageSource'> <property name='basenames'> <list> <value>resource</value> <value>validation</value> </list> </property></bean> <bean class='com.obs2.util.MessageResource'> <property name='parentMessageSource' ref='propertiesMessageSource'/></bean> <bean class='com.obs2.util.MessageResourceInterpolator'> <property name='messageResource' ref='databaseMessageSource'/></bean> <!-- 驗證器 --><bean 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初始化時激發*/@Overridepublic void contextInitialized(ServletContextEvent e) {// 取得ServletContextServletContext context = e.getServletContext();WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context);// 設置國際化多語言MessageResource messageSource = applicationContext.getBean(MessageResource.class); messageSource.reload();} /*** context刪除時激發*/@Overridepublic 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 {@Resourceprivate MessageResource messageResource; public void setMessageResource(MessageResource messageResource) { this.messageResource = messageResource;} @Overridepublic 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; } @Overridepublic 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 } }靜態資源

<!-- 資源文件綁定器,文件名稱:messages.properties(沒有找到時的默認文件), messages_en.properties(英文),messages_zh_CN.properties(中文),等等--><bean 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>文案獲取資源獲取接口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

MessageSourceResolvable 將屬性名、參數數組以及默認信息封裝起來,它的功能和第一個接口方法相同。

MessageSource類結構

MessageSource分別被HierarchicalMessageSource和ApplicationContext接口擴展,這里我們主要看一下HierarchicalMessageSource接口的幾個實現類

Java Spring項目國際化(i18n)詳細方法與實例

HierarchicalMessageSource接口添加了兩個方法,建立父子層級的MessageSource結構,類似于前面我們所介紹的HierarchicalBeanFactory。該接口的setParentMessageSource (MessageSource parent)方法用于設置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。

HierarchicalMessageSource接口最重要的兩個實現類是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它們基于Java的ResourceBundle基礎類實現,允許僅通過資源名加載國際化資源。ReloadableResourceBundleMessageSource提供了定時刷新功能,允許在不重啟系統的情況下,更新資源的信息。StaticMessageSource主要用于程序測試,它允許通過編程的方式提供國際化信息。而DelegatingMessageSource是為方便操作父MessageSource而提供的代理類。

ResourceBundleMessageSource與ReloadableResourceBundleMessageSource對比1)通過ResourceBundleMessageSource配置資源

<bean id=' messageSource 'class='org.springframework.context.support.ResourceBundleMessageSource'> <!--①通過基名指定資源,相對于類根路徑--> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> </bean>2)通過ReloadableResourceBundleMessageSource配置資源

<bean id='messageSource 'class='org.springframework.context.support.ReloadableResourceBundleMessageSource'> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> <!--①刷新資源文件的周期,以秒為單位--> <property name='cacheSeconds' value='5'/></bean>3)對比

兩者都是利用資源名通過getMessage()接口就可以加載整套的國際化資源文件,唯一區別在于ReloadableResourceBundleMessageSource可以定時刷新資源文件,以便在應用程序不重啟的情況下感知資源文件的變化。很多生產系統都需要長時間持續運行,系統重啟會給運行帶來很大的負面影響,這時通過該實現類就可以解決國際化信息更新的問題。上面的配置中cacheSeconds屬性讓ReloadableResourceBundleMessageSource每5秒鐘刷新一次資源文件(在真實的應用中,刷新周期不能太短,否則頻繁的刷新將帶來性能上的負面影響,一般不建議小于30分鐘)。cacheSeconds默認值為-1表示永不刷新,此時,該實現類的功能就蛻化為ResourceBundleMessageSource的功能。

頁面獲取文案利用Spring標簽獲取

引入標簽庫:

<%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %>

獲取文案:

<s:message code='test.app'/><spring:message code='main.title' />利用JSTL標簽獲取

引入標簽庫:

<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core_rt'%>

獲取文案:

<fmt:message key='test.app'/>Java代碼中獲取文案利用MessageSource接口獲取1)自動注入

@Autowiredprivate MessageSource messageSource;String s = messageSource.getMessage('SystemError', new Object[]{}, Locale.US);2)手動bean獲取

a. 獲取容器

容器已經初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器沒有初始化:

String[] configs = {'com/baobaotao/i18n/beans.xml'};ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 獲取bean跟文案

MessageSource ms = (MessageSource) wac.getBean('myResource');Object[] params = {'John', new GregorianCalendar().getTime()};String str1 = ms.getMessage('greeting.common',params,Locale.US);利用Spring容器獲取

在前面的MessageSource類圖結構中我們發現ApplicationContext實現了MessageSource的接口,也就是說ApplicationContext的實現類本身也是一個MessageSource對象。

將ApplicationContext和MessageSource整合起來, Spring此處的設計人為:在一般情況下,國際化信息資源應該是容器級。我們一般不會將MessageSource作為一個Bean注入到其他的Bean中,相反MessageSource作為容器的基礎設施向容器中所有的Bean開放。只要我們考察一下國際化信息的實際消費場所就更能理解Spring這一設計的用意了。國際化信息一般在系統輸出信息時使用,如Spring MVC的頁面標簽,控制器Controller等,不同的模塊都可能通過這些組件訪問國際化信息,因此Spring就將國際化消息作為容器的公共基礎設施對所有組件開放。

既然一般情況下我們不會直接通過引用MessageSource Bean使用國際信息,那如何聲明容器級的國際化信息呢? Spring容器啟動過程時,在初始化容器的時候通過initMessageSource()方法所執行的工作就是初始化容器中的國際化信息資源,它根據反射機制從BeanDefinitionRegistry中找出名稱為“messageSource”且類型為org.springframework.context.MessageSource的Bean,將這個Bean定義的信息資源加載為容器級的國際化信息資源。請看下面的配置:

<!--注冊資源Bean,其Bean名稱只能為messageSource --><bean class='org.springframework.context.support.ResourceBundleMessageSource'> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property></bean>然后通過ApplicationContext直接訪問國際化信息:

a. 獲取容器

容器已經初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器沒有初始化:

String[] configs = {'com/baobaotao/i18n/beans.xml'};ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 獲取bean跟文案

Object[] params = {'John', new GregorianCalendar().getTime()};String str1 = ctx.getMessage('greeting.common',params,Locale.US);注意事項1)編碼問題

a. 改變properties文件編碼為UTF-8/GBK,然而ResourceBundleMessageSource的默認編碼defaultEncoding是ISO-8859-1,需要在xml中增加一個相應屬性將其改變為你需要的UTF-8/GBK之類。

b. 如果資源文件想統一使用ISO-8859-1格式,可以將原本用UTF-8寫好的中文資源文件使用jdk自帶的工具native2ascii將UTF-8文件和內容轉為ISO-8859-1文件,其中的中文內容會使用16進制unicode編碼為u****格式:

cmd命令:

JAVA_HOMEbinnative2ascii -encoding UTF-8 messages_zh_CN.properties messages_zh_C1N.properties

本文主要講解了Java Spring項目國際化(i18n)詳細方法與實例,更多關于Java Spring項目國際化技巧請查看下面的相關鏈接

標簽: Java
相關文章:
主站蜘蛛池模板: 国产区香蕉精品系列在线观看不卡 | 久久人人网 | 成人精品一区二区三区 | 亚色综合 | 国产精品免费观看视频播放 | 99综合久久 | 国语自产免费精品视频一区二区 | 欧美亚洲国产色综合 | 91热这里只有精品 | 国产在视频线精品视频 | 欧美中日韩在线 | 伊人干综合网 | 一级特黄牲大片免费视频 | 麻豆精品国产免费观看 | 亚洲一区二区久久 | 一级毛片aaaaaa视频免费看 | 日韩精品国产精品 | 黄色小视频观看 | 免费看片网址 | 成人偷拍自拍 | 色香影院| 2021国产成人午夜精品 | 国产九九热视频 | 日本在线精品视频 | 午夜男人一级毛片免费 | 国产精品高清视亚洲乱码 | 国产中的精品一区的 | 麻豆福利影院 | 黄色字幕网 | 国产在线短视频 | 国产一区亚洲二区 | 精品视频h | 免费人成网站在线播放 | 国产成人啪精品视频免费网 | 视频在线观看黄 | 久久亚洲精品无码观看不卡 | 日韩激情视频在线 | 日本无卡无吗中文免费 | 毛片毛片毛片毛片毛片毛片毛片 | 四色婷婷婷婷色婷婷开心网 | 成人性视频在线三级 |