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は開発においてUnicode標準をもっとサポートする必要があります。ほとんどの場合、Windows環境においてです。 ここに、Lazarusでユニコードサポートをもっときちんとやりたい人のために、いくつかの基本的な情報を示します。このページは積極的に修正、拡張、変更していってください。

すでにユニコード標準や、DelphiでのWideStringの経験がある人にとって、Unicodeをサポートするプログラムを書く際に、このページの情報は助けになるでしょう。 以前の非ラテン言語(西方の)での利用方法や、多くの種類のキャラクタセットにとっても、また、助けになるでしょう。

注意:細かい実装については、いま議論の最中ですので、この文書の内容は変わる可能性があります。

実装のガイドライン

要求事項

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

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

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

Unicodeへの移行

LazarusではAnsiエンコーディングが、もっともよく利用されています。なぜなら、今日のGtk1とWin32のインターフェースでは、それがデフォルトでだからです。近い将来、すべてのwidgetsetはUTF-8をサポートするようになり、全てのアプリケーションはutf-8でかかれ、オブジェクトインスペクタはutf-8で動作し、utf-8に変換された文字列をインターフェースに直接渡せるようになるでしょう。

Gtk 2やQt,WinCE(将来的にはWin32U)などのように、人々が完全に動かないウイジェットセット向けのソフトウエアを開発するとき、彼らはGtkやwin32のようなもっと安定したウイジェットセットでコンパイルされたIDEを使います。 iso文字列をutf-8のためのウジェットセットに渡すような、こういった矛盾を解決するためには、ターゲットウイジェットと同じエンコーディング上で動作するIDEを使うことが必要です。これはつまり、私達はUnicodeに移行するために安定したUTF-8のIDEを必要としています。

現在、私達はエンコーディングに関して、いくつかのウイジェットセットのグループをもっています。

  • ANSIエンコーディングのインターフェース: win32 , gtk (1)
  • UTF-8エンコーディングのインターフェース: gtk (1), gtk2, qt, fpGUI
  • 現在はANSIエンコーディングだが、将来的にはUTF-8への移行が必要なインターフェース: win32, wince


gtk 1 は、ANSIとUTF-8の両方のグループであることに注意してください。理由は、Gtk1では、環境変数でそれらを決めることができるからです。 最新のLazarusでは、Win32,WinCE,gtk向けにコンパイルされた既存のソフトウエアは良く動きます。しかし、他のwidgetsetでは、エンコーディングの問題に直面します。

そして、UTF-8を使う新しいソフトウエアでは、Unicodeグループのウイジェットセットを使ってコンパイルされたものに限って動くでしょう。

重要な点は、ターゲットと同じグループでコンパイルされているIDEを使うということです。なぜなら、IDEはウイジェットセットのエンコーディングを使ってコンパイルされているからです。そして、それはターゲット向けのエンコーディングをおこなうLFMやLRSファイルを生成することができません。

ロードマップ

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

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

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

優先的に主要な仕事

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

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

どのようにWin9xのユニコードをサポートするかの詳細は、まだ討論中です。

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


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

ノート:

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


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

ノート:

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


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

ノート:

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


2番目の(その他の)仕事

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


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

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


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

Notes:

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

ユニコードの要点

ユニコード標準は、文字を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 には、いkつかの重要で便利な特徴があります。 つまり、バイト順で解釈されるので、バイトのlo-やhi-オーダーのようなものは存在しません。 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にあります。このおかげで、再構成をしたり、堅牢なプログラムを作ることができます。

(訳注: 上記の訳の説明ではよく分からないと思いますが、デバッグの際などに知っておいたほうがいいでしょう。

ユニコードは、 最初のバイトが(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は次のような特徴があります。: It uses a single 16-bit word to encode characters from U+0000 to U+d7ff, and a pair of 16-bit words to encode any of the remaining Unicode characters.

Finally, any Unicode character can be represented as a single 32-bit unit in UTF-32.

For more, see: 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エンコードでのみ動きます。しかし、インターフェースのキーボードコードはいまだ昔のGtk1のコードをベースとしていて、UTF-8を完全にはサポートしていません。

Win32インターフェースは、ANSI widgetsとして設定されていますので、現在はwin32ではUnicodeを使うことは不可能です。

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

Windows CEのみが、文字列のエンコードとしてUCS-2をサポートしています。しかし、Lazarusの現在のWindowsCEインターフェースでは、WindowsAPIに渡す前に、ISOからUCS-2に変換しています。 これは、すべての変換するコードはwinceproc.ppファイルのいくつかの関数に集中しているので、簡単に修正できます。

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

win32インターフェースでUnicodeを有効にする

方針

まず最初に、最も重要なこととして、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);

これは、次のようにします。

var
  WideCaption: WideString;

  ....

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

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;

ロードマップ

下記は、チェックすべきユニットのリストです。

  • "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"
  • "win32wseditbtn.pp"
  • "win32wsextctrls.pp"
  • "win32wsextdlgs.pp"
  • "win32wsfilectrl.pp"
  • "win32wsforms.pp"
  • "win32wsgrids.pp"
  • "win32wsimglist.pp"
  • "win32wsmaskedit.pp"
  • "win32wsmenus.pp"
  • "win32wspairsplitter.pp"
  • "win32wsspin.pp"
  • "win32wsstdctrls.pp"
  • "win32wstoolwin.pp"
  • "winext.pas"