摘要:特別是開發一些只做國內市場,只有中文的項目時,可能就直接被忽視了。應用場景的使用場景基本就是根據不同國家和語言,進行不同的顯示。比如中解析時,可以同時處理兩種格式。一組為,一組為。對于中文,英文,日文都有一個默認的匹配。
摘要
Locale是日常開發中比較容易忽視的技術點。特別是開發一些只做國內市場,只有中文的項目時,Locale可能就直接被忽視了。而且在項目提出多語言支持的時候,因為沒有很好的理解,可能給自己埋了很多坑。
什么是Locale其實java.util.Locale的Java Doc有很詳細的解釋,我就不過多解釋。
A Locale object represents a specific geographical, political, or cultural region. An operation that requires a Locale to perform its task is called locale-sensitive and uses the Locale to tailor information for the user.
主要記住Locale實例包括以下信息就可以了。在多年的開發經驗中,script和variant基本沒有用到。就不過多介紹。
language
script
country (region)
variant
lanugageISO 639 alpha-2 or alpha-3 language code
在實際使用中,基本我們碰不到3位字母表示的語言。
countryISO 3166 alpha-2 country code or UN M.49 numeric-3 area code.
同樣實際使用中,基本使用2位字母表示的國家
scirpt 和 variantIANA Language Subtag Registry 定義了完整列表。
感覺script是地區的別稱,variant是方言。了解一下就好。
在實際使用中,大部分同學第一反應可能是會寫出以下Locale。
zh_CN
en_US
ja_JP
如果你也是只想到這些,請打開你的瀏覽器->開發者工具->控制臺中輸入以下js
window.navigator.language
瀏覽器為中文,你又在國內。輸出結果就是zh-CN
然后如果重新你設置瀏覽器語言,比如設置成英文。再執行一下,輸出結果是en-CN
What?? en-CN?? 這是什么鬼? 首先通過這個Locale,我們可以知道country,是和你實際在哪個地區有關。
那該怎么處理? 后面詳細說怎么應用。
Locale的使用場景基本就是根據不同國家和語言,進行不同的顯示。實際經驗以下2點為主。
多語言 (下文會詳細說明)
金額顯示。
日期格式顯示。
以金額顯示為例,如果沒有類似開發經驗的話,你可以為會想,金額還有不同的格式。一般不都是 1,000,000.00。那你就錯了。舉兩個例子。
日語。日本人金額是不帶小數點的。
法語。法語中千位分隔符為空格,小數點為逗號。比如 1 000 000,00
正確理解Locale,并正確使用可以寫出既規范,又簡練,質量又高的代碼。而不是見招拆招,每個語言寫一個自己的實現。
創建Locale 實例的正確姿勢使用正確的姿勢創建非常重要,這在后面Spring里應用部分非常重要。
以下這段代碼是我見過最多的創建方式。
Locale locale = new Locale("zh_CN");
其中zh_CN可能是前端直接傳入,為了方便直接作為Locale構造方法參數。其實這是一個錯誤的用法。這樣的使用,創建出來的Locale.language就是zh_cn。
以下先列舉一下兩種正確姿勢。然后比較一下結果
Locale API
// 使用Locale構造方法 // 如果前端傳入"zh_CN",此處需要自行解析并拆分 Locale locale = new Locale("zh", "CN"); // 使用Locale預置常量。請自行查看Locale源代碼。 Locale locale = Locale.SIMPLIFIED_CHINESE
Commons-Lang LocaleUtils.toLocale()
Locale locale = LocaleUtils.toLocale("zh_CN");
以上三種創建方式,可以創建出一樣的Locale object。
本人推薦使用Commons Lang3 LocaleUtils.toLocale()
下面我們的來對比一下錯誤和正解方式創建的Locale有什么區別。
public class LocaleShowCase { public static void main(String[] args) { logLocale(new Locale("zh_CN")); logLocale(Locale.SIMPLIFIED_CHINESE); } private static void logLocale(Locale locale) { System.out.println("================================="); System.out.println(String.format("Locale.toString: %s", locale.toString())); System.out.println(String.format("Language: %s", locale.getLanguage())); System.out.println(String.format("Country: %s", locale.getCountry())); System.out.println(String.format("LanguageTag: %s", locale.toLanguageTag())); System.out.println("================================="); } }
輸出結果
================================= Locale.toString: zh_cn Language: zh_cn Country: LanguageTag: und ================================= ================================= Locale.toString: zh_CN Language: zh Country: CN LanguageTag: zh-CN =================================
讓我們來分析一下結果
首先看一下數據。錯誤的創建方式,其實是把zh_CN作為language。Country和LanguageTag為空
Language輸出時均為小寫
Country輸出時均為大寫
LanugageTag為Language和Country以"-"連接
Locale.toString則是Language和Country以"_"連接
zh_CN vs zh-CN什么時候使用"_",什么時候使用"-",確實比較搞。
比如request.getLocale()中解析Locale時,可以同時處理兩種格式。而Commons Lang3 LocaleUtils.toLocale()的入參只支持下劃線格式。
不過我們可以定義這樣的規范,在后端服務中只使用"_"格式,而前端只使用"-"格式。
前端前端框架太多,就只說一下最近在玩的umi+dva+react。
UMI Locale處理umi開發的項目中使用umi-plugin-react/locale來處理Locale。
import { setLocale, getLocale } from "umi-plugin-react/locale"; setLocale(language, true); getLocale();多語言
資源文件使用"-"格式命名。
. |-- en-US | |-- common.ts | `-- form.ts |-- ja-JP | |-- common.ts | `-- form.ts |-- zh-CN | |-- common.ts | `-- form.ts |-- en-US.ts |-- ja-JP.ts `-- zh-CN.ts顯示多語言
import { formatMessage } from "umi-plugin-react/locale"; formatMessage({id: "xxx"})日期顯示
import { formatDate } from "umi-plugin-react/locale"; formatDate(new Date());數字顯示
formatNumber(10000000.00);后端服務
這里只介紹基于Spring Boot開發的Stateless Rest API。SpringMVC已經過時,就不做介紹。
Locale處理 如何確認當前API調用使用的Locale?Spring Boot使用LocaleResolver來確定當前API調用使用什么Locale。在LocaleResolver獲取Locale之后,將Locale存入LocaleContextHolder中。
Spring Boot提供了幾個標準實現,主要區別是針對Locale存放的地方不一樣提供對應獲取方式
AcceptHeaderLocaleResovler 從Request Header的Accept-Language中獲取
CookieLocaleResolver 從Cookie中獲取
FixedLocaleResolver 固定Locale,只使用系統配置的Locale
SessionLocaleResolver 從Session中獲取
雖然Spring已經提供了多種獲取LocaleResolver實現,但是在具體業務場景中會有更復雜的場景。比如需要根據當前登錄用戶的語言設置。這個時間就需要我們自己實現一套LocaleResovler。
多語言 資源文件在Spring中,我們可以添加properties文件來做多語言支持。
. |-- java .... `-- resources `-- i18n |-- messages.properties |-- messages_ja.properties |-- messages_ja_JP.properties |-- messages_xx.properties |-- messages_zh.properties |-- messages_zh_CN.properties |-- another.properties |-- another_zh_CN.properties |-- another_zh_TW.properties |-- another_en.properties `-- another_ja.properties
可以看到在例子
有兩組資源文件。一組為message,一組為anthor。在項目中可以這種方式進行模塊化管理。
每組資源文件可以有自己支持的locale列表
每個文件定義對應locale的翻譯
沒有locale資源文件為默認語言。如messages.properties, another.properties。當沒有locale匹配時,使用默認資源文件內容。
messages_xx.properties? 這是合法的。但建議使用,也基本不會碰到。這里是說明一下,框架是支持的。用new Locale("xx")可以創建language為xx的Locale。
Locale匹配優先級language+country+variant > language+country > lanaguage
以message*.properties為例:
zh_CN -> messages_zh_CN.properties
zh或zh_JP -> messages_zh.properties
en_US -> messages.properties.
配置MessageSource@Configuration public class MessageConfiguration { @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setDefaultEncoding("UTF-8"); messageSource.setBasenames("classpath:i18n/messages", "classpath:i18n/another"); return messageSource; } }
這里注意messageSource.setBasenames("classpath:i18n/messages", "classpath:i18n/another")BaseName就是資源文件的組名。
使用方式@Service public class XXXService { private final MessageSource messageSource; public XXXService(MessageSource messageSource) { this.messageSource = messageSource; } public String getI18N(String key, Object[] params) { return messageSource.getMessage(key, params, LocaleContextHolder.getLocale()) } }日期顯示
DateFormat fullDF = DateFormat.getDateInstance(DateFormat.FULL, locale); System.out.println(fullDF.format(new Date()));數字顯示
System.out.println(NumberFormat.getInstance(locale).format(10000000));實際場景中的應用
一般產品基本需要用戶登錄,在LocaleResovler中也提到。我們可以根據當前用戶的語言設置作為使用Locale。這樣比較好控制服務接收到的Locale。而且我們在開發時可以定義好系統支持的語言,比如支持zh_CN, en_US, ja_JP。這樣在用戶登錄后的API調用就不用擔心接收到不支持的Locale。而因為需要使用用戶設置語言,我們需要自己實現一個LocaleResovler。
@Data public class Principal { private String username; private String language; ... } public class CustomLocaleResolver implements LocaleResolver { private Locale defaultLocale; public CustomLocaleResolver(Locale defaultLocale) { this.defaultLocale = defaultLocale; } public Locale resolveLocale(HttpServletRequest request) { Principal principal = (Principal) SecurityContextHolder.getContext().getAuthentication(); if (principal != null && !StringUtils.isEmpty(principal.getLanguage())) { return LocaleUtils.toLocale(principal.getLanguage()); } else { return request.getHeader("Accept-Language") != null ? request.getLocale() : this.defaultLocale; } } public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { throw new UnsupportedOperationException("Cannot change Principal data - use a different locale resolution strategy"); } } @Configuration public class LocaleConfiguration { @Bean public CustomLocaleResolver localeResolver(@Value("${default-language:zh_CN}") String defaultLanguage) { return new CustomLocaleResolver(LocaleUtils.toLocale(defaultLanguage)); } }
而用戶沒有登錄之前,而前端不做任何處理時,后端會接收到類似en_CN的Locale,而無法匹配資源文件。如果按以下資源文件設計,給每個語言設置一個默認翻譯,則可以解決接收到不規則Locale問題。
. |-- java .... `-- resources `-- i18n |-- messages.properties |-- messages_ja.properties |-- messages_ja_JP.properties |-- messages_en.properties |-- messages_en_US.properties |-- messages_en_GB.properties |-- messages_zh.properties |-- messages_zh_CN.properties `-- messages_zh_TW.properties
這樣的編排方式,不管你在什么國家。對于中文,英文,日文都有一個默認的匹配。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105963.html
摘要:特別是開發一些只做國內市場,只有中文的項目時,可能就直接被忽視了。應用場景的使用場景基本就是根據不同國家和語言,進行不同的顯示。比如中解析時,可以同時處理兩種格式。一組為,一組為。對于中文,英文,日文都有一個默認的匹配。 摘要 Locale是日常開發中比較容易忽視的技術點。特別是開發一些只做國內市場,只有中文的項目時,Locale可能就直接被忽視了。而且在項目提出多語言支持的時候,因為...
摘要:概述為我們提供國際化支持,通過設置系統的環境,根據運行環境使用不同的語言顯示。提供接口的作用是解析客戶端使用的地區,目的是為了根據這些信息實現視圖多語言即國際化。接口繼承接口,增加時區支持。 概述 Spring MVC為我們提供國際化支持,通過設置系統的環境,根據運行環境使用不同的語言顯示。Spring提供LocaleResolver接口的作用是解析客戶端使用的地區(Locale),目...
摘要:與一樣,該類繼承抽象類,并且通過外部的屬性文件定義邏輯視圖名稱與真正的視圖對象的關系,屬性文件默認是下的,可以通過或屬性來指定,該屬性指的是文件的基名稱,也就是說以屬性值開頭的屬性文件。 概述 本章再學習另外兩個ViewResolver,分別是XmlViewResolver和ResourceBundleViewResolver,從功能上說,這兩個視圖解析器都是從外部資源文件中查找視圖V...
摘要:概述上一篇就默認的進行了分析,詳細請參考,本節我們繼續分析學習,主要分析解析器類繼承關系如下圖由上面類圖可知,繼承并實現接口,主要是操作的工具類,繼承接口,增加了信息操作。即通過實現的選擇。 概述 上一篇就Spring MVC默認的LocaleResovler(AcceptHeaderLocaleResolver)進行了分析,詳細請參考https://segmentfault.com/...
摘要:此解析器與差不多,更改下配置文件中的類全路徑即可。總結本章介紹了以及三個視圖解析器。這部分內容有點兒多,我會盡快結束。 概述 通過上幾篇的學習,我們分析了并試驗了ViewResolverComposite、BeanNameViewResolver和ContentNegotiatingViewResolver,這三個類都直接實現ViewResolver接口。Spring MVC提供了很多...
閱讀 3422·2023-04-25 22:44
閱讀 926·2021-11-15 11:37
閱讀 1632·2019-08-30 15:55
閱讀 2639·2019-08-30 15:54
閱讀 1080·2019-08-30 13:45
閱讀 1430·2019-08-29 17:14
閱讀 1853·2019-08-29 13:50
閱讀 3402·2019-08-26 11:39