LCL Unicode Support/zh TW

From Free Pascal wiki
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

介紹

Lazarus對Unicode的支援還需要進一步開發,尤其是在Windows平台上。以下提供一些基本的資訊,讓想要加強Lazarus對Unicode支援的人參考,如果您發現這些資訊有誤、不足或過時了,請您不吝修正、補充或更新它,謝謝。

如果您已經初步了解Unicode的標準,且您已經在Delphi上面有使用過WideString這個型別來撰寫程式,會有助於您理解Lazarus對Unicode支援的加強工作。如果您使用過非Latin編碼的字元集來撰寫腳本語言,也會有些幫助的。

請注意: 實作的細節部分目前還在討論中,這部分的文件隨時都有可能更新。

程式實作規範

需求

Lazarus的精神是”程式寫一次,就能在各種平台上『編譯』” (JAVA的精神則是『程式寫一次,就能在各種平台上『執行』)。根據Lazarus的精神,表示在理想狀態下,一個支援Unicode的應用程式,應該只有一份能夠支援Unicode的程式碼,而不用為各種不同的語系作不同的原始碼,或者資源檔。更不用在程式碼裡面使用條件定義(IFDEF)為不同的作業系統/不同語系作編譯的定義。

在LCL的 “interface”宣告部分,已經可以支援各種相容於Unicode的作業系統,讓程式人員可以不用去管每個系統上面對Unicode要怎麼處理的繁瑣細節。

而Lazarus本身需要注意的,則是在Lazarus內部用來溝通的字串 (例如應用程式跟LCL,以及 LCL跟視窗元件之間),都是透過Pascal的原始string (string裡的每個字元都是1個Byte,而Unicode是多個Byte的字元集)。因此,邏輯上來說,Lazarus的程式碼就必須以UTF-8編碼來儲存,才能保留住所有Unicode的相關資訊。

和Unicode進行整合

目前絕大多數版本的Lazarus使用的是Ansi編碼,因為這是Gtk1預設的編碼法,也是Win32目前的預設編碼法,Windows 2000以後的Windows作業系統雖然都已經能夠相容於Unicode,但預設的編碼法仍然是Ansi,這個情形在不久的未來恐怕會有所改變,所有的視窗元件都會支援UTF-8,而所有的應用程式在傳遞資料給介面時,也都需要先轉換成 UTF-8了,當然,這得依賴各種IDE的作者在元件檢視器裡面更改程式碼才能作到。

當我們在還沒完全支援Unicode的視窗元件上面開發程式的時候(例如 Gtk 2, Qt, WinCE, 或許也包含將會出現的Win32U),我們是使用IDE來對比較穩定的視窗元件進行編譯的 (例如Gtk跟Win32)。為了保持一致性(例如以ISO字元編碼把資料傳遞給UTF-8的視窗元件),讓IDE跟視窗元件使用一致的編碼是必要的,這也就表示在能夠製作出Unicode的應用程式前,我們將會需要一個穩定的UTF-8的IDE程式。

目前我們有幾組不同的視覺元件,分別使用以下的編碼法:

  • 使用ANSI編碼法: win32跟 gtk (1) 介面
  • 使用 UTF-8 編碼法: gtk (1), gtk2, qt, fpGUI, carbin
  • 目前還使用ANSI編碼,但需要升級到UTF-8編碼: win32, wince

請留意,gtk 1同時屬於ANSI跟UTF-8的陣營喔,這是因為gtk 1的編碼法,是可以從Gtk 1的環境變數裡面加以控制的。

正如目前的Lazarus一樣,大多數的應用程式目前都能正常運作,如果用win32, wince或gtk介面重新編譯,就得面對要編譯其他視窗元件時,使用不同編碼的窘境了。而支援UTF-8的應用程式在重新編譯給使用Unicode的視窗元件時,就沒有這個問題。

很重要的一點是,當您要編譯程式時,請記得使用跟您要編譯的程式相同編碼法的IDE來作。這是因為IDE在進行程式編譯的時候,IDE是用它被編譯時的編碼法來產生LFM跟LRS檔案的,而不是我們想要甚麼編碼法,它就能自動切換過去的,這點非常重要,不可不察。

發展路線

目前我們已經有了準則,所以該建立發展路線,並加以實現的時候了。 為此,我們建立了以下的計畫,我們的計畫是把工作分為兩個群組,一個是主要工作,另一個是次要工作。

所有的主要工作都必須在我們宣布Lazarus支援之前完成,這些工作會被當成我們工作中的主要確認部分。

次要工作則是該作,但沒有自願者想作,或者為這些工作定出範圍之前不會進行的。


主要工作

使 Win32 視窗元件支援 UTF-8

備註: 在這個步驟中,我們會將所有的32 bits Windows作業系統同時當成目標,這階段中所有寫出來的程式碼都會在目前的win32介面中用IFDEF來區隔,以避免在主要的介面中產生問題。在過渡時期結束之後,IFDEF的宣告就會全部移除,只留下Unicode的支援。

狀態: 部分已完成。


更新 Gtk 2 的鍵盤程式碼,使得UTF-8能夠被支援

備註:

狀態: 幾乎已完成。部分gtk2預先定義的功能,讓使用者自訂的部分還沒有完成,但我不知道哪幾個語系會使用者這些功能。


讓Lazarus IDE能正確的跟Win32 Unicode視窗元件運作並支援UTF-8

備註:

狀態: 已完成。除了字元對應表,目前字元對應表仍舊只顯示255個字元,但反正目前所有的OS都已經提供了很好的Unicode字元對應表,所以這應該也不是那麼重要了。

讓Lazarus IDE能正確的跟Gtk 2視窗元件運作並支援UTF-8

備註:

狀態: 已完成。還有些gtk2介面的問題,但這已經跟UTF-8無關了

次要工作

升級Windows CE的視窗元件,使它能使用UTF-8

備註: 字串轉換的程式碼已經集中在winceproc.pp這個檔案裡面,還需要許多測試。

狀態: 尚未開始


升級Gtk 1鍵盤功能,讓它能使用UTF-8

備註:

狀態: 尚未開始


完成synedit對由右到左(RTL)的文字顯示的支援

備註: RTL是指由右到左的輸入法,例如阿拉伯文。

狀態: 尚未開始

Unicode 須知

Unicode的標準是將整數的0到10FFFF(十六進位)對應為文字。每一個對應關係,稱為一個個的編碼點(code point)。換句話說,Unicode的字元原則上是定義了U+000000到U+10FFFF個編碼點(用十進位來算,是 0到1,114,111)。

要表現Unicode的編碼點的位元順序,一共有三種方法。這些方法被稱為Unicode轉換格式(Unicode transformation formats)分別是: UTF-8, UTF-16和UTF-32。這三種格式之間是可以相互轉換的,以下是這些格式的基本說明:

(原文)

                           UTF-8 UTF-16 UTF-32
Smallest code point [hex] 000000 000000 000000
Largest code point  [hex] 10FFFF 10FFFF 10FFFF
Code unit size [bits]          8     16     32
Minimal bytes/character        1      2      4
Maximal bytes/character        4      4      4

(中文)

                           UTF-8 UTF-16 UTF-32
最小編碼點 [十六進位] 000000 000000 000000
最大編碼點  [十六進位] 10FFFF 10FFFF 10FFFF
單位編碼Size [bits]          8     16     32
佔用位元組量(最少)        1      2      4
佔用位元組量(最多)        4      4      4


UTF-8 包含幾個重要且有用的屬性:它是以Byte的順序進行解譯的,所以沒有Hi-Byte跟Lo-Byte的差別(其它雙位元組的多國語系字元編碼都有這樣的問題)。 Unicode對字元的定義中,從U+0000到U+007F (ASCII)正好就是直接對應到00h到7Fh,可以跟ASCII直接相容。這意味著使用7-bit ASCII字元的檔案或字串,在傳遞ASCII跟UTF-8的時候,是完全相同的編碼方式。而編碼點大於U+007F的字元則是依照位元組的順序進行編碼,正好每兩個Byte就能代表一個Unicode的字元。因此不會出現一個字元的Byte使用或涵蓋到別的字元的資料,也使得製作子字串的搜尋功能簡單多了。代表非ASCII字元的位元組中,第一個Byte的內容一定必須在C0h到FDh之間,並且說明這個Unicode字元是使用了幾個Bytes來記錄的。所有在第一個Byte之後的資料,都一定落在80h到BFh之間,這樣的規則也使得自動化與重新修復文件的程序變得更簡單。

UTF-16則包含了以下重要的屬性: 它只使用16 bit的Word對從U+0000到U+d7ff這些字元進行編碼,而其餘的Unicode字元編碼,則會使用成對的16-bit Words。

最後,任何一個Unicode字元都可以用32-bit為單位的資料來表現,就稱為UTF-32.

如果您需要更多的資料,請參閱: Unicode FAQ - Basic questions, Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM, Wikipedia: UTF-8 [1]

Lazarus 元件庫架構須知

LCL包含了兩個部分:

  1. 跟編譯目標作業平台無關的部分,這部分是使用跟Delphi VCL相似的類別架構來實現的。
  2. "Interfaces" – 使用各編譯目標作業平台相關的API來實現的。

介於兩個部分之間的溝通,則是透過TWidgetset這個抽象類別來達成的,每個Widgetset的實現都是從TWidgetset這個父類別衍生而來的。

GTK 1的視覺元件是最舊的版本。在該版本的視覺元件當中,字元的編碼是以”LANG”這個環境變數來決定的,通常是” ISO-8859-n”這一類的單位元字串編碼。近幾年內建GTK 1的系統,已經開始把預設字元編碼設定為UTF-8了,例如2007年的Mandriva就是一例。在Lazarus的Gtk 1介面中,還少了支援UTF-8的鍵盤控制功能,這是個大問題,如果不解決的話,Lazarus將無法真正支援跨平台Unicode相容的目標。

Gtk2的視覺元件只能使用UTF-8編碼,並且完全支援UTF-8的各項要求。

Win32介面在預設時,使用的是ANSI編碼的視覺元件,目前已經開始進行修改,讓它能夠支援UTF-8,但因為還沒完成,所以預設是不使用UTF-8的,也因此目前在Win32介面的視覺元件上,暫時還無法使用Unicode。

Qt介面已經完成了支援UTF-8的準備,在Qt介面中,原生的字元編碼是使用UTF-16,但Lazarus的Qt介面會把UTF-8轉換為UTF-16,所以在支援上沒有問題。

Windows CE只支援UTF-16的字元編碼,但Lazarus的Windows CE介面則是在呼叫Windows API之前,先把ISO字元轉換為UTF-16編碼,所以這部分還算容易修改,就像目前我們把所有的字元轉換函式集中放在winceproc.pp裡面一樣。

如果您需要更多資訊,請參考: LCL內部資訊

讓win32介面相容於Unicode

編譯能使用Unicode的LCL-Win32函式庫

要使Windows版的LCL函式庫能使用Unicode,您得先到Lazarus的選單中"Tools" --> "Configure Build Lazarus"進行設定。

請在"Options"的欄位中加上” –dWindowsUnicodeSupport”這個設定值,並把所有的建置對象都選為NONE,只留下LCL的設定選項是Clean+Build,然後再設定win32為要建置的視覺元件(widgetset),按下”Build”按鍵,即可將LCL重新編譯為與Unicode相容的版本了。

完成後,您就可以把您已完成的應用程式重新編譯,編譯完成後,它們也都能夠支援Unicode了。

準則

首先,也是最重要的,所有為Win32介面進行的增補工作,都必須被包在 IFDEF WindowsUnicodeSupport這個編譯條件式裡面,以避免破壞了現有的ANSI介面,等到Unicode相容修正的專案穩定之後,這些IFDEF的編譯條件式就會一起被拿掉,而只留下Unicode相容的程式碼。在目前這個時間點上,所有現存使用ANSI標準編碼的程式,都還得由程式設計人員將之升級,才能與Unicode相容。

Windows平台 <= Win9x只支援ISO系列的字元編碼標準,並只有部分功能支援Unicode。Windows系列的平台,是從Windows NT跟Windows CE開始完全支援Unicode的(也同時還支援ISO系列編碼,算是雙軌並行),Win9x跟NT的API裡面,每一個功能都同時提供了兩個函式,一個是ANSI編碼的(函式名稱結尾都會加個A),另一個則是Unicode相容的(函式名稱結尾則是會加個W)。

  • W的函式中如果需要傳遞字串參數,接受的字串必須是WideString,例如UTF-16字串,而Windows CE只使用 *W的API。

在Windows 9x上面,Unicode系列函式的呈現

有些Unicode系列的API也會呈現在Windows 9x平台上,以下就是這些Unicode系列API的列表: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp

轉換範例:

  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
Length(ButtonCaption), TextSize);

要改為:

  {$ifdef WindowsUnicodeSupport}
    GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
  {$else}
    GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
  {$endif}

需要轉換ANSI跟Unicode版本的函式

第一個簡單的轉換範例:

function TGDIWindow.GetTitle: String;
var
 l: Integer;
begin
   l := Windows.GetWindowTextLength(Handle);
   SetLength(Result, l);
   Windows.GetWindowText(Handle, @Result[1], l);
end;

必須轉換為:

function TGDIWindow.GetTitle: String;
var
 l: Integer;
 AnsiBuffer: string;
 WideBuffer: WideString;
begin

{$ifdef WindowsUnicodeSupport}

 if UnicodeEnabledOS then
 begin
   l := Windows.GetWindowTextLengthW(Handle);
   SetLength(WideBuffer, l);
   l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
   SetLength(WideBuffer, l);
   Result := Utf8Encode(WideBuffer);
 end
 else
 begin
   l := Windows.GetWindowTextLength(Handle);
   SetLength(AnsiBuffer, l);
   l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
   SetLength(AnsiBuffer, l);
   Result := AnsiToUtf8(AnsiBuffer);
 end;

{$else}

   l := Windows.GetWindowTextLength(Handle);
   SetLength(Result, l);
   Windows.GetWindowText(Handle, @Result[1], l);

{$endif}

end;

Roadmap 未來發展計畫

在相容於Unicode的延伸計畫中,甚麼是必須要作的:

  • TForm, TButton, TLabel
  • 大多數的控制項
  • 選單元件
  • LCLIntf.ExtTextOut 以及大多數和字串相關的WinAPI
  • 以TStrings為基礎的控制項. 例如: TComboBox, TListBox等等
  • SynEdit 顯示與輸入UTF-8字元必須正確

要支援Unicode的話,目前已知的問題:

  • SynEdit不支援由右到左顯示的字元
  • 在編輯器上面雙擊非ANSI編碼的字元時,被雙擊的字不會被選取,反而是被雙擊的字左方的其他字會被選取起來
  • 複製貼上Unicode字元的時候,如果當時的視窗環境不是使用Unicode為編碼字元的話,複製貼上的動作會不正確
  • MessageBox上面的按鍵文字就算已經翻譯完成,也無法正確顯示Unicode字元。這已經在IDE上面測試過了,不過也可能單純的就只是IDE的問題而已。
  • 在Project option設定應用程式Title的時候,如果設定為Unicode的字串,顯示可能不正確。

在重新檢視程式碼之後,下列的問題需要進行測試,因為這些程式碼似乎無法相容於Unicode:

  • 在PrepareCreateWindow (Win32WSControls這個單元檔裡面) 的這行程式:
StrCaption := PChar(AWinControl.Caption);
  • (還有一些問題是沒有被確認的,如果經過確認的話,就會一道列在上述的清單裡面)

需要檢查的Unit檔的清單:

  • "win32callback.inc"
  • "win32def.pp"
  • "win32int.pp"
  • "win32lclintf.inc"
  • "win32lclintfh.inc"
  • "win32listsl.inc"
  • "win32listslh.inc"
  • "win32memostrings.inc"
  • "win32object.inc"
  • "win32proc.pp"
  • "win32winapi.inc"
  • "win32winapih.inc"
  • "win32wsactnlist.pp"
  • "win32wsarrow.pp"
  • "win32wsbuttons.pp"
  • "win32wscalendar.pp"
  • "win32wschecklst.pp"
  • "win32wsclistbox.pp"
  • "win32wscomctrls.pp"
  • "win32wscontrols.pp"
  • "win32wscustomlistview.inc"
  • "win32wsdbctrls.pp"
  • "win32wsdbgrids.pp"
  • "win32wsdialogs.pp"
  • "win32wsdirsel.pp" - Felipe
  • "win32wseditbtn.pp" - Felipe
  • "win32wsextctrls.pp" - Felipe
  • "win32wsextdlgs.pp" - Felipe
  • "win32wsfilectrl.pp" - Felipe
  • "win32wsforms.pp" - Felipe
  • "win32wsgrids.pp" - Felipe
  • "win32wsimglist.pp" - Felipe
  • "win32wsmaskedit.pp" - Felipe
  • "win32wsmenus.pp" - Felipe
  • "win32wspairsplitter.pp" - Felipe
  • "win32wsspin.pp" - Felipe
  • "win32wsstdctrls.pp" - Felipe
  • "win32wstoolwin.pp" - Felipe
  • "winext.pas" - Felipe

螢幕截圖

Lazarus Unicode Test.png

額外參考

  • UTF-8 - Description of UTF-8 strings