欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET企業(yè)級(jí)項(xiàng)目中遇到的國(guó)際化問(wèn)題和解決方法

 更新時(shí)間:2014年07月16日 08:52:28   投稿:junjie  
這篇文章主要介紹了.NET企業(yè)級(jí)項(xiàng)目中遇到的國(guó)際化問(wèn)題和解決方法,說(shuō)明了理國(guó)際化問(wèn)題的一些典型例子和經(jīng)驗(yàn)之談,需要的朋友可以參考下

企業(yè)級(jí)的系統(tǒng)和我們平常桌面、手機(jī)上運(yùn)行的軟件有著很重要的區(qū)別,其中比較重要的一點(diǎn)就是環(huán)境(包括第三方的系統(tǒng)的不同接口以及各系統(tǒng)的不同版本、安全性、數(shù)據(jù)等)比較復(fù)雜,所以不論在產(chǎn)品維護(hù)還是部署過(guò)程中要考慮的因素都有很多。

偶們的系統(tǒng)的最新版本最近在南非上線,遇到了不少問(wèn)題。昨天晚上本屌和同事解決了一個(gè)比較“嚴(yán)重”的問(wèn)題(因?yàn)橛绊懙较到y(tǒng)的核心功能),發(fā)現(xiàn)是由“國(guó)際化”的問(wèn)題所引起。雖然找到問(wèn)題的原因并不困難,但是要在客戶的環(huán)境解決還是費(fèi)了不少精力(因?yàn)椴荒軓漠a(chǎn)品中去修復(fù)這個(gè)問(wèn)題,即使給一個(gè)補(bǔ)丁按照公司的流程至少也需要半個(gè)月)。之前我也幫運(yùn)營(yíng)團(tuán)隊(duì)解決過(guò)一部分這類問(wèn)題,在工作中也看到過(guò)不少,那么這里一并記錄一下吧。希望大家在以后的開發(fā)過(guò)程中多加注意。

一、數(shù)據(jù)庫(kù)的日期時(shí)間格式問(wèn)題

這應(yīng)該是幾乎所有的系統(tǒng)在部署到不同國(guó)家的客戶環(huán)境中都會(huì)遇到的問(wèn)題,卡死不少程序,耗費(fèi)無(wú)數(shù)人力。因此這是在數(shù)據(jù)存儲(chǔ)時(shí)一定要格外注意的地方。這里以SQL Server為例。

(1)數(shù)據(jù)表中的時(shí)間格式

對(duì)于日期和時(shí)間,SQL Server中提供了datetime、datetime2、datetimeoffset、date、time這幾種類型。我個(gè)人推薦只使用datetime和datetime2,因?yàn)檫@兩個(gè)類型與.NET的程序兼容性最好(都可以直接對(duì)應(yīng)成DateTime)。在.NET中也提供了DateTimeOffset類型,但是在一些比較老的語(yǔ)言中并不支持,為了兼容使用datetime就足夠了。使用其他類型會(huì)牽扯到轉(zhuǎn)型的問(wèn)題,應(yīng)盡量避免以免產(chǎn)生額外的問(wèn)題。

需要注意的是,datetime支持的最小時(shí)間是1753年,因此要避免在程序中直接使用DateTime.MinValue傳到數(shù)據(jù)庫(kù)中,否則會(huì)產(chǎn)生out-of-range的錯(cuò)誤。

在一些古老的系統(tǒng)中,會(huì)使用int類型來(lái)存儲(chǔ)日期,因?yàn)閕nt會(huì)占4個(gè)字節(jié),所以兩者互相轉(zhuǎn)型是支持的,使用convert進(jìn)行轉(zhuǎn)型即可,一般不會(huì)有什么問(wèn)題。

(2)時(shí)區(qū)的問(wèn)題

建議在數(shù)據(jù)庫(kù)中所有數(shù)據(jù)都以UTC時(shí)間保存,在顯示的時(shí)候客戶端根據(jù)本地的時(shí)區(qū)進(jìn)行處理,因此在存儲(chǔ)過(guò)程中一定要注意把時(shí)間轉(zhuǎn)換成UTC時(shí)間。如果不保存UTC時(shí)間,以后在顯示和遷移的過(guò)程中難免會(huì)發(fā)生混亂,所以盡量不要使用當(dāng)?shù)氐臅r(shí)間來(lái)保存,即使系統(tǒng)只在一個(gè)地區(qū)使用。

在設(shè)計(jì)SSRS報(bào)表時(shí),也要注意時(shí)區(qū)的問(wèn)題。由于數(shù)據(jù)庫(kù)中存儲(chǔ)的是UTC時(shí)間,所以在調(diào)用Reporting Service服務(wù)的時(shí)候應(yīng)把用戶輸入的時(shí)間轉(zhuǎn)換成UTC時(shí)間后傳給報(bào)表服務(wù)。在顯示的使用,使用如下代碼格式化單元格:

復(fù)制代碼 代碼如下:
=System.TimeZone.CurrentTimeZone.ToLocalTime(Fields!DateTime.Value)

不要在報(bào)表的存儲(chǔ)過(guò)程中對(duì)時(shí)區(qū)進(jìn)行處理。雖然很方便且直觀,但是當(dāng)你的系統(tǒng)有幾十張報(bào)表并且由不同的人開發(fā)的時(shí)候就會(huì)發(fā)現(xiàn)混亂的局面。

(3)報(bào)表的時(shí)間格式

這是微軟考慮不周導(dǎo)致的問(wèn)題。由于報(bào)表服務(wù)是一個(gè)Web Service,所以在實(shí)際的運(yùn)行環(huán)境中無(wú)論是日期控件還是報(bào)表中輸出日期的單元格都是使用IE的語(yǔ)言設(shè)置,而非Windows的地區(qū)。但是在設(shè)計(jì)報(bào)表的時(shí)候它又是使用的Windows的日期格式。如果客戶有這類需求,那么我們至少修改IE的設(shè)置。

但是最坑人的地方是要修改的是數(shù)據(jù)庫(kù)所在的服務(wù)器的IE設(shè)置,這樣做會(huì)影響到所有的客戶端,那么這也沒辦法。有時(shí)候這樣做了也不見得好使,似乎又與數(shù)據(jù)庫(kù)的連接用戶的默認(rèn)語(yǔ)言有關(guān),所以這類問(wèn)題我們一般不會(huì)花太多時(shí)間去修正。

好在這只是一個(gè)顯示的問(wèn)題,并不影響系統(tǒng)的正常功能,也不會(huì)出錯(cuò),所以很少會(huì)聽到客戶抱怨這類問(wèn)題。

(4)應(yīng)用程序與數(shù)據(jù)庫(kù)之間的日期時(shí)間傳遞

我們首先來(lái)看下面一段代碼:

復(fù)制代碼 代碼如下:
DateTime creation_time = DateTime.Now.AddDays(-1);
string sql = string.Format("SELECT * FROM [User] WHERE [CreationTime] < '{0}'", creation_time);

在數(shù)據(jù)庫(kù)中CreationTime是一個(gè)datetime類型,而我們單引號(hào)之間的內(nèi)容是一個(gè)字符串,此時(shí)SQL Server會(huì)自動(dòng)對(duì)字符串進(jìn)行轉(zhuǎn)型,因此相當(dāng)于隱式實(shí)現(xiàn)了一個(gè)convert語(yǔ)句,把{0}處的內(nèi)容轉(zhuǎn)型成datetime類型。{0}處實(shí)際上是一個(gè)字符串,在string.Format方法里會(huì)隱式調(diào)用creation_time的ToString方法,這個(gè)方法所返回的日期時(shí)間的格式會(huì)依據(jù)當(dāng)前線程所處的語(yǔ)言環(huán)境來(lái)確定。

在調(diào)試的過(guò)程中似乎不會(huì)發(fā)現(xiàn)問(wèn)題,但是當(dāng)產(chǎn)品給一個(gè)國(guó)外的客戶時(shí),就很有可能發(fā)現(xiàn)SQL Server拋出out-of-range的錯(cuò)誤,大意是將varchar轉(zhuǎn)型成datetime類型時(shí)越界。導(dǎo)致這個(gè)問(wèn)題的原因簡(jiǎn)單來(lái)說(shuō)就是因?yàn)榭蛻舳说娜掌诟袷脚c數(shù)據(jù)庫(kù)的設(shè)置不一致(更詳細(xì)的下面會(huì)說(shuō)明),此時(shí)系統(tǒng)理解的年、月、日這幾個(gè)字段所處的位置不在正確的地方上,因此有時(shí)會(huì)越出月或者日的邊界。

避免這個(gè)問(wèn)題的根本解決辦法是在訪問(wèn)數(shù)據(jù)庫(kù)的時(shí)候只使用參數(shù)形式,根本不應(yīng)該出現(xiàn)非參數(shù)的拼接形式(同時(shí)可以避免SQL注入以及其他bug),如果你在做代碼審查,看到上面這種代碼應(yīng)該果斷reject回去。使用參數(shù)形式的代碼大概是這樣:

復(fù)制代碼 代碼如下:

SqlCommand command = new SqlCommand("SELECT * FROM [User] WHERE [CreationTime] < @creationTime", connection);
command.Parameters.AddWithValue("creationTime",  creation_time);

這樣,就會(huì)避免將DateTime類型轉(zhuǎn)型成字符串,直接使用SQL Server兼容的類型訪問(wèn),就避免了格式不一致導(dǎo)致的轉(zhuǎn)型失?。ɑ蛘咿D(zhuǎn)換成錯(cuò)誤的結(jié)果)。

我們?cè)谙到y(tǒng)日志中發(fā)現(xiàn)某個(gè)模塊頻繁拋出越界的錯(cuò)誤,就是因?yàn)闆]有使用參數(shù)的形式進(jìn)行訪問(wèn)。與此對(duì)應(yīng)的系統(tǒng)其他模塊都沒有這個(gè)類型的錯(cuò)誤。

 (5)SQL語(yǔ)句中的日期格式轉(zhuǎn)型問(wèn)題

這就是昨天做支持過(guò)程中遇到的一個(gè)非常詭異的問(wèn)題。在我們的產(chǎn)品的SQL語(yǔ)句中有一段類似下面這樣的代碼:

復(fù)制代碼 代碼如下:

DECLARE @startTime VARCHAR(100)
SELECT @startTime = CONVERT(VARCHAR, timeIn, 23), @endTime = CONVERT(VARCHAR, timeIn + 1, 23) FROM...

我們不談?wù)撨@段代碼寫得怎樣,我們只關(guān)心為什么會(huì)出錯(cuò)。在客戶現(xiàn)場(chǎng)中,客戶的描述是:“在前一天系統(tǒng)還是好的,但是在換班后(我們的系統(tǒng)執(zhí)行換班操作后日期會(huì)往后加一天),系統(tǒng)就無(wú)法操作。”

分析日志發(fā)現(xiàn)拋出的錯(cuò)是日期轉(zhuǎn)型越界,出錯(cuò)的代碼片段應(yīng)該就是在這個(gè)地方,那么為什么會(huì)出錯(cuò)呢?

經(jīng)過(guò)一番折騰,發(fā)現(xiàn)了SQL Server的一個(gè)隱藏炸彈(還好有測(cè)試的同事在其他系統(tǒng)見過(guò)這個(gè)問(wèn)題,否則也不會(huì)往這個(gè)方向去想):對(duì)于每一個(gè)有權(quán)限訪問(wèn)數(shù)據(jù)庫(kù)的用戶(包括Windows登錄方式和SQL Server登錄方式),在用戶的屬性中都有一個(gè)稱之為“默認(rèn)語(yǔ)言”的屬性。在我們公司的產(chǎn)品中,基本只會(huì)在British English和English這兩者之間進(jìn)行選擇。

這個(gè)屬性會(huì)影響到SQL語(yǔ)句中牽涉到日期與字符串轉(zhuǎn)型時(shí)的處理。因此,在SQL語(yǔ)句中轉(zhuǎn)型是與這個(gè)“默認(rèn)語(yǔ)言”有關(guān)系的。同時(shí),還會(huì)與客戶端(訪問(wèn)數(shù)據(jù)庫(kù)的服務(wù)所在的機(jī)器)的本地時(shí)間格式有關(guān)。

客戶的數(shù)據(jù)庫(kù)登錄用戶的默認(rèn)語(yǔ)言選擇是British English,而數(shù)據(jù)庫(kù)服務(wù)器和客戶端的時(shí)間格式配置都是南非的時(shí)間格式。這種不一致可能會(huì)導(dǎo)致一些不可預(yù)知的問(wèn)題。我們要用戶把默認(rèn)語(yǔ)言給成English(注意:1. 改了之后要重啟SQL Server的服務(wù)使之生效; 2. 特別注意修改的是訪問(wèn)數(shù)據(jù)庫(kù)的用戶,每個(gè)用戶都有這個(gè)屬性),然后問(wèn)題莫名其妙地就解決了。

但是后來(lái)我們?cè)趯?shí)驗(yàn)環(huán)境中按照這種配置都無(wú)法重現(xiàn)這個(gè)問(wèn)題,我覺得有可能是客戶修改了一些日期格式的原因所導(dǎo)致。

今天早上我做了一個(gè)實(shí)驗(yàn),把默認(rèn)語(yǔ)言設(shè)置成British English,而客戶端的時(shí)間格式是中國(guó)的格式,此時(shí)上面的timeIn + 1得到的結(jié)果竟然是月份加1!這樣就使我們恍然大悟:難怪客戶在前一天沒有問(wèn)題,而過(guò)了一天問(wèn)題才出現(xiàn)。因?yàn)槌霈F(xiàn)問(wèn)題的時(shí)間是6月12日,由于某種特殊的配置(我們至今還是沒嘗試出是何種格式),系統(tǒng)把12當(dāng)成是月份+1,得到13就越界了,而使用6月11日就不會(huì)出越界的問(wèn)題,但結(jié)果是錯(cuò)誤的。

這顯而易見是微軟的一個(gè)bug,無(wú)論何種配置,都不應(yīng)該轉(zhuǎn)型出錯(cuò),即使是月份+1,應(yīng)該會(huì)進(jìn)到年的字段去。

上面這段SQL語(yǔ)句的正確寫法應(yīng)該是使用DATEDIFF進(jìn)行日期運(yùn)算,而且更不應(yīng)該出現(xiàn)第三個(gè)參數(shù)“23”這種直接指定格式的輸出。

(6)結(jié)論

通過(guò)上面的分析,有一點(diǎn)可以肯定的是:在編碼過(guò)程中,除非僅用于顯示,否則必須要避免日期和字符串的相互轉(zhuǎn)換!

日期轉(zhuǎn)型成字符串應(yīng)該只在客戶端顯示的最后一步進(jìn)行轉(zhuǎn)型,而字符串轉(zhuǎn)換成時(shí)間應(yīng)該只在應(yīng)用程序中使用DateTime提供的與時(shí)間格式相關(guān)的方法進(jìn)行完成。一定要避免使用字符串進(jìn)行日期和時(shí)間的運(yùn)算,即使是在SQL語(yǔ)句中也是如此。

這樣應(yīng)該就能避免由時(shí)間格式所導(dǎo)致的問(wèn)題,而且也不會(huì)碰到(5)里面那種詭異的問(wèn)題:即使知道問(wèn)題出在哪,也不知道怎么樣設(shè)置才不會(huì)出這種問(wèn)題。

上面是著重闡述的與數(shù)據(jù)庫(kù)相關(guān)的日期格式問(wèn)題,下面再提兩種我遇到的小問(wèn)題。

二、Web Server的日期格式問(wèn)題

微軟的.NET與JSON格式兼容得不是很好,我碰到了兩個(gè)問(wèn)題。

(1)JSON解析DateTime格式

JSON的時(shí)間格式和.NET完全不一樣。DateTime說(shuō)白了就是一個(gè)8字節(jié)的二進(jìn)制位,而JSON是以字符串來(lái)表示日期的。JSON的日期類似于“/Date(1242357713797+0800)/”。

一種比較懶的處理方式是直接使用eval語(yǔ)句讓系統(tǒng)自動(dòng)解析,這種方法的效果比較好(性能就不知道了),轉(zhuǎn)換后可以按照當(dāng)?shù)氐娜掌诟袷竭M(jìn)行輸出,比較方便。

(2).NET解析JSON的日期格式

從JSON的字符串進(jìn)行解析時(shí),除了把中間的數(shù)字進(jìn)行運(yùn)算外(1970年1月1日+數(shù)字*10000作為DateTime類型的Ticks),還要考慮后面的時(shí)區(qū)。處理比較麻煩。

當(dāng)然可以想一個(gè)偷懶的辦法,那就是直接傳字符串而不通過(guò)JSON的日期格式:既然客戶端顯示的是本地時(shí)間格式,那么如果服務(wù)器的時(shí)間格式也是一樣的就可以直接用DateTime的Parse就完事了。這種方式適合偷懶人士(比如我),暫時(shí)也沒有發(fā)現(xiàn)什么問(wèn)題,除了接口類型不太好看以外。

(3)微軟的bug

又是時(shí)間格式引發(fā)的bug!將Web Server的格式設(shè)置為南非的以后,發(fā)現(xiàn)所有將DateTime序列化成JSON格式的代碼全都報(bào)錯(cuò):日期時(shí)間轉(zhuǎn)換越界。在調(diào)用堆棧中全部都是系統(tǒng)的代碼,將時(shí)間格式設(shè)置成美國(guó)后問(wèn)題消失,因此可以肯定這是微軟的一個(gè)bug(在4.0版本的.NET Framework中,不知道是否修正了)。

那么只能讓客戶使用美國(guó)的時(shí)間格式湊合著用了,我們也只能在下一個(gè)版本解決這個(gè)問(wèn)題。方案很簡(jiǎn)單,直接轉(zhuǎn)換成字符串傳給客戶端就行了,這樣客戶端也不必寫代碼去解析DateTime格式。

由于.NET本身就和JSON的兼容有缺陷,所以使用上述辦法也并無(wú)不妥。假如客戶有國(guó)際化的需求,其實(shí)可以定義一種中間格式(比如傳輸?shù)倪^(guò)程中都以中國(guó)的格式為準(zhǔn)),客戶端和服務(wù)器按照這個(gè)中間格式進(jìn)行解析,就能避免由格式不配套產(chǎn)生的轉(zhuǎn)型錯(cuò)誤。

三、文本的國(guó)際化處理

這個(gè)說(shuō)起來(lái)很簡(jiǎn)單,只要改變編碼習(xí)慣就可以了。在一個(gè)資源文件(比如XML格式)中首先以默認(rèn)語(yǔ)言(比如中文)存儲(chǔ)所有需要顯示出來(lái)的字符串以及對(duì)應(yīng)的名稱(名稱最好按功能+內(nèi)容來(lái)命名,不要用數(shù)字編號(hào),這樣可以避免在有10000個(gè)記錄的資源文件中不確定是否有某個(gè)需要的字符串的尷尬),然后依葫蘆畫瓢制作其他的語(yǔ)言包。

在應(yīng)用程序中,推薦使用MVVM或者M(jìn)VP模式進(jìn)行數(shù)據(jù)綁定,令其作為靜態(tài)資源定位到一個(gè)中間的靜態(tài)類,由這個(gè)類決定使用哪種語(yǔ)言包。這樣做的好處是我們不需要寫代碼去初始化界面上各種文本標(biāo)簽(試想我們?cè)诖绑w加載的使用寫一大堆語(yǔ)句去設(shè)置Label或者M(jìn)enu的Text?)。使用數(shù)據(jù)綁定,系統(tǒng)會(huì)在需要顯示這個(gè)資源的時(shí)候訪問(wèn)靜態(tài)類去取文本內(nèi)容。

微軟已經(jīng)考慮到這個(gè)問(wèn)題并把它做得很完善(除了一個(gè)小bug外我們暫時(shí)沒發(fā)現(xiàn)其他的bug,被上面的兩個(gè)微軟的bug搞怕了吧?這個(gè)小bug影響不大在此不做說(shuō)明),你可以參考這里:http://msdn.microsoft.com/en-us/kb/ms745650(v=vs.80)

這樣做的好處是:在程序編譯后會(huì)生成幾個(gè)文件夾,這些文件夾按語(yǔ)言的名字來(lái)命名(比如zh-cn表示簡(jiǎn)體中文),文件夾中有資源文件的dll。在系統(tǒng)啟動(dòng)的時(shí)候可以讓用戶選一種語(yǔ)言,然后設(shè)置CultureInfo,這樣系統(tǒng)就會(huì)自動(dòng)去相應(yīng)的文件夾取這個(gè)語(yǔ)言包里的字符串。還有一個(gè)很人性化的功能,就是如果在對(duì)應(yīng)語(yǔ)言的資源文件中沒有找到這個(gè)字符串,就會(huì)從默認(rèn)的語(yǔ)言包中取出并顯示。(比如我們產(chǎn)品有繁體中文的語(yǔ)言包,如果有些地方?jīng)]有進(jìn)行翻譯,在繁體中文的語(yǔ)言包中就不會(huì)有這個(gè)字符串,此時(shí)系統(tǒng)會(huì)顯示出英文語(yǔ)言包中的字符串。)

以上是我遇到的處理國(guó)際化問(wèn)題的一些典型例子,如果你遇到了一些這樣的問(wèn)題,歡迎指出并共同探討。

相關(guān)文章

最新評(píng)論