LCL Unicode Support/zh CN

From Free Pascal wiki
Revision as of 06:52, 13 May 2008 by Pengtu (talk | contribs) (LCL组件的Unicode支持)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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编码的字元集来撰写脚本语言,也会有些帮助的。

请注意: 实作的细节部分目前还在讨论中,这部分的文件随时都有可能更新。

本文初版由User:Dennies翻译正体中文页面转换而来。
'元件'一词英文为:component,简体界面中译为'组件'。

程式实作规范

需求

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