L'implémentation originale de la conversion du chinois vers le Pinyin est souvent conçue pour l'environnement Delphi. Lors de l'utilisation sous Lazarus, des erreurs de conversion surviennent fréquemment en raison de la gestion des encodages de caractères (UTF-8). Le code suivant a été adapté pour fonctionner correctement sous Lazarus/FreePascal, en incluant le support pour le Pinyin complet et l'extraction des initiales.
Exemple d'utilisation dans une application
unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
uPinyinConverter, LConvEncoding;
type
TFormMain = class(TForm)
btnConvert: TButton;
inputEdit: TEdit;
resultMemo: TMemo;
procedure btnConvertClick(Sender: TObject);
end;
var
FormMain: TFormMain;
implementation
{$R *.lfm}
procedure TFormMain.btnConvertClick(Sender: TObject);
var
strInput: string;
begin
strInput := inputEdit.Text;
// Ajout du Pinyin complet
resultMemo.Lines.Add(ExtractFullPinyin(strInput));
// Ajout des initiales uniquement
resultMemo.Lines.Add(ExtractPinyinInitials(strInput));
end;
end.
Unité de traitement : uPinyinConverter
Cette unité contient la logique de mappage entre les codes GBK et les phonétiques Pinyin. Elle utilise une table d'indexation pour transformer les caractères chinois en leur équivalent phonétique.
unit uPinyinConverter;
{$mode objfpc}{$H+}
interface
uses
SysUtils, Classes, LazUTF8, LConvEncoding;
const
TOTAL_PY_ENTRIES = 405;
{ Table des sons de base }
PINYIN_SOUNDS: array [1..TOTAL_PY_ENTRIES] of string[6] = (
'a', 'ai', 'an', 'ang', 'ao', 'ba', 'bai', 'ban', 'bang', 'bao',
'bei', 'ben', 'beng', 'bi', 'bian', 'biao', 'bie', 'bin', 'bing', 'bo',
'bu', 'ca', 'cai', 'can', 'cang', 'cao', 'ce', 'ceng', 'cha', 'chai',
'chan', 'chang', 'chao', 'che', 'chen', 'cheng', 'chi', 'chong', 'chou', 'chu',
'chuai', 'chuan', 'chuang', 'chui', 'chun', 'chuo', 'ci', 'cong', 'cou', 'cu',
'cuan', 'cui', 'cun', 'cuo', 'da', 'dai', 'dan', 'dang', 'dao', 'de',
'deng', 'di', 'dian', 'diao', 'die', 'ding', 'diu', 'dong', 'dou', 'du',
'duan', 'dui', 'dun', 'duo', 'e', 'en', 'er', 'fa', 'fan', 'fang',
'fei', 'fen', 'feng', 'fu', 'fou', 'ga', 'gai', 'gan', 'gang', 'gao',
'ge', 'ji', 'gen', 'geng', 'gong', 'gou', 'gu', 'gua', 'guai', 'guan',
'guang', 'gui', 'gun', 'guo', 'ha', 'hai', 'han', 'hang', 'hao', 'he',
'hei', 'hen', 'heng', 'hong', 'hou', 'hu', 'hua', 'huai', 'huan', 'huang',
'hui', 'hun', 'huo', 'jia', 'jian', 'jiang', 'qiao', 'jiao', 'jie', 'jin',
'jing', 'jiong', 'jiu', 'ju', 'juan', 'jue', 'jun', 'ka', 'kai', 'kan',
'kang', 'kao', 'ke', 'ken', 'keng', 'kong', 'kou', 'ku', 'kua', 'kuai',
'kuan', 'kuang', 'kui', 'kun', 'kuo', 'la', 'lai', 'lan', 'lang', 'lao',
'le', 'lei', 'leng', 'li', 'lia', 'lian', 'liang', 'liao', 'lie', 'lin',
'ling', 'liu', 'long', 'lou', 'lu', 'luan', 'lue', 'lun', 'luo', 'ma',
'mai', 'man', 'mang', 'mao', 'me', 'mei', 'men', 'meng', 'mi', 'mian',
'miao', 'mie', 'min', 'ming', 'miu', 'mo', 'mou', 'mu', 'na', 'nai',
'nan', 'nang', 'nao', 'ne', 'nei', 'nen', 'neng', 'ni', 'nian', 'niang',
'niao', 'nie', 'nin', 'ning', 'niu', 'nong', 'nu', 'nuan', 'nue', 'yao',
'nuo', 'o', 'ou', 'pa', 'pai', 'pan', 'pang', 'pao', 'pei', 'pen',
'peng', 'pi', 'pian', 'piao', 'pie', 'pin', 'ping', 'po', 'pou', 'pu',
'qi', 'qia', 'qian', 'qiang', 'qie', 'qin', 'qing', 'qiong', 'qiu', 'qu',
'quan', 'que', 'qun', 'ran', 'rang', 'rao', 're', 'ren', 'reng', 'ri',
'rong', 'rou', 'ru', 'ruan', 'rui', 'run', 'ruo', 'sa', 'sai', 'san',
'sang', 'sao', 'se', 'sen', 'seng', 'sha', 'shai', 'shan', 'shang', 'shao',
'she', 'shen', 'sheng', 'shi', 'shou', 'shu', 'shua', 'shuai', 'shuan', 'shuang',
'shui', 'shun', 'shuo', 'si', 'song', 'sou', 'su', 'suan', 'sui', 'sun',
'suo', 'ta', 'tai', 'tan', 'tang', 'tao', 'te', 'teng', 'ti', 'tian',
'tiao', 'tie', 'ting', 'tong', 'tou', 'tu', 'tuan', 'tui', 'tun', 'tuo',
'wa', 'wai', 'wan', 'wang', 'wei', 'wen', 'weng', 'wo', 'wu', 'xi',
'xia', 'xian', 'xiang', 'xiao', 'xie', 'xin', 'xing', 'xiong', 'xiu', 'xu',
'xuan', 'xue', 'xun', 'ya', 'yan', 'yang', 'ye', 'yi', 'yin', 'ying',
'yo', 'yong', 'you', 'yu', 'yuan', 'yue', 'yun', 'za', 'zai', 'zan',
'zang', 'zao', 'ze', 'zei', 'zen', 'zeng', 'zha', 'zhai', 'zhan', 'zhang',
'zhao', 'zhe', 'zhen', 'zheng', 'zhi', 'zhong', 'zhou', 'zhu', 'zhua', 'zhuai',
'zhuan', 'zhuang', 'zhui', 'zhun', 'zhuo', 'zi', 'zong', 'zou', 'zu', 'zuan',
'zui', 'zun', 'zuo', '', 'ei', 'm', 'n', 'dia', 'cen', 'nou',
'jv', 'qv', 'xv', 'lv', 'nv'
);
{ Table d'indexation (segmentée pour l'exemple) }
PINYIN_INDEX: array [1..126, 1..191] of Word = (
// Note: Les données volumineuses de la table originale doivent être insérées ici.
// Structure: [Octet1 - 128, Octet2 - 63]
(354,279,331,0,190,39,284,67,249,167,353,19,133,92,129,152,31,385,105,131,84,348,348,329,133,0,376,116,283,349,344,87,133,0,0,0,284,202,0,0,0,0,0,0,342,345,0,276,199,243,0,0,52,0,375,102,88,176,170,348,136,180,375,0,354,294,299,94,344,368,344,55,347,333,131,347,353,369,166,74,325,92,0,329,6,19,0,306,0,332,372,114,107,20,33,370,279,258,79,191,243,328,220,83,43,324,62,376,229,354,64,73,329,348,336,92,2,349,232,282,84,305,389,357,134,332,343,336,363,332,217,208,335,340,377,389,13,232,348,294,348,374,107,181,378,21,250,14,371,47,346,369,14,332,250,284,10,121,110,333,91,78,194,19,113,123,102,251,47,337,282,320,106,157,348,101,349,189,340,3,175,77,178,29,37,343,377,263,125,294,56),
{ ... les autres lignes suivent le même schéma ... }
(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
);
function ProcessChineseText(const AText: string; AFullPinyin: Boolean; ASeparator: Boolean): string;
function ExtractFullPinyin(const AText: string): string;
function ExtractPinyinInitials(const AText: string): string;
implementation
function ProcessChineseText(const AText: string; AFullPinyin: Boolean; ASeparator: Boolean): string;
var
i, vIdx: Integer;
vEncodedStr: string;
vResult: string;
vPinyin: string;
begin
vResult := '';
// Lazarus utilise UTF-8, la table utilise CP936 (GBK)
vEncodedStr := Utf8ToCp936(AText);
i := 1;
while i <= Length(vEncodedStr) do
begin
// Vérification si caractère sur 2 octets (Chinois)
if (Ord(vEncodedStr[i]) >= 129) and (i < Length(vEncodedStr)) and (Ord(vEncodedStr[i+1]) >= 64) then
begin
vIdx := PINYIN_INDEX[Ord(vEncodedStr[i]) - 128, Ord(vEncodedStr[i+1]) - 63];
if vIdx > 0 then
begin
vPinyin := UpperCase(PINYIN_SOUNDS[vIdx]);
if not AFullPinyin then
vPinyin := Copy(vPinyin, 1, 1);
if ASeparator and (vResult <> '') then
vResult := vResult + ' ' + vPinyin
else
vResult := vResult + vPinyin;
end;
Inc(i, 2);
end
else
begin
// Caractère ASCII standard
if vEncodedStr[i] in ['a'..'z', 'A'..'Z', '0'..'9'] then
vResult := vResult + UpperCase(vEncodedStr[i]);
Inc(i);
end;
end;
Result := vResult;
end;
function ExtractFullPinyin(const AText: string): string;
begin
Result := ProcessChineseText(AText, True, True);
end;
function ExtractPinyinInitials(const AText: string): string;
begin
Result := ProcessChineseText(AText, False, False);
end;
end.
Le fonctionnement repose sur la conversion préalable de la chaîne UTF-8 en CP936 via Utf8ToCp936 (disponible dans LConvEncoding). L'algorithme parcourt ensuite les octets : s'il détecte une séquence de caractères chinois (octets >= 129), il calcule l'offset dans la matrice PINYIN_INDEX pour récupérer l'index de la syllabe correspondnate dans PINYIN_SOUNDS.