2013年9月10日 星期二

[轉貼] 無縫式簡繁轉換


/黃忠成
 
 
 
    前些日子在整理硬碟時,在裡頭找到了許多以往於幾家公司任職顧問時所留下來的程式,這些程式當時都是為了解決該公司開發軟體上的問題而寫的,
那時由於時間上並不充裕,所以常常以WebApplication1、App2、Test1命名,結果現在嘗到苦果,根本不清楚這堆程式到底是為啥而寫的,只好一個個的開來看,
再一一重新命名了。
 
   有趣的是,我在這堆程式中找到了個簡繁轉換的ASP.NET例子,當時會寫下這隻程式的原因是,客戶希望開發的網頁能在瀏覽器是簡體語系時顯示簡體,
在繁體時顯示繁體。唔!我想大多數人看到這,大概會想說我又開始重提幾百年前就有的ASP.NET多語系支援了吧?嘿!不要急,讓我把客戶的需求講完。
 
1、網頁必須於簡體瀏覽器上顯示簡體,繁體瀏覽器上顯示繁體(這超好解的)
2、資料必須來自於資料庫,顯示於網頁(嗯,這不難...等會!這有那麼一點點陷阱的味道)
3、我們的資料庫只存一種語系,所以,若使用者於簡體瀏覽器上輸入簡體時,實際要存成繁體(這...有點難度囉)。
 
   第一個需求很簡單,也很易解,運用ASP.NET的多語系支援便可輕鬆過關,第二個問題就麻煩些了,因為除非有簡繁兩個資料庫,否則怎麼讓一個資料庫輸出
成兩種語言呢?正規的設計師在此多半會運用各個控件的DataBound事件,於內判斷語系進行轉換,比較偏門的設計師(我是其中之一...),可能就會搬出無差別格鬥技,
將整個網頁的Response硬轉,這招雖然很極端,效率上也有疑慮,但也不失為一解決方案。
 
   重點在第三個需求,客戶要求的不僅是顯示,打簡還得存成繁,這就很麻煩了,正規的解法大概有兩種,一是從UI介面著手,在資料存進資料庫前進行轉碼,
二是由資料面著手,從TableAdapter的DB-Direct Access Method著手,一樣是在資料存進資料庫前動手腳。
 
   總結以上所述,我們大概可以理出一個可行的解決方案,那就是在DataBound進行輸出時的轉碼,於TableAdapter中進行輸入轉碼。 聽起來這並不是件很難的事,
不過可以想見的,當網頁或是控件數目眾多,繁複一事暫且不提,漏網之魚是必然有的。
   對此問題我想過許多種解法,一是替換所有的ASP.NET控件,為其訂製字串轉換機制,但是這不僅極端,且成效上也不符合經濟效益,因為很多字串的顯示
跟ASP.NET控件是沒關係的,例如Title。 二是配合Base Page,讓設計師於Init時呼叫一特定函式,為所有可以找到DataBound事件的控件掛上事件,然後於此
事件中進行轉換,當然!TableAdapter部份的寫入轉換仍然是不可少的。
 
 
我的解法
 
     OK,總結以上的敘述,基本上需求是可以達成的,即使過程很繁複!不過我還是希望能找出一個既可達成需求,又不會對設計師造成過大負擔的解決方案。所以我開始由
根本上著手,也就是重新思考ASP.NET應用程式執行的過程,基本上!ASP.NET分為繪製與Post兩個大區塊,繪製部份是呼叫各個控件的Render函式,將輸出繪製到一個
HTMLTextWriter物件中,最後ASP.NET再將此物件內容輸出給瀏覽器。
 
    因此,要進行簡繁轉換動作的最佳場所,就是在Page的PreRenderComplete事件,於此時,所有控件的準備動作皆已完成,接下來的動作便是將內容繪制到HTMLTextWriter中,
所以在此事件中,我們只要一一掃描Page上的所有控件,再一一轉換,等到真正的繪製發生時,就會依照我們所願的將轉好的資料輸出到瀏覽器,這樣一來既沒有無差別轉換法
的效能疑慮,又能夠達到我們的需求。
 
   接著要處理的是輸入部份,對ASP.NET應用程式而言,輸入指的便是使用者於瀏覽器上的控件,如TextBox中輸入字串,此時這個字串會被存成參數,然後以Post方式送回到
ASP.NET頁面,因此!要對輸入字串做轉碼的時間點,就是Page的Init,只要在這個地方對Request的Params屬性內容進行轉換,接下來的動作我們便不需考慮TableAdapter了,
因為輸入的轉碼動作已在UI層就做好了。
 
   問題是,Request.Params可是唯讀的呀,要怎麼才能改呢?其實,這有一個解法,那就是透過Reflection機制,當然!這是一個危險的動作,除非很了解ASP.NET的運作機制,
不然這個東西可是不能亂動的。
 
  如果解決了以上的問題,那麼剩下的就是如何讓設計師更方便使用這個機制,最好的情況是,設計師完全不用管,只要掛上我們寫的類別,就可以讓網站中的所有網頁都納到此簡繁
轉換機制下。正巧!ASP.NET有個類別叫PageHandlerFactory,當使用者要求一個ASP.NET網頁時,ASP.NET會依據.aspx副檔名來建立PageHandlerFactory物件,此物件的工作就是
載入對應的.aspx並執行取得結果後返回給瀏覽器。因此,假如我們可以繼承此PageHandlerFactory,便能於使用者要求.aspx時,取得Page物件,再為其掛上PreRender、Init等事件,
這樣一來,豈不是神不知鬼不覺的將簡繁機制注入到一個原本不考慮此事的網站中了?
 
實作的程式碼
 
   好了,現在就只剩下實作了,以下便是RenderConverter.cs的實作碼,此處簡繁轉換的部份,我運用了Visual Basic的StrConv函式達成,請注意,此函式的轉換存在一些問題,
某些字可能會轉成?號,在實際專案中,我們都是使用自己的簡繁碼對照表。
 
 
.......略
 
 
執行結果如下。
 
 
輸入資料時如下。
 
 
 
更新後如下。
 
 
實體存的資料如下。
 
 
Case Closed
 
   基本上,關於這個問題,我的任務就到此為止了,那時就把此類別及範例交給客戶,至於其如何使用或是之後所遭遇的問題及改動,
就不在我可以公開的範圍了。
 
  另外,當年ASP.NET AJAX並未開始流行,所以此例子也沒考慮到這個,如果你是使用ASP.NET AJAX,雖然我做了些小測試沒有發生錯誤,
但這不代表沒問題。
 
 最後,如果仔細看這個範例,你應該會查覺到,這不是一個僅能運用於簡繁轉換的技巧。
 
範例下載

沒有留言:

張貼留言