LCL Unicode Support/zh CN

From Lazarus wiki
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,简体界面中译为'组件'。

Instructions for users 操作指南

In unicode widgetsets it's important to note that not everything is unicode. The Free Pascal Runtime Library, the Free Pascal FCL library and the Fileutils unit all are ansi. It's the responsability of the developer to know what is the encoding of their strings and do the proper conversion between libraries which expect different encodings.

Usually the encoding is per-library. Each library will uniformally expect 1 kind of encoding, which will usually either be unicode (UTF-8 for Lazarus) or Ansi (which means the system encoding, and may be utf-8 or not). The fileutils unit from the LCL is an exception because it's destined in the future to be added to the fcl or the rtl. The RTL and the FCL expect ansi strings.

You can can convert between unicode and ansi using the UTF8ToAnsi and AnsiToUTF8 functions from the System unit.

Examples:

Say you get a string from a TEdit and you want to give it to some rtl file routine:

<delphi> var

 MyString: string; // utf-8 encoded

begin

 MyString := MyTEdit.Text;
 SomeRTLRoutine(UTF8ToAnsi(MyString));

end; </delphi>

And for the opposite direction:

<delphi> var

 MyString: string; // ansi encoded

begin

 MyString := SomeRTLRoutine;
 MyTEdit.Text := AnsiToUTF8(MyString);

end; </delphi>

Dealing with directory and filenames 应付文件夹和文件名

Lazarus controls which expect filenames and directory names use utf-8 strings, but the RTL uses ansi strings for directories and filenames.

For example, consider a button, which sets the Directory property of the TFileListBox to the current directory. The RTL Function GetCurrentDir is ansi, and not unicode, so conversion is needed:

<delphi> procedure TForm1.Button1Click(Sender: TObject); begin

 FileListBox1.Directory:=AnsiToUTF8(GetCurrentDir);

end; </delphi>

程式实作规范

需求

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