试用GNU Gettext 开源多语组件包

来源:互联网 发布:实业难做 知乎 编辑:程序博客网 时间:2024/06/06 01:51
试用GNU Gettext 开源多语组件包

尝试过CnPack中的CnMultiLang组件和国外的GNU Gettext组件后,感觉GNU Gettext做多语言要方便些。

这两者都是开源免费的多语言组件,而且都采用语言文件保存的字符串替换的方式,不同的是CnMultiLang采用的是[元件.属性]=[字串]的方式,而GNU Gettext采用的是[旧字串]=[新字串]的方式。

CnMultiLang虽然比较成熟对中文化的支持比较好,而且是以控件的方式出现在组件面板中,可以可视化编辑属性,但是其多语化的方式为[元件.属性]=[字串]的方式,不方便的地方有两点:1、会有很多重复的地方 2、静态字符串,例如对话框、输入框的字符串的多语化非常不方便。

GNU Gettext采用的是字典方式,按照词汇对应。而且,各个细节都非常贴心。安装后,在工程目录文件夹上点右键即可生成字典模板,然后在某个特定语言文件上右键合并,即可智能合并刚生成的模板,字典文件采用通用的PO格式,可以采用通用编辑器编辑(推荐Poedit,很好用)。另外,最贴心的地方就是,对于混杂在代码中的字符串,只要使用一个函数_()将字符串扩起来,再重新生成字典模板即可将其加入翻译字典;对于一些动态生成的按钮、对话框标题等,只要将其加入resourcestring即可。当然,缺点也是有的:1、没有图形化方式编辑,需要自行添加代码(其实没几行)2、对中文化支持不好,需要自行添加代码解决(设置一下全局默认字体大小即可) 3、一词多义的情况不好处理(加空格或者放到新的domain里)

下面说一下GNU Gettext使用中需要注意的个别地方:

下载地址http://dybdahl.dk/dxgettext/

1、 在FormCreate中添加初始化代码
这些代码一般比较固定:
    ...{Initialazation for locales}     // 设置忽略列表
    TP_GlobalIgnoreClassProperty(TAction,'Category');
    TP_GlobalIgnoreClassProperty(TControl,
'HelpKeyword');
    TP_GlobalIgnoreClassProperty(TNotebook,
'Pages');
    TP_GlobalIgnoreClassProperty(TControl,
'ImeName');
    TP_GlobalIgnoreClass(TFont);
     
// 开始翻译组件
    TranslateComponent(self);



2、主菜单要加"&"
因为Delphi会自动给主菜单文字前面加一个"&"字符,如果你没有手动加的话,这样会造成主菜单这些单词无法被翻译。
例如:主菜单"File"最好写成"&File",否则生成的字典为“File”,但Delphi会编译时自动将菜单名替换为"&File",这样就无法匹配了。

3、resourcestring
常用的resourcestring举例:
resourcestring
    yes
='&Yes';no='&No';ok='OK'// 对话框按钮
    warning='Warning';error='Error'// 对话框标题


MessageDlg示例:
    MessageDlg(_('Exit without saving ?'), mtWarning, mbOKCancel,0)



4、切换语言的方法
procedure TMainForm.mniLangCnClick(Sender: TObject);
begin
    UseLanguage(
'zh_CN');
    RetranslateComponent(Self);
end;



对话框文字被截断问题补充上文中介绍的方法,即在FormCreate方法中设置DefFontData.Height属性,经过证实无效。
经考证,其实字符被截断的关键是设置了错误的Charset。例如,简体中文字符应该设置为GB2312_CHARSET,如果设置为ANSI_CHARSET则会计算字符宽度不准确,造成字符被截断的问题。

那么,介绍我现在使用的解决办法,就是当改变语言时自动读取并设置po文件中的字体信息,这种方法需要自己用文本编辑器添加一个自定义Property,例如"Default-Font: Size=9; Charset=GB2312_CHARSET\n"。

具体改写方法如下:

1、在gnugettext.pas文件中找到TGnuGettextInstance.WhenNewLanguage函数,并改写为
procedure TGnuGettextInstance.WhenNewLanguage(const LanguageID: string);
var
    s:
string;
    font:TStrings;
    i,code:Integer;
begin
    
// This is meant to be empty.
    ...{Read font info from po file, by Zero File}
    
// reset charset first
    DefFontData.Charset:=DEFAULT_CHARSET;
    
//read properties
    s := GetTranslationProperty('Default-Font');
    
if s = '' then Exit;
    font:
=TStringList.Create;
    font.Delimiter:
=';';
    font.DelimitedText:
= s;
    
for i:=0 to font.Count-1 do
    begin
        s:
=LowerCase(font.Names);
        
if 'name' = s then
            DefFontData.Name:
=font.ValueFromIndex
        
else if 'size' = s then
            DefFontData.Height:
= -MulDiv(StrToInt(font.ValueFromIndex),
                    GetDeviceCaps(GetDC(
0), LOGPIXELSY), 72)
        
else if 'charset' = s then begin
            
if IdentToCharset(font.ValueFromIndex,code) then
                DefFontData.Charset:
= code;
        end;
    end;
end;



2、在每个语言包的po文件中添加一条Property
例如:
简体中文:"Default-Font: Size=9; Charset=GB2312_CHARSET\n"
繁体中文:"Default-Font: Size=9; Charset=CHINESEBIG5_CHARSET\n"



==================以下转载cnblog  http://www.cnblogs.com/shangdawei/archive/2013/06/08/3127788.html=============


GNU Gettext for Delphi, C++ and Kylix

http://sourceforge.net/projects/dxgettext/

Description

GNU GetText translation tools for Borland Delphi and Borland C++ Builder

http://downloads.sourceforge.net/project/dxgettext/For%20Delphi%20and%20C%2B%2B%20Builder/1.2.2/dxgettext-1.2.2.exe

Bugfixes and improvements to gnugettext.pas

These changes are in the subversion repository on SourceForge.

使Delphi支持多语言的一种途径, 使用方法:

http://dybdahl.dk/dxgettext/docs/howto.php

How to do a translation

This document applies to the latest version. 
If you haven't got the latest version, please download it now.

1) Modify your source code.

Add gnugettext.pas to your project.

Add gnugettext to the uses clause of all units that contains forms.

Add the following line to the OnFormCreate event of your forms:

TranslateComponent (self);

Add typical ignores just before the TranslateComponent (self) you put in the main form.

Typical ignores

These are typical ignores in a program when using TranslateProperties(). 
You can copy and paste from this page to your application. 
Please note that only published properties of objects are subject to translation, 
whereas properties that are private, protected or public are automatically ignored.

If you find something that is missing on this pages, don't hesitate to tell us.

VCL, important ones

TP_GlobalIgnoreClassProperty(TAction,'Category'); TP_GlobalIgnoreClassProperty(TControl,'HelpKeyword');TP_GlobalIgnoreClassProperty(TNotebook,'Pages');

VCL, not so important

These are normally not needed.

TP_GlobalIgnoreClassProperty(TControl,'ImeName');TP_GlobalIgnoreClass(TFont);

Database (DB unit)

Field names and table names often tend to have names that are also used for 
other purposes elsewhere in the program. Therefore, it is very wise 
to add this somewhere in your program if you are using databases.

TP_GlobalIgnoreClassProperty(TField,'DefaultExpression');TP_GlobalIgnoreClassProperty(TField,'FieldName');TP_GlobalIgnoreClassProperty(TField,'KeyFields');TP_GlobalIgnoreClassProperty(TField,'DisplayName');TP_GlobalIgnoreClassProperty(TField,'LookupKeyFields');TP_GlobalIgnoreClassProperty(TField,'LookupResultField');TP_GlobalIgnoreClassProperty(TField,'Origin');TP_GlobalIgnoreClass(TParam);TP_GlobalIgnoreClassProperty(TFieldDef,'Name');

MIDAS/Datasnap

TP_GlobalIgnoreClassProperty(TClientDataset,'CommandText');TP_GlobalIgnoreClassProperty(TClientDataset,'Filename');TP_GlobalIgnoreClassProperty(TClientDataset,'Filter');TP_GlobalIgnoreClassProperty(TClientDataset,'IndexFieldnames');TP_GlobalIgnoreClassProperty(TClientDataset,'IndexName');TP_GlobalIgnoreClassProperty(TClientDataset,'MasterFields');TP_GlobalIgnoreClassProperty(TClientDataset,'Params');TP_GlobalIgnoreClassProperty(TClientDataset,'ProviderName');

Database controls

TP_GlobalIgnoreClassProperty(TDBComboBox,'DataField');TP_GlobalIgnoreClassProperty(TDBCheckBox,'DataField');TP_GlobalIgnoreClassProperty(TDBEdit,'DataField');TP_GlobalIgnoreClassProperty(TDBImage,'DataField');TP_GlobalIgnoreClassProperty(TDBListBox,'DataField');TP_GlobalIgnoreClassProperty(TDBLookupControl,'DataField');TP_GlobalIgnoreClassProperty(TDBLookupControl,'KeyField');TP_GlobalIgnoreClassProperty(TDBLookupControl,'ListField');TP_GlobalIgnoreClassProperty(TDBMemo,'DataField');TP_GlobalIgnoreClassProperty(TDBRadioGroup,'DataField');TP_GlobalIgnoreClassProperty(TDBRichEdit,'DataField');TP_GlobalIgnoreClassProperty(TDBText,'DataField');

Interbase Express (IBX)

TP_GlobalIgnoreClass(TIBDatabase);TP_GlobalIgnoreClass(TIBDatabase);TP_GlobalIgnoreClass(TIBTransaction);TP_GlobalIgnoreClassProperty(TIBSQL,'UniqueRelationName');

Borland Database Engine

TP_GlobalIgnoreClass(TSession);TP_GlobalIgnoreClass(TDatabase);

ADO components

TP_GlobalIgnoreClass (TADOConnection);TP_GlobalIgnoreClassProperty(TADOQuery,'CommandText');TP_GlobalIgnoreClassProperty(TADOQuery,'ConnectionString');TP_GlobalIgnoreClassProperty(TADOQuery,'DatasetField');TP_GlobalIgnoreClassProperty(TADOQuery,'Filter');TP_GlobalIgnoreClassProperty(TADOQuery,'IndexFieldNames');TP_GlobalIgnoreClassProperty(TADOQuery,'IndexName');TP_GlobalIgnoreClassProperty(TADOQuery,'MasterFields');TP_GlobalIgnoreClassProperty(TADOTable,'IndexFieldNames');TP_GlobalIgnoreClassProperty(TADOTable,'IndexName');TP_GlobalIgnoreClassProperty(TADOTable,'MasterFields');TP_GlobalIgnoreClassProperty(TADOTable,'TableName');TP_GlobalIgnoreClassProperty(TADODataset,'CommandText');TP_GlobalIgnoreClassProperty(TADODataset,'ConnectionString');TP_GlobalIgnoreClassProperty(TADODataset,'DatasetField');TP_GlobalIgnoreClassProperty(TADODataset,'Filter');TP_GlobalIgnoreClassProperty(TADODataset,'IndexFieldNames');TP_GlobalIgnoreClassProperty(TADODataset,'IndexName');TP_GlobalIgnoreClassProperty(TADODataset,'MasterFields');

ActiveX stuff

TP_GlobalIgnoreClass (TWebBrowser);

TMS Software

It is not possible to read all string properties of TAdvStringGrid's subcomponents 
without generating an exception. Therefore, 
this class should not be handled by translateproperties().

TP_GlobalIgnoreClass (TAdvStringGrid);

2) Extract the text to be translated into .po files:

Click with the right mouse button on the file folder that contains your Delphi source code, 
and choose "GG Extract translations to template".

This will create a file named default.po with all the texts to be translated. 
Because it has no translations in it, it is named a "translation template".

3) Preparing the translation

If you haven't translated anything yet, copy your .po file to a subfolder and proceed to step 4).

If you have an old translation of your program, click with the right mouse button on that old translation, 
and choose to "Merge template". Here, you must specify the .po file that was generated in step 2). 
This will update your old .po file to contain the new texts, and it will remove unnecessary translations.

4) Translate the .po files with poedit

The easiest way to translate, is to install poedit. It is a free tool, so just get it and install it - 
it is available for Windows, Macintosh and Linux. It is very easy to use, 
and when you click the save button, it saves the .po file 
but also automatically writes an .mo file each time.

5) Deploy your application like this:

The .mo file is a compact, binary file that is much faster for lookups than .po files. 
It's the one you deploy with your application.

FileDescriptionappdir/application.exeYour applicationappdir/locale/da/LC_MESSAGES/default.moDanish translationsappdir/locale/de/LC_MESSAGES/default.moGerman translationsappdir/locale/de_AT/LC_MESSAGES/default.moGerman translations for Austria

The directory names for the different languages are 
either just the two-letter ISO 639 language code or 
a combination of the ISO 639 language code and the ISO 3166 country code.

In the above example, if the program was running on a German computer, 
it would first search for the de_DE directory, and then for the de directory because de_DE does not exist.

6) Consider to make a single-file version

You can avoid all the .mo files by including them inside your .exe file. 
In order to do this, you must first make sure that the above works, 
and then you right click the .exe file and choose "GG Embed translations":

This will append all the .mo files to the .exe file 
in a way that makes the .exe file self-contained.

Important tips

  • If you did everything above, you will have translated all strings 
    in your user interface and all resourcestrings. 
    Using resourcestrings works great, but it works much better 
    if you would consider to use the _() syntax instead.
  • GNU gettext is designed for translating English language programs to other languages. 
    It is possible to translate from other languages, too, 
    but then the program will be limited to run on computers 
    that use the same character set in ansistrings. 

0 0
原创粉丝点击