LCL Unicode Support/ja

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 Documentation日本語版 - 翻訳ノート - 日本語障害情報

イントロダクション

Lazarus は、バージョン0.9.25で、Gtk1 を除くすべてのプラットフォームにおいてユニコードに完全対応しました。In this page one can find instructions for Lazarus users, roadmaps, descriptions of basic concepts and implementation details.

ユーザー向けの解説

ウイジェットセットのユニコード対応でも、すべてがユニコードでない、ということを知っておくことが重要です。 FreePascal Runtime LibraryとFreePascal FCL LibraryはANSIが前提となっています。異なるエンコーディングのライブラリの間で期待される正しい変換をおこなうのは、開発者の責任です。

大抵、ライブラリ毎にエンコーディングが決められています。それぞれのライブラリは、1つのエンコーディング上での動作を期待され、それは、大抵はunicode(LazarusではUTF-8)か、Ansi(システムのエンコーディング)のいずれかです。FPC 2.2.2のRTLとFCLでは Ansi文字列です。FPC 2.3.xも今のところはAnsi文字列です。 (訳注:著者は、広い意味では、SJISはANSIのスーパーセットという考えかもしれないし、あるいは、SJISというエンコーディングがあるのを知らないのかもしれない。)

unicodeとansiの変換は、SystemユニットのUTF8ToAnsi関数とAnsiToUTF8関数、あるいは、FileUtilユニットのUTF8ToSys関数とSysToUTF8関数でおこなうことができます。 あとの2つはよりスマートですが、あなたのプログラムにいくつかのコードが必要になるかもしれません。

例:

TEditからの文字列をRTLのファイル関数の引数として与えたい、としましょう。:

<delphi> var

 MyString: string; // utf-8エンコード

begin

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

end; </delphi>

逆ならば、

<delphi> var

 MyString: string; // ansiエンコード

begin

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

end; </delphi>

重要: UTF8ToAnsi はUTF8文字列が正しくないキャラクタを含むとき、空の文字列を返します。

重要: AnsiToUTF8 と UTF8ToAnsi はLinux、BSD、Mac OS X環境では、widestringマネージャを必要とします。 (FileUtilユニットの)SysToUTF8 や UTF8ToSys 関数や、プログラムのusesセクションの最初のユニットにcwstringを加えることで、widestringマネージャを使うことができます。


ディレクトリやファイル名の扱い

Lazarusのコントロールや関数はファイル名とディレクトリ名であっても、utf-8エンコーディングとして取り扱いますが、RTLはansi文字列として扱います。

たとえば、TFileListBoxのDirectoryプロパティにカレントディレクトリをセットするボタンがあるとしましょう。 RTL関数 GetCurrentDir はansiエンコーディングの文字列を返します。unicodeではありませんので、変換が必要です。

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

 FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
 // または、FileUtilユニットの関数を利用できます。
 FileListBox1.Directory:=GetCurrentDirUTF8;

end; </delphi>

FileUtilユニットはUTF-8文字列による共通ファイル関数を定義しています。

<Delphi> // basic functions similar to the RTL but working with UTF-8 instead of the // system encoding

// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX // but normally these OS use UTF-8 as system encoding so the widestringmanager // is not needed. function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8 procedure SetNeedRTLAnsi(NewValue: boolean); function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager

// file operations function FileExistsUTF8(const Filename: string): boolean; function FileAgeUTF8(const FileName: string): Longint; function DirectoryExistsUTF8(const Directory: string): Boolean; function ExpandFileNameUTF8(const FileName: string): string; function ExpandUNCFileNameUTF8(const FileName: string): string; {$IFNDEF VER2_2_0} function ExtractShortPathNameUTF8(Const FileName : String) : String; {$ENDIF} function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint; function FindNextUTF8(var Rslt: TSearchRec): Longint; procedure FindCloseUTF8(var F: TSearchrec); function FileSetDateUTF8(const FileName: String; Age: Longint): Longint; function FileGetAttrUTF8(const FileName: String): Longint; function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint; function DeleteFileUTF8(const FileName: String): Boolean; function RenameFileUTF8(const OldName, NewName: String): Boolean; function FileSearchUTF8(const Name, DirList : String): String; function FileIsReadOnlyUTF8(const FileName: String): Boolean; function GetCurrentDirUTF8: String; function SetCurrentDirUTF8(const NewDir: String): Boolean; function CreateDirUTF8(const NewDir: String): Boolean; function RemoveDirUTF8(const Dir: String): Boolean; function ForceDirectoriesUTF8(const Dir: string): Boolean;

// environment function ParamStrUTF8(Param: Integer): string; function GetEnvironmentStringUTF8(Index : Integer): String; function GetEnvironmentVariableUTF8(const EnvVar: String): String; function GetAppConfigDirUTF8(Global: Boolean): string; </Delphi>

Mac OS X

FileUtilユニットのファイル関数はMac OS Xでは特別な配慮が必要です。 OS Xはファイル名を(1つのファイル名になるように)正規化します。 たとえば、'ä.txt'というファイル名をユニコードでエンコードすると(訳注 aのウムラウトの部分のみで)「#$C3#$A4 」と 「'a'#$CC#$88」の、2つの異なる順序でエンコードできます。 Linux とBSDでは両方のどちらのエンコードでも作ることができます。しかしながらOS Xでは自動的に3バイトのシーケンスで作ります。

これはすなわち、単にエンコードのバイト列の一致では判断できない、ということを意味しています。コード上では次の点に注意します。

<Delphi> if Filename1=Filename2 then ... // これは充分ではありません。 if AnsiCompareFileName(Filename1,Filename2)=0 then ... // cwstringを使わない環境で fpc 2.2.2では上手く動きません。 if CompareFilenames(Filename1,Filename2)=0 then ... // FileUtilやFileProcsを使う場合、これはいつでも上手く行きます。 </Delphi>

Windows環境での東アジア言語

Windows XPのユーザーインターフェース用のデフォルトフォント(Tahoma)は、アラビア語、ロシア語、西欧言語などのいくつかの言語で正しく表示がおこなえますが、中国、韓国、日本などの東アジア諸国の言語では正しく表示できません。

コントロールパネルで、地域の設定を選び、言語タブをクリックして、EastAsiaLanguageパックをインストールし、それらの言語の標準のユーザーインターフェースフォントを正しくつかいましょう。 明らかにWindowsXPではこれらの言語がすでにインストールされている言語パックに含まれています。より詳しい情報はこちらをどうぞ。

実装のガイドライン

要求事項

Lazarusの精神は、"Write once, Compile everywhere."です。 これは、理想的には、ユニコードを使うアプリケーションでは、ターゲットにむけた条件定義などがなく、1つのユニコードをサポートするソースコードで、すべてのターゲットのプログラムを生成できる、ということです。

LCLの"interface"の部分はターゲットプラットホームがユニコードをサポートするよう、ターゲットプラットホームのためのユニコードサポートを実装するべきです。そして、同時に、アプリケーションプログラマからは、プラットホームに依存している実装を隠蔽しなくてはなりません。

Lazarusに関して言えることは、アプリケーションコードとLCLの境界における内部的な文字列の通信は、LCLとWidgetsetsと同様に、古典的な(バイト並びの)stringで成り立っている、ということです。 論理的には、それらの保持には、UTF-8を使ってエンコードされているべきです。

Unicodeへの移行

LazarusではAnsiエンコーディングが、もっともよく利用されています。なぜなら、0.9.24までのGtk1とWin32のインターフェースでは、それがデフォルトでだからです。With 0.9.25 all widgetsets will use UTF-8 by default (with the exception of gtk1, which only supports UTF-8 when the system encoding is UTF-8 and it has some limitations). So all applications that pass strings directly to the interface (be written on code or on the object inspector) will need to be converted to utf-8. 現在、私達はエンコーディングに関して、いくつかのウイジェットセットのグループをもっています。

  • ANSIエンコーディングのインターフェース: gtk1(ANSI用)
  • UTF-8エンコーディングのインターフェース: gtk1(UTF-8用), gtk2, qt, fpGUI, carbon, win32, wince, そのほかすべて

gtk1 は、ANSIとUTF-8の両方のグループであることに注意してください。理由は、Gtk1では、環境変数でそれらを決めることができるからです。

The IDE was extended to load/save/edit files of different encodings (one encoding per file). It has a built in heuristic to determine the encoding and you can change the encoding of a file at any time (Source Editor / Popup Menu / File Settings/ Encoding). So the IDE can open old files and projects and can be used to convert the encoding.

ロードマップ

今、ロードマップを作成して、実行に移す時です。私達は、ガイドラインをもっています。 次のような実現可能な計画をたてました。2つのグループに仕事を分ける計画です。 1つ目は、優先的に主要な柱となる仕事を、2番目はそれ以外の仕事を計画しています。

私達が、Lazarusはユニコードが使えるよ、といえる前に、すべての主要な仕事は完全に実装されていなくてはなりません。そして、そのことが、私達の努力の最も向かう方向です。

2番目の仕事は、あったらいいな、というものです。しかし、誰かがそれらのために奉仕をしない限り、実装されることはないでしょう。

1番目に重要な仕事

Win32 Widgetset に UTF-8をサポートさせる

ノート:このステップでは、私達はすべての32ビットWindowsのバージョンを同時にターゲットとしています。この努力でつくられるすべてのコードは、この作業によって発生するバグを避けられるよう、現在のwin32インターフェースとIFDEFによって分離されるでしょう。 移行する時期になれば、IFDEFはユニコードだけが残るように除去されます。

状態:完全に実装されました。


Gtk 2 キーボード関数をUTF-8で動くように更新する

ノート:

状態:ほとんど完了しました。いくつかのgtk2上での編集前の機能がまだサポートされていません。しかし、どの言語がそれを必要とするのかは分かりません。(訳注:漢字入力のことかも?)

Lazarus IDE が正しくWin32 ウイジェットセットで動いてUTF-8をサポートするようにする

ノート:

状態:完了しました。文字コードマップで255キャラクタしか表示できないという制限がありますが、最近のOSの機能ではユニコードでの文字コードマップを提供しています。


Lazarus IDE が正しくGtk2 ウイジェットセットで動いてUTF-8をサポートするようにする

ノート: 状態:完了しました。 gtk2 intfにバグがありますが、utf-8とは関係がありません。

2番目に重要な仕事

Windows CE ウイジェットセットをUTF-8を使うように更新する


ノート:文字列の変換ルーチンは winceproc.ppに集約されています。多くのテストが必要です。

状態:完了しました。


Gtk 1 キーボード関数をUTF-8で動くように更新する

Notes:

状態:まだ実装されていない

syneditでRTL(右から左へ)の記法

Notes: RTLはたとえば、アラビア語のように右から左への記法を意味します。

状態:まだ実装されていない

ユニコードの要点

ユニコード標準は、文字を0から10FFFF(h)までの整数に割り当てています。それぞれの割り当ては、コードポイントと呼ばれています。いいかえると、ユニコード文字列は、原理的には、U+000000 から U+10FFFF(0から1 114 111)までのコードポイントで定義されます。

一意のバイト順になるように、現在のユニコードのコードポイントには、3つの枠組みがあります。 これらの枠組みには、UTF-8,UTF-16,UTF-32とよばれるユニコード変換形式(訳注:Unicode transformation formats=UTF)があります。 これらの間の相互変換は可能です。

基本となる要素を示します。

                             UTF-8 UTF-16 UTF-32
最小 code point [16進]    000000 000000 000000
最大 code point  [16進]   10FFFF 10FFFF 10FFFF
Code unit size [ビット数]      8     16     32
1文字の最小バイト数              1      2      4
1文字の最大バイト数              4      4      4

UTF-8 には、いくつかの重要で便利な特徴があります。 つまり、バイト順で解釈されるので、バイトのlo-やhi-オーダーのようなものは存在しません。

(訳注:バイトのオーダーについて、8ビットを超える複数のバイトで表現する場合、バイト順をどう表現/解釈するか、プロセッサによってことなっています。CPUやハードウエア系の用語ではビッグエンディアン、リトルエンディアンとも呼ばれます。インテルのx86系プロセッサは、リトルエンディアン、PowerPCやMIPS系、Cellプロセッサはビッグエンディアンです。また、両方のエンディアンを扱えるバイエンディアンというプロセッサも存在します。ネットワーク上はビッグエンディアンが主流なため、インテルはバイトオーダーを高速にあわせるため、486以降に、BSWAP命令が追加されました。ソフトウエアでは、自由にメーカーが決めることができるため、どちらのフォーマットも存在しますが、Windowsで最初作られたソフトはリトルエンディアン、Macで最初作られたソフトはビッグエンディアンが多いようです。しかし、ユニコードのテキストファイルなどは、これらの区別をするためにBOMが設定されることがあります。)

Unicode文字のU+0000から+007F(ASCII)は、単に00hから7Fhになり、ASCIIと互換性があります。 これはすなわち、7bitのASCII文字セットしか含まないファイルや文字はASCIいとUTF-8は同じである、ということを意味します。 U+007F以上の文字は、複数のバイトの並びでエンコードされ、それぞれには、2つの最も意味のあるビットセットを持っています。(訳注:most significant bitは通常はMSB(最上位ビット)と略されます)

1文字のバイト列には、その中に他の文字の、より長いバイト列を含んでいます。 このことは、部分文字列の検索を容易にします。非アスキー文字をあらわすマルチバイトの最初のバイトは、必ずC0hからFDhの範囲にあり、これは、この文字のために、どれくらいのバイト長さがあるのか示しています。マルチバイト列における、全ての続くバイトは、80hからBFhにあります。このおかげで、再構成をしたり、堅牢なプログラムを作ることができます。

(訳注: 上記の訳の説明ではよく分からないと思いますが、デバッグの際などに知っておいたほうがいいと思うので説明すると、 utf-8形式のユニコード文字列のバイト表現は、

  • 最初のバイトが(00-7f)hは1バイト、
  • 最初のバイトが(c0-df)hの場合は2バイト、
  • 最初のバイトが(e0-ef)hの場合は3バイト、
  • 最初のバイトが(f0-f7)hの場合は4バイトで構成されています。

2文字目以降のバイトは、常に(80-bf)hであって、 JISやEUCに比べると判定などがしやすい、というを上の文章が言っている(再構成や、堅牢)のだろうと思います。

ちなみに、漢字の表現には3バイト、新拡張JISコード文字には、4バイトが必要。

日本語のテキストファイルはS-JISの1.5倍のサイズになってしまいますが、ソースコードなどは、ほとんど英文字が多いので、ファイルサイズはそれほど増えないでしょう。

訳注ここまで )


UTF-16は次のような特徴があります。: UTF-16はU+0000からU+d7ffまでの文字は1つの16bitワードを使い、その他のユニコード文字は2つの16ビットワードを使います。

最後に、UTF-32では、すべてのユニコード文字は1つの32ビットとして表現できます。

更なる情報は、こちらをみてください: Unicode FAQ - Basic questions, Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM, Wikipedia: UTF-8 [1]

Lazarusコンポーネントライブラリのアーキテクチャの要点

LCL(Lazarusコンポーネントライブラリ)は2つの部分からできています。

  1. DelphiのVCLのように、クラス階層を実装していて、ターゲットプラットホームに依存しない部分 
  2. "Interfaces" つまり、それぞれのターゲットのAPIをインターフェースするために実装している部分。

2つの部分の橋渡しの仕組みは、抽象クラスである TWidgetsetにあります。 それぞれのwidgetsetは、TWidgetsetから派生したクラスの中で実装されています。

Gtk1 widgetsetが最も古いものです。このwidgetsetにおいていは文字列のエンコードはLANG環境変数によって決定されます。このエンコードは、通常はisoのシングルバイトであるISO-8859-nが使われています。最近の多くのディストリビューション(たとえば、Mandriva 2007など)は、Gtk1をUTF-8として設定して出荷されています。ところが、LazarusのGtk1インターフェースは、キーボードの扱いでUTF-8を適切に扱っていません。この問題が最近大きくなり、Lazarusに対するクロスプラットホームでのユニコードサポートの要望が日増しにつよくなっています。

Gtk2 widgetsetはUTF-8エンコードでのみ動作し、UTF-8 に完全対応しています。

Lazarus 0.9.28 Windows と Windows CE インターフェースはユニコードに完全対応しています。

Qtインターフェースは、UTF-8として準備されています。Qtそのものは、UTF-16をネイティブなエンコードとして採用しています。LazarusのQtインターフェースはUTF-8をUTF-16に変換しています。

こちらも参照してください。: Internals of the LCL

win32インターフェースでユニコードを有効にする

方針

まず最初に、最も重要なこととして、Win32インターフェースのすべてのユニコードパッチは、これまでのANSIインターフェースを壊してしまわないように、IFDEF WindowsUnicodeSupportによって囲まれていなければなりません。これらの作業が安定してきてから、すべてのifdefが取り除かれて、ユニコード部分だけが残るようになります。この時点で、すべての既存のANSI文字を使うプログラムが、Unicodeへ移行できるようになるでしょう。

Win9xでのWindowsプラットホームは、ISOコードページ標準でなりたっており、Unicodeは部分的なサポートしかありません。WinNTとWindowsCEを起源とするWindowsプラットホームは、完全にUnicodeをサポートしています。Win9xとNTには、古いANSI文字を引数にとる*Aという関数と、Unicodeワイド文字列(つまり、UTF-16でエンコードされた文字列)を引数にとる*Wという関数の、2つの同じ機能をもったAPI関数を提供しています。Windows CEでは、ワイドAPI関数しか使いません。

Windows 9xに存在するワイド文字列関数

いくつかのWide系API関数はWindows9xにも存在します。ここに、そのような関数のリストを示します。: 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とWideのバージョンが必要な関数

最初の変換例:

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;

ロードマップ

Unicodeに対応し、動作しているもの

  • TForm, TButton, TLabel
  • Most controls
  • Menus
  • LCLIntf.ExtTextOut and most other text related winapis
  • TStrings based controls. Examples: TComboBox, TListBox, etc
  • SynEdit shows and can input UTF-8 characters correctly
  • Setting/Getting unicode strings to/from the ClipBoard
  • Setting the Application Title in the project options to (for example) 'Minha Aplicação'.
  • Double clicking words with non-ascii chars in the editor to select them

Unicode化における既知の問題

  • SynEdit does not support RTL (right to left)
  • Is OpenFileDialogCallBack tested with selection large numbers of files?
    • Is this problem unicode specific? I think it's a generic problem. --Sekelsenmat 13:40, 14 February 2008 (CET)
      • Maybe. I know I tested it with large number of files before the Unicode version was added. If it is a generic problem, then the the non-Unicode version got broken, when the Unicode version was added. Vincent 21:45, 15 February 2008 (CET)
  • class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.

Unicode化で起こりうる問題

Based on a code review, the following needs to be tested, because the code doesn't seem to be Unicode aware:

  • class procedure TWin32WSCustomComboBox.SetText
  • TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware
  • TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
  • TWin32WidgetSet.TextOut: Is Windows.TextOut supported on windows 9X?
  • MessageBox buttons don't show unicode correctly when they are translated. Tested on the IDE. Could be a problem on the IDE however.
    • Note: I couldn't reproduce using the portuguese translation --Sekelsenmat 22:20, 12 January 2008 (CET)
  • (list of unconfirmed problems, if confirmed can be moved to the list above)

スクリーンショット

Lazarus Unicode Test.png

参考

  • UTF-8 - UTF-8文字列についての記述