Sign in Join
Wiki copytemporary

LCL Unicode Support/ja: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## イントロダクション
22
33Lazarus は、バージョン0.9.25で、Gtk1 を除くすべてのプラットフォームにおいてユニコードに完全対応しました。 このページは、FPC 2.6.4 を使用している Lazarus バージョン1.4 までについての内容です。
44
55Lazarus は バージョン2.0 から、FPC 3.0+ の機能を用いたユニコードへの対応の改善がなされています。 その詳細は、右のリンク先を見てください。[[Unicode Support in Lazarus/ja]]
66
77## ユーザー向けの解説
88
99Lazurus がユニコードに対応したウイジェットセットを持つといっても、すべてがユニコードに対応しているわけではないことに注意して下さい。 開発者は、使用する文字列のエンコーディングを知り、異なるエンコーディングを用いるライブラリに対しても正しい変換を行う必要があります。
1010
1111エンコーディングは普通ライブラリ(例えば、DLL や Lazarus のパッケージなど)ごとに定まっています。 各ライブラリは、1種類のエンコーディングに対応しており、大抵は unicode ( Lazarus では UTF-8 ) か、 ANSI (正確にはシステムのエンコーディングであり、 UTF-8 の可能性もあります) のいずれかです。 FPC のバージョンが2.6より古い場合、 RTL と FCL は ANSI エンコーディングが前提になっています。
1212
1313Unicode と ANSI 間の変換は、以下の関数でできます。
1414
1515- FPC の System ユニットの**UTF8ToAnsi**関数 と **AnsiToUTF8** 関数
1616- Lazarus の FileUtil ユニットの**UTF8ToSys**関数と**SysToUTF8** 関数
1717
1818後者がよりスマート(早い)ですが、プログラムにいくつかのコードを書く必要があるかもしれません。
1919
2020### FPC は ユニコードis not Unicode aware
2121
2222Free Pascal Runtime Library (RTL) と Free Pascal Free Component Library (FCL) in current FPC versions (\<= 2.6.x) are ANSI, so you will need to convert strings coming from Unicode libraries or going to Unicode libraries (e.g. the LCL).
2323
2424There are significant improvements to development branches of FPC 2.7.1 with regard to Strings. See RawByteString and UTF8String in [[FPC Unicode support]].
2525
2626### ANSI と ユニコード間の変換
2727
2828> **Note:** AnsiToUTF8 と UTF8ToAnsi は Linux, BSD そして Mac OS X においてはrequire a widestring managerを必要とします。 代わりに、 SysToUTF8 と UTF8ToSys 関数(FileUtil ユニットの) 、または add the widestring manager by adding cwstring as one of the first units to your program's uses section.
2929
3030例:
3131
3232TEdit からの文字列を RTL のファイル関数の引数として与えたい、としましょう。:
3333
3434``` pascal
3535var
3636 MyString: string; // utf-8 エンコード
3737begin
3838 MyString := MyTEdit.Text;
3939 SomeRTLRoutine(UTF8ToAnsi(MyString));
4040end;
4141```
4242
4343逆ならば、
4444
4545``` pascal
4646var
4747 MyString: string; // ANSI エンコード
4848begin
4949 MyString := SomeRTLRoutine;
5050 MyTEdit.Text := AnsiToUTF8(MyString);
5151end;
5252```
5353
5454**重要**: UTF8ToAnsi はUTF8文字列が正しくないキャラクタを含むとき、空の文字列を返します。
5555
5656**重要**: AnsiToUTF8 と UTF8ToAnsi はLinux、BSD、Mac OS X環境では、widestringマネージャを必要とします。 (FileUtilユニットの)SysToUTF8 や UTF8ToSys 関数や、プログラムのusesセクションの最初のユニットにcwstringを加えることで、widestringマネージャを使うことができます。
5757
5858### Dealing with UTF8 strings and characters
5959
6060If you want to iterate over the characters of an UTF8 string, there are basically two ways.
6161
6262- iterate over the bytes - useful for searching a substring or when looking only at the ASCII characters of the UTF8 string. For example when parsing xml files.
6363- iterate over the characters - useful for graphical components like synedit. For example when you want to know the third printed character on the screen.
6464
6565#### Searching a substring
6666
6767Due to the special nature of UTF8 you can simply use the normal string functions:
6868
6969``` pascal
7070procedure Where(SearchFor, aText: string);
7171var
7272 BytePos: LongInt;
7373 CharacterPos: LongInt;
7474begin
7575 BytePos:=Pos(SearchFor,aText);
7676 CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
7777 writeln('The substring "',SearchFor,'" is in the text "',aText,'"',
7878 ' at byte position ',BytePos,' and at character position ',CharacterPos);
7979end;
8080```
8181
8282#### Accessing an UTF8 string as array
8383
8484An array consists of elements with the same size. An UTF8 character can have 1 to 4 bytes. So you have to convert the UTF8 string to either an array of unicode values (longwords) or to an array of PChar with a pointer to each character.
8585
8686#### Iterating over characters using UTF8CharacterToUnicode
8787
8888The following demonstrates how to iterate the 32bit unicode value of each character in an UTF8 string:
8989
9090``` pascal
9191uses LCLProc;
9292...
9393procedure IterateUTF8Characters(const AnUTF8String: string);
9494var
9595 p: PChar;
9696 unicode: Cardinal;
9797 CharLen: integer;
9898begin
9999 p:=PChar(AnUTF8String);
100100 repeat
101101 unicode:=UTF8CharacterToUnicode(p,CharLen);
102102 writeln('Unicode=',unicode);
103103 inc(p,CharLen);
104104 until (CharLen=0) or (unicode=0);
105105end;
106106```
107107
108108### ディレクトリやファイル名の扱い
109109
110110Lazarus のコントロールや関数はファイル名とディレクトリ名であっても、 utf-8 エンコーディングとして取り扱いますが、 RTL は ANSI 文字列として扱います。
111111
112112たとえば、 TFileListBox の Directory プロパティにカレントディレクトリをセットするボタンがあるとしましょう。 RTL の関数 [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] は ANSI エンコーディングの文字列を返します。 ユニコードではないので、変換が必要です。
113113
114114``` pascal
115115procedure TForm1.Button1Click(Sender: TObject);
116116begin
117117 FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
118118 // または、FileUtilユニットの関数を利用できます。
119119 FileListBox1.Directory:=GetCurrentDirUTF8;
120120end;
121121```
122122
123123FileUtil ユニットは UTF-8 文字列による共通ファイル関数を定義しています。
124124
125125``` pascal
126126// basic functions similar to the RTL but working with UTF-8 instead of the
127127// system encoding
128128
129129// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX
130130// but normally these OS use UTF-8 as system encoding so the widestringmanager
131131// is not needed.
132132function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
133133procedure SetNeedRTLAnsi(NewValue: boolean);
134134function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager
135135function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager
136136
137137// file operations
138138function FileExistsUTF8(const Filename: string): boolean;
139139function FileAgeUTF8(const FileName: string): Longint;
140140function DirectoryExistsUTF8(const Directory: string): Boolean;
141141function ExpandFileNameUTF8(const FileName: string): string;
142142function ExpandUNCFileNameUTF8(const FileName: string): string;
143143{$IFNDEF VER2_2_0}
144144function ExtractShortPathNameUTF8(Const FileName : String) : String;
145145{$ENDIF}
146146function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
147147function FindNextUTF8(var Rslt: TSearchRec): Longint;
148148procedure FindCloseUTF8(var F: TSearchrec);
149149function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
150150function FileGetAttrUTF8(const FileName: String): Longint;
151151function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
152152function DeleteFileUTF8(const FileName: String): Boolean;
153153function RenameFileUTF8(const OldName, NewName: String): Boolean;
154154function FileSearchUTF8(const Name, DirList : String): String;
155155function FileIsReadOnlyUTF8(const FileName: String): Boolean;
156156function GetCurrentDirUTF8: String;
157157function SetCurrentDirUTF8(const NewDir: String): Boolean;
158158function CreateDirUTF8(const NewDir: String): Boolean;
159159function RemoveDirUTF8(const Dir: String): Boolean;
160160function ForceDirectoriesUTF8(const Dir: string): Boolean;
161161
162162// environment
163163function ParamStrUTF8(Param: Integer): string;
164164function GetEnvironmentStringUTF8(Index : Integer): String;
165165function GetEnvironmentVariableUTF8(const EnvVar: String): String;
166166function GetAppConfigDirUTF8(Global: Boolean): string;
167167```
168168
169169#### Mac OS X
170170
171171FileUtilユニットのファイル関数はMac OS Xでは特別な配慮が必要です。 OS Xはファイル名を(1つのファイル名になるように)正規化します。 たとえば、'ä.txt'というファイル名をユニコードでエンコードすると(訳注 aのウムラウトの部分のみで)「#\$C3#\$A4 」と 「'a'#\$CC#\$88」の、2つの異なる順序でエンコードできます。 Linux とBSDでは両方のどちらのエンコードでも作ることができます。しかしながらOS Xでは自動的に3バイトのシーケンスで作ります。
172172
173173これはすなわち、単にエンコードのバイト列の一致では判断できない、ということを意味しています。コード上では次の点に注意します。
174174
175175``` pascal
176176if Filename1=Filename2 then ... // これは充分ではありません。
177177if AnsiCompareFileName(Filename1,Filename2)=0 then ... // cwstringを使わない環境で fpc 2.2.2では上手く動きません。
178178if CompareFilenames(Filename1,Filename2)=0 then ... // FileUtilやFileProcsを使う場合、これはいつでも上手く行きます。
179179```
180180
181181## UTF8 and source files - the missing BOM
182182
183183When you create source files with Lazarus and type some non ascii characters the file is saved in UTF8. It does **not use BOM** (Byte Order Mark). You can change the encoding via right click on source editor / File Settings / Encoding. The reason for the lacking BOM is how FPC treats Ansistrings. For compatibility the LCL uses Ansistrings and for portability the LCL uses UTF8.
184184
185185Note: Some MS Windows text editors might treat the files as system codepage and show them as invalid characters. Do not add the BOM. If you add the BOM you have to change all string assignments.
186186
187187For example:
188188
189189``` pascal
190190Button1.Caption := 'Über';
191191```
192192
193193When no BOM is given (and no codepage parameter was passed) the compiler treats the string as system encoding and copies each byte unconverted to the string. This is how the LCL expects strings.
194194
195195``` pascal
196196// source file saved as UTF without BOM
197197if FileExists('Über.txt') then ; // wrong, because FileExists expects system encoding
198198if FileExistsUTF8('Über.txt') then ; // correct
199199```
200200
201201## Widestrings and Ansistrings
202202
203203When passing Ansistrings to Widestrings you have to convert the encoding.
204204
205205``` pascal
206206var
207207 w: widestring;
208208begin
209209 w:='Über'; // wrong, because FPC will convert system codepage to UTF16
210210 w:=UTF8ToUTF16('Über'); // correct
211211 Button1.Caption:=UTF16ToUTF8(w);
212212end;
213213```
214214
215215## Source file with UTF8 BOM
216216
217217If you do a lot of widestring conversions in a unit, the code might become more readable saving the source as UTF8 with BOM. The compiler allows one encoding per unit.
218218
219219### Windows環境での東アジア言語
220220
221221WindowsXP のユーザーインターフェース用のデフォルトフォント(Tahoma)は、アラビア語、ロシア語、西欧言語などのいくつかの言語で正しく表示がおこなえますが、中国、韓国、日本などの東アジア諸国の言語では正しく表示できません。
222222
223223コントロールパネルで、地域の設定を選び、言語タブをクリックして、EastAsiaLanguageパックをインストールし、それらの言語の標準のユーザーインターフェースフォントを正しくつかいましょう。 明らかにWindowsXPではこれらの言語がすでにインストールされている言語パックに含まれています。より詳しい情報は[こちら](http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html)をどうぞ。
224224
225225## 実装のガイドライン
226226
227227### 要求事項
228228
229229Lazarusの精神は、"Write once, Compile everywhere."です。 これは、理想的には、ユニコードを使うアプリケーションでは、ターゲットにむけた条件定義などがなく、1つのユニコードをサポートするソースコードで、すべてのターゲットのプログラムを生成できる、ということです。
230230
231231LCLの"interface"の部分はターゲットプラットホームがユニコードをサポートするよう、ターゲットプラットホームのためのユニコードサポートを実装するべきです。そして、同時に、アプリケーションプログラマからは、プラットホームに依存している実装を隠蔽しなくてはなりません。
232232
233233Lazarusに関して言えることは、アプリケーションコードとLCLの境界における内部的な文字列の通信は、LCLとWidgetsetsと同様に、古典的な(バイト並びの)stringで成り立っている、ということです。 論理的には、それらの保持には、UTF-8を使ってエンコードされているべきです。
234234
235235### Unicodeへの移行
236236
237237LazarusではAnsiエンコーディングが、もっともよく利用されています。なぜなら、0.9.24までのGtk1とWin32のインターフェースでは、それがデフォルトだからです。With 0.9.25では全てのウィジェットセットがデフォルトで UTF-8 を利用します。gtk1 は例外で、UTF-8 をサポートするのはシステムの文字コードが UTF-8 である場合に限られ、しかも制限があります。そこで、インターフェースに直接文字列を送る(プログラムコードの中であれオブジェクトインスペクタの中であれ)タイプのアプリケーションでは、UTF-8への変換が必要になります。現在、私達はエンコーディングに関して、いくつかのウイジェットセットのグループをもっています。
238238
239239- ANSIエンコーディングのインターフェース: gtk1(ANSI用)
240240- UTF-8エンコーディングのインターフェース: gtk1(UTF-8用), gtk2, qt, fpGUI, carbon, win32, wince, そのほかすべて
241241
242242gtk1 は、ANSIとUTF-8の両方のグループであることに注意してください。理由は、Gtk1では、環境変数でそれらを決めることができるからです。
243243
244244IDEは複数のエンコーディングを用いたファイルの読み込み、保存、編集を行えるよう拡張されました(ファイル毎にエンコーディングが決まります)。ファイルがどのエンコーディングによっているかを自動判定するルーチンが内蔵されており、ユーザがエンコーディングを変更することも常時可能です(Source Editor / Popup Menu / File Settings/ Encoding)。よって、古いファイルやプロジェクトをIDEで開き、エンコーディングを変更して保存することも可能です。
245245
246246## ロードマップ
247247
248248今、ロードマップを作成して、実行に移す時です。私達は、ガイドラインをもっています。 次のような実現可能な計画をたてました。2つのグループに仕事を分ける計画です。 1つ目は、優先的に主要な柱となる仕事を、2番目はそれ以外の仕事を計画しています。
249249
250250私達が、Lazarusはユニコードが使えるよ、といえる前に、すべての主要な仕事は完全に実装されていなくてはなりません。そして、そのことが、私達の努力の最も向かう方向です。
251251
2522522番目の仕事は、あったらいいな、というものです。しかし、誰かがそれらのために奉仕をしない限り、実装されることはないでしょう。
253253
254254### 1番目に重要な仕事
255255
256256**Win32 Widgetset に UTF-8をサポートさせる**
257257
258258ノート:このステップでは、私達はすべての32ビットWindowsのバージョンを同時にターゲットとしています。この努力でつくられるすべてのコードは、この作業によって発生するバグを避けられるよう、現在のwin32インターフェースとIFDEFによって分離されるでしょう。 移行する時期になれば、IFDEFはユニコードだけが残るように除去されます。
259259
260260状態:完全に実装されました。
261261
262262**Gtk 2 キーボード関数をUTF-8で動くように更新する**
263263
264264ノート:
265265
266266状態:ほとんど完了しました。いくつかのgtk2上での編集前の機能がまだサポートされていません。しかし、どの言語がそれを必要とするのかは分かりません。(訳注:漢字入力のことかも?)
267267
268268**Lazarus IDE が正しくWin32 ウイジェットセットで動いてUTF-8をサポートするようにする**
269269
270270ノート:
271271
272272状態:完了しました。文字コードマップで255キャラクタしか表示できないという制限がありますが、最近のOSの機能ではユニコードでの文字コードマップを提供しています。
273273
274274**Lazarus IDE が正しくGtk2 ウイジェットセットで動いてUTF-8をサポートするようにする**
275275
276276ノート: 状態:完了しました。 gtk2 intfにバグがありますが、utf-8とは関係がありません。
277277
278278### 2番目に重要な仕事
279279
280280**Windows CE ウイジェットセットをUTF-8を使うように更新する**
281281
282282ノート:文字列の変換ルーチンは winceproc.ppに集約されています。多くのテストが必要です。
283283
284284状態:完了しました。
285285
286286**Gtk 1 キーボード関数をUTF-8で動くように更新する**
287287
288288Notes:
289289
290290状態:まだ実装されていない
291291
292292**syneditでRTL(右から左へ)の記法**
293293
294294Notes: RTLはたとえば、アラビア語のように右から左への記法を意味します。
295295
296296状態:まだ実装されていない
297297
298298## ユニコードの要点
299299
300300ユニコード標準は、文字を0から10FFFF(h)までの整数に割り当てています。それぞれの割り当ては、コードポイントと呼ばれています。いいかえると、ユニコード文字列は、原理的には、U+000000 から U+10FFFF(0から1 114 111)までのコードポイントで定義されます。
301301
302302一意のバイト順になるように、現在のユニコードのコードポイントには、3つの枠組みがあります。 これらの枠組みには、UTF-8,UTF-16,UTF-32とよばれるユニコード変換形式(訳注:Unicode transformation formats=UTF)があります。 これらの間の相互変換は可能です。
303303
304304基本となる要素を示します。
305305
306306`                             UTF-8 UTF-16 UTF-32`\
307307`最小 code point [16進]    000000 000000 000000`\
308308`最大 code point  [16進]   10FFFF 10FFFF 10FFFF`\
309309`Code unit size [ビット数]      8     16     32`\
310310`1文字の最小バイト数              1      2      4`\
311311`1文字の最大バイト数              4      4      4`
312312
313313**UTF-8** には、いくつかの重要で便利な特徴があります。 つまり、バイト順で解釈されるので、バイトのlo-やhi-オーダーのようなものは存在しません。
314314
315315(訳注:バイトのオーダーについて、8ビットを超える複数のバイトで表現する場合、バイト順をどう表現/解釈するか、プロセッサによってことなっています。CPUやハードウエア系の用語ではビッグエンディアン、リトルエンディアンとも呼ばれます。インテルのx86系プロセッサは、リトルエンディアン、PowerPCやMIPS系、Cellプロセッサはビッグエンディアンです。また、両方のエンディアンを扱えるバイエンディアンというプロセッサも存在します。ネットワーク上はビッグエンディアンが主流なため、インテルはバイトオーダーを高速にあわせるため、486以降に、BSWAP命令が追加されました。ソフトウエアでは、自由にメーカーが決めることができるため、どちらのフォーマットも存在しますが、Windowsで最初作られたソフトはリトルエンディアン、Macで最初作られたソフトはビッグエンディアンが多いようです。しかし、ユニコードのテキストファイルなどは、これらの区別をするためにBOMが設定されることがあります。)
316316
317317Unicode文字のU+0000から+007F(ASCII)は、単に00hから7Fhになり、ASCIIと互換性があります。 これはすなわち、7bitのASCII文字セットしか含まないファイルや文字はASCIいとUTF-8は同じである、ということを意味します。 U+007F以上の文字は、複数のバイトの並びでエンコードされ、それぞれには、2つの最も意味のあるビットセットを持っています。(訳注:most significant bitは通常はMSB(最上位ビット)と略されます)
318318
3193191文字のバイト列には、その中に他の文字の、より長いバイト列を含んでいます。 このことは、部分文字列の検索を容易にします。非アスキー文字をあらわすマルチバイトの最初のバイトは、必ずC0hからFDhの範囲にあり、これは、この文字のために、どれくらいのバイト長さがあるのか示しています。マルチバイト列における、全ての続くバイトは、80hからBFhにあります。このおかげで、再構成をしたり、堅牢なプログラムを作ることができます。
320320
321321(訳注: 上記の訳の説明ではよく分からないと思いますが、デバッグの際などに知っておいたほうがいいと思うので説明すると、 utf-8形式のユニコード文字列のバイト表現は、
322322
323323- 最初のバイトが(00-7f)hは1バイト、
324324- 最初のバイトが(c0-df)hの場合は2バイト、
325325- 最初のバイトが(e0-ef)hの場合は3バイト、
326326- 最初のバイトが(f0-f7)hの場合は4バイトで構成されています。
327327
3283282文字目以降のバイトは、常に(80-bf)hであって、 JISやEUCに比べると判定などがしやすい、というを上の文章が言っている(再構成や、堅牢)のだろうと思います。
329329
330330ちなみに、漢字の表現には3バイト、新拡張JISコード文字には、4バイトが必要。
331331
332332日本語のテキストファイルはS-JISの1.5倍のサイズになってしまいますが、ソースコードなどは、ほとんど英文字が多いので、ファイルサイズはそれほど増えないでしょう。
333333
334334訳注ここまで )
335335
336336**UTF-16**は次のような特徴があります。: UTF-16はU+0000からU+d7ffまでの文字は1つの16bitワードを使い、その他のユニコード文字は2つの16ビットワードを使います。
337337
338338最後に、**UTF-32**では、すべてのユニコード文字は1つの32ビットとして表現できます。
339339
340340更なる情報は、こちらをみてください: [Unicode FAQ - Basic questions](http://www.unicode.org/faq/basic_q.html), [Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM](http://www.unicode.org/faq/utf_bom.html), [Wikipedia: UTF-8](http://en.wikipedia.org/wiki/UTF-8) [1](http://en.wikipedia.org/wiki/ISO-8859)
341341
342342## Lazarusコンポーネントライブラリのアーキテクチャの要点
343343
344344LCL(Lazarusコンポーネントライブラリ)は2つの部分からできています。
345345
3463461. DelphiのVCLのように、クラス階層を実装していて、ターゲットプラットホームに依存しない部分 
3473472. "Interfaces" つまり、それぞれのターゲットのAPIをインターフェースするために実装している部分。
348348
3493492つの部分の橋渡しの仕組みは、抽象クラスである TWidgetsetにあります。 それぞれのwidgetsetは、TWidgetsetから派生したクラスの中で実装されています。
350350
351351Gtk1 widgetsetが最も古いものです。このwidgetsetにおいていは文字列のエンコードはLANG環境変数によって決定されます。このエンコードは、通常はisoのシングルバイトであるISO-8859-nが使われています。最近の多くのディストリビューション(たとえば、Mandriva 2007など)は、Gtk1をUTF-8として設定して出荷されています。ところが、LazarusのGtk1インターフェースは、キーボードの扱いでUTF-8を適切に扱っていません。この問題が最近大きくなり、Lazarusに対するクロスプラットホームでのユニコードサポートの要望が日増しにつよくなっています。
352352
353353Gtk2 widgetsetはUTF-8エンコードでのみ動作し、UTF-8 に完全対応しています。
354354
355355Lazarus 0.9.28 Windows と Windows CE インターフェースはユニコードに完全対応しています。
356356
357357Qtインターフェースは、UTF-8として準備されています。Qtそのものは、UTF-16をネイティブなエンコードとして採用しています。LazarusのQtインターフェースはUTF-8をUTF-16に変換しています。
358358
359359こちらも参照してください。: [[LCL Internals/ja|Internals of the LCL]]
360360
361361## win32インターフェースでユニコードを有効にする
362362
363363### 方針
364364
365365まず最初に、最も重要なこととして、Win32インターフェースのすべてのユニコードパッチは、これまでのANSIインターフェースを壊してしまわないように、IFDEF WindowsUnicodeSupportによって囲まれていなければなりません。これらの作業が安定してきてから、すべてのifdefが取り除かれて、ユニコード部分だけが残るようになります。この時点で、すべての既存のANSI文字を使うプログラムが、Unicodeへ移行できるようになるでしょう。
366366
367367Win9xでのWindowsプラットホームは、ISOコードページ標準でなりたっており、Unicodeは部分的なサポートしかありません。WinNTとWindowsCEを起源とするWindowsプラットホームは、完全にUnicodeをサポートしています。Win9xとNTには、古いANSI文字を引数にとる*Aという関数と、Unicodeワイド文字列(つまり、UTF-16でエンコードされた文字列)を引数にとる*Wという関数の、2つの同じ機能をもったAPI関数を提供しています。Windows CEでは、ワイドAPI関数しか使いません。
368368
369369#### Windows 9xに存在するワイド文字列関数
370370
371371いくつかのWide系API関数はWindows9xにも存在します。ここに、そのような関数のリストを示します。: <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp>
372372
373373変換例:
374374
375375 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
376376 Length(ButtonCaption), TextSize);
377377
378378これは、次のようにします。
379379
380380 {$ifdef WindowsUnicodeSupport}
381381 GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
382382 {$else}
383383 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
384384 {$endif}
385385
386386#### AnsiとWideのバージョンが必要な関数
387387
388388最初の変換例:
389389
390390 function TGDIWindow.GetTitle: String;
391391 var
392392 l: Integer;
393393 begin
394394 l := Windows.GetWindowTextLength(Handle);
395395 SetLength(Result, l);
396396 Windows.GetWindowText(Handle, @Result[1], l);
397397 end;
398398
399399これは、次のようにします。
400400
401401 function TGDIWindow.GetTitle: String;
402402 var
403403 l: Integer;
404404 AnsiBuffer: string;
405405 WideBuffer: WideString;
406406 begin
407407
408408 {$ifdef WindowsUnicodeSupport}
409409
410410 if UnicodeEnabledOS then
411411 begin
412412 l := Windows.GetWindowTextLengthW(Handle);
413413 SetLength(WideBuffer, l);
414414 l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
415415 SetLength(WideBuffer, l);
416416 Result := Utf8Encode(WideBuffer);
417417 end
418418 else
419419 begin
420420 l := Windows.GetWindowTextLength(Handle);
421421 SetLength(AnsiBuffer, l);
422422 l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
423423 SetLength(AnsiBuffer, l);
424424 Result := AnsiToUtf8(AnsiBuffer);
425425 end;
426426
427427 {$else}
428428
429429 l := Windows.GetWindowTextLength(Handle);
430430 SetLength(Result, l);
431431 Windows.GetWindowText(Handle, @Result[1], l);
432432
433433 {$endif}
434434
435435 end;
436436
437437### ロードマップ
438438
439439#### Unicodeに対応し、動作しているもの
440440
441441- TForm, TButton, TLabel
442442- Most controls
443443- Menus
444444- LCLIntf.ExtTextOut and most other text related winapis
445445- TStrings based controls. Examples: TComboBox, TListBox, etc
446446- SynEdit shows and can input UTF-8 characters correctly
447447- Setting/Getting unicode strings to/from the ClipBoard
448448- Setting the Application Title in the project options to (for example) 'Minha Aplicação'.
449449- Double clicking words with non-ascii chars in the editor to select them
450450
451451#### Unicode化における既知の問題
452452
453453- SynEdit does not support RTL (right to left)
454-- <s>Is OpenFileDialogCallBack tested with selection large numbers of files?
454+- Is OpenFileDialogCallBack tested with selection large numbers of files?
455455 - Is this problem unicode specific? I think it's a generic problem. --Sekelsenmat 13:40, 14 February 2008 (CET)
456456 - 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)
457- - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10918></s> Fixed and implemented.
458-- <s>class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.
459- - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10919></s> Implemented.
457+ - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10918> Fixed and implemented.
458+- class function TWin32WSSelectDirectoryDialog.CreateHandle: Title, FileName and InitialDir should be made Unicode aware.
459+ - Associated bugtracker item: <http://bugs.freepascal.org/view.php?id=10919> Implemented.
460460
461461**Unicode化で起こりうる問題**
462462
463463Based on a code review, the following needs to be tested, because the code doesn't seem to be Unicode aware:
464464
465-- <s>class procedure TWin32WSCustomComboBox.SetText</s>
466-- <s>TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware</s>
467-- <s>TWin32WidgetSet.MessageBox doesn't call MessageBoxW.</s>
465+- class procedure TWin32WSCustomComboBox.SetText
466+- TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint is not Unicode aware
467+- TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
468468- TWin32WidgetSet.TextOut: Is Windows.TextOut supported on windows 9X?
469469 - Yes, please see [[LCL Unicode Support|LCL_Unicode_Support#Wide_functions_present_on_Windows_9x]]. Althought I never tested this.
470470- MessageBox buttons don't show unicode correctly when they are translated. Tested on the IDE. Could be a problem on the IDE however.
471471 - Note: I couldn't reproduce using the portuguese translation --Sekelsenmat 22:20, 12 January 2008 (CET)
472472- (list of unconfirmed problems, if confirmed can be moved to the list above)
473473
474474### スクリーンショット
475475
476-<figure>
477-<img src="Lazarus_Unicode_Test.png" title="Lazarus_Unicode_Test.png" />
478-<figcaption>Lazarus_Unicode_Test.png</figcaption>
479-</figure>
476+![](/wiki-images/4/49/Lazarus_Unicode_Test.png)
480477
481478## 日本語版特別情報
482479
483480- 現在 TStringListUTF8 や TFileStreamUTF8 などの実装が進められています。興味のある人は、components\lazutils\lazutf8classes.pas や components\lazutils\lazutf8.pas を参照してください。
484481
485482ただ、現実装にはやや不安な点があることも確かですので、ここにいくつか情報をあげておきます。
486483
487484TStringListUTF8 を大文字小文字を区別しないでソートしたときの実装がやや不安定です。そこで、次のような TStringListUTF8_mod クラスを作成して使用するとよいかもしれません。
488485
489486``` pascal
490487type
491488 TStringListUTF8_mod = class(TStringListUTF8)
492489 protected
493490 function DoCompareText(const s1,s2 : string) : PtrInt; override;
494491 public
495492 end;
496493
497494function TStringListUTF8_mod.DoCompareText(const s1, s2: string): PtrInt;
498495begin
499496 if CaseSensitive then
500497 Result:= CompareStr(s1, s2)
501498 else
502499 Result:= WideCompareText(UTF8Decode(s1), UTF8Decode(s2));
503500end;
504501```
505502
506503こちらの方が信頼性が高い可能性があります。特に Windows では API で処理するので、速度も速い可能性もあります(実装では、UTF8LowerCase を実行後に CompareStr を実行しているだけですので。速度的に改善すべき余地があると思うのですが外国の開発者の理解を得るのは容易ではないです)。
507504
508505Windows 限定になりますが、次のコードも試す価値があります。
509506
510507``` pascal
511508uses
512509 lazutf8, Windows;
513510
514511type
515512 TStringListUTF8_mod = class(TStringListUTF8)
516513 protected
517514 function DoCompareText(const s1,s2 : string) : PtrInt; override;
518515 public
519516 end;
520517
521518{
522519 Copyright(C)malcome Lazarusユーザーの方はこのコードを自由に使用して構いません。
523520}
524521function _UTF8CompareText(const S1, S2: utf8string): Integer;
525522var
526523 l1, l2: Integer;
527524 c1, c2: LongWord;
528525 p1, p2: PChar;
529526begin
530527 p1 := @S1[1];
531528 p2 := @S2[1];
532529 if (p1 <> nil) and (p2 <> nil) then begin
533530 while (p1^ <> #0) and (p2^ <> #0) do begin
534531 c1:= UTF8CharacterToUnicode(p1, l1);
535532 c2:= UTF8CharacterToUnicode(p2, l2);
536533 if (c1 = 0) or (c2 = 0) then Break;
537534 if c1 <> c2 then begin
538535 c1:= LongWord(Windows.CharLowerW(PWideChar(c1)));
539536 c2:= LongWord(Windows.CharLowerW(PWideChar(c2)));
540537 if c1 <> c2 then Break;
541538 end;
542539 Inc(p1, l1); Inc(p2, l2);
543540 end;
544541 if (p1^ = #0) or (p2^ = #0) then
545542 Result := Byte(p1^) - Byte(p2^)
546543 else
547544 Result := c1 - c2
548545 end else
549546 Result := p1 - p2;
550547end;
551548
552549function TStringListUTF8_mod.DoCompareText(const s1, s2: string): PtrInt;
553550begin
554551 if CaseSensitive then
555552 Result:= CompareStr(s1, s2)
556553 else
557554 Result:= _UTF8CompareText(s1, s2);
558555end;
559556```
560557
561558この方が、文字列すべてを操作対象としない分だけ速度が上がり、文字列リストが大きくなれば大きくなるほど、オリジナル版と顕著な速度差が生じると思われます。UTF8 を一度 UTF16 に変換しなくてもよいようにすればさらに速度が上がるとともに、Windows 以外の OS にも対応できるようになりますが、現時点ではソースが長くなりすぎますのでここには掲載できません。
562559
563560さらに、アプリケーションを日本や英語圏など半角アルファベット以外の文字の大文字小文字についてはさほど気にしない国でしか使用しないのであるならば、
564561
565562``` pascal
566563type
567564 TStringListUTF8_mod = class(TStringListUTF8)
568565 protected
569566 function DoCompareText(const s1,s2 : string) : PtrInt; override;
570567 public
571568 end;
572569
573570function TStringListUTF8_mod.DoCompareText(const s1, s2: string): PtrInt;
574571begin
575572 if CaseSensitive then
576573 Result:= CompareStr(s1, s2)
577574 else
578575 Result:= CompareText(s1, s2);
579576end;
580577```
581578
582579とすることでかなり高速化できます(漢字などのUTF8コードがASCIIコードに重なることがないため、CompareText でもうまく処理できるため)。
583580
584581関連しますが、lazutf8.pas の UTF8LowerCase 関数、UTF8UpperCase 関数も、
585582
586583``` pascal
587584function UTF8LowerCase(const s: String): String;
588585begin
589586 Result := UTF8Encode(WideLowerCase(UTF8Decode(s)));
590587end;
591588
592589function UTF8UpperCase(const s: String): String;
593590begin
594591 Result := UTF8Encode(WideUpperCase(UTF8Decode(s)));
595592end;
596593```
597594
598595の方(LCLProc にもともとあったコードの方)が信頼性も速度も上かもしれません。仮にクライアントから問題を報告されても、「Windows API の仕様です」と答えられるというのもメリットかもしれません。
599596
600597- Windowsにおいて、TPicture.LoadFromFile のように、内部で UTF8ToSys 関数をコールすることでユニコード対応を謳っているだけのものには、注意が必要です。Ansiに変換できない文字コードがUTF8に含まれていた場合、うまく処理できないからです(たとえば平仮名とハングルを混ぜたファイル名を使った場合や旧漢字体を使った場合など)。ParamStrUTF8 のように内部で SysToUTF8を使用しているでだけなのに、ユニコード対応を謳っているものも同様です。徐々に改善されてきているみたいですが、外国の開発者達の理解を得にくい問題ですので、アプリケーション作成の際に注意して対応してください。
601598
602599## 参考
603600
604601- [[UTF-8]] - UTF-8文字列についての記述
605602
606603---
607604*From the Free Pascal wiki, page "LCL Unicode Support/ja", as it stood on 2020-02-19 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history