admin管理员组文章数量:1531792
2024年7月23日发(作者:)
----------------仅供学习参考----------------
关于DELPHI程序多语言支持的问题
写DELPHI程序,是如何实现繁体化的?是如何开发英文版的?是通过外挂语言
包吗?
我完整地说一下我的问题:我写一个DELPHI程序,开发环境是是简体中文版本
win xp系统+ Delphi 7的。现在想做一个繁体版本以及其他非简体中文版本的
出来。此时遇到这几个问题:
1、在非简体中文系统中均出现乱码或界面文字错位。
2、而且语言包速度太慢了,每次启动要预读一下语言文件进行界面翻译,工程
量大的时候明显感到启动速度下降了
3、因为工程量比较大,逐一设置“提示内容”及“界面内容”的语言包比较麻
烦,所以不想用ini文件或dll资源库的语言包形式
请问各位DELPHI高手你们是怎么做的?有没有一劳永逸的方法?
听说DELPHI内置的多国语言功能, 有没有用法帮助?
还有没有其他一些好用简单强大的Unicode语言包控件?
---------------------------------------------------------------------
Delphi的多语言是这么用的,但是添加多语言后编译连接速度很慢,有时候10
来分钟。
可以把你的Delphi的窗体文件分给翻译来翻译,如果是Delphi7,需要先用
来转换为文本格式。
1、Project-New Application 创建你的中文版软件。
2、Project-Language-Add 添加你的语言,比如美国英语(ENU)。或者File-
New-Others-Dll Wizard
3、保存后生成多个文件夹。
4、Project-Build All
5、程序目录下生成和工程文件同名的语言文件:;,软件
发布需要带这些文件。
6、把软件里面所有字符串定义为资源字符串。
resourcestring
C_AppTitle = 'xx管理系统';
C_Error = '错误';
C_Warning = '警告';
7、在软件中用如下代码切换语言:
const
//定义你得多语言:
ENGLISH = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
CHINESE = (SUBLANG_CHINESE_SIMPLIFIED shl 10) or LANG_CHINESE;
uses
ReInit;
...
if LoadNewResourceModule(ENGLISH) <> 0 then
begin
ReInitializeForms();
end;
更多请参考Delphi帮助,里面写得更清楚。
我以前也用INI文件做多语言,感觉挺好用,Delphi带得这个多语言感觉编译
起来太慢,而且总需要Build半天。
利用INI文件实现界面无闪烁多语言切换
程序运行时,我们查找当前目录下所有的语言配置文件(*.ini),为了达到这个目的,我编
写了如下的函数搜索目录下所有的语言配置文件的文件名,然后将文件名去掉ini扩展名
保存返回:
function LanguagePack:TStrings;
var
ResultStrings:TStrings;
DosError:integer;
SearchRec:TsearchRec;
begin
ResultStrings:=;
DosError:=FindFirst(ExtractFilePath(ParamStr(0))+'*.ini', faAnyFile, SearchRec);
while DosError=0 do
begin
{ 返回的文件名并去掉末尾的.ini字符 }
(ChangeFileExt(,''));
DosError:=FindNext(SearchRec);
end;
FindClose(SearchRec);
Result:=ResultStrings;
end;
在Form建立的事件中添加代码,将目录下所有的语言文件名加入选择列表框中。
procedure eate(Sender: TObject);
begin
ings(SearchLanguagePack);
end;
程序的重点在如何切换语言,在ComboBox1的OnChange事件中进行切换操作。
这里我写了SetActiveLanguage过程用于实现这一操作。
procedure ox1Change(Sender: TObject);
begin
SetActiveLanguage();
end;
其中SetActiveLanguage代码如下:
procedure iveLanguage(LanguageName:string);
const
Translations='Translations';
Messages='Messages';
var
frmComponent:TComponent;
i:Integer;
begin
with (ExtractFilePath(ParamStr(0))+LanguageName+'.ini') do
begin
for i:=0 to ComponentCount-1 do { 遍历Form组件 }
begin
frmComponent:=Components[i];
if frmComponent is TLabel then { 如果组件为TLabel型则当作TLabel处理,以下同 }
begin
(frmComponent as TLabel).Caption:=
ReadString(Translations,
+'.Caption',(frmComponent as TLabel).Caption);
end;
if frmComponent is TCheckBox then
begin
(frmComponent as TCheckBox).Caption:=
ReadString(Translations,
+'.Caption',(frmComponent as TCheckBox).Caption);
end;
if frmComponent is TButton then
begin
(frmComponent as TButton).Caption:=
ReadString(Translations,
+'.Caption',(frmComponent as TButton).Caption);
(frmComponent as TButton).Hint:=
ReadString(Translations,
+'.Hint',(frmComponent as TButton).Hint);
end;
if frmComponent is TMenuItem then
begin
(frmComponent as TMenuItem).Caption:=
ReadString(Translations,
+'.Caption',(frmComponent as TMenuItem).Caption);
end;
end;
M1:=ReadString(Messages,'M1',M1);
end;
end;
在这个过程中,我们遍历了Form中的所有组件,根据他们的类别和组件名动态的从ini配
置文件中读出应该显示的语言文字。
用遍历组件的方法比一个一个写出具体的组件维护起来要方便很多,代码的适应性也更强。
其中M1为一个字符串变量,这样提示消息也能切换,比如在Button1的Click事件中
procedure 1Click(Sender: TObject);
begin
ShowMessage(M1);
end;
就可以根据不同的语言给出不同的提示文字。
好了,整个工程就做完了,你可以运行测试一下,是不是切换迅速而且无闪烁。
Delphi实现多语言界面
关键字:多语言界面,Delphi,国际化,本地化。
随着Internet在全球的普及,一个软件开发者,开发出来的产品可以随意发布到全球各个
角落,然而与此同时,开发出来的产品也面临着一个新的问题:如 何实现各种不同的语言
界面,甚至根据最终用户的操作系统的语言版本,自动更改语言界面?难道为每一个不同
的语言编写一个不同的版本?不,完全没有必要。 Delphi 5.0作为一个优秀的快速RAD
开发工具,可以很容易地实现国际化支持,因为Delphi 5.0内置了对多语言界面的支持。
一个程序,如果需要不同的语言版本,那么应该有一下几点需要注意的地方[]:
1. 必须允许你的程序代码能够处理好各种语言字符串,例如如果要中文化,必须能够处理
双字节。
2. 你必须设计好你的程序界面,以便能够使你的程序界面元素有足够的空间显示语言文字
信息。一般说来,在50个字节以内的英文单词所表达的意思,用其他的语言 来描述的话,
长度要超过50字节,但中文是一个例外。特别对于几个字节的英文单词,其他的语言的长
度几乎百分之百要超过英文的长度!因此,必须在控件中留 出足够的长度以便在更改语言
之后,还能显示全部的语言文字信息。
3. 你必须翻译所有的资源。
本文将着重讨论如何用Delphi 5.0实现多语言的支持和切换,界面设计和上述要求不在本
文讨论范围之内。
要 为程序添加语言支持,只要在Delphi主菜单项Project下面选择LanguagesàAdd…即可。
点击之后出现语言向导,读者按照向导进行操作 即可。向导结束之后,会生成一个工程组
文件(BPG),最后出现Translation Manager,软件开发者可以在这里翻译所有语言的所有资
源,包括字体、位置、文字等等。说明一下:你可以随时随地用Project下面的 Languages
子菜单的功能来添加、删除、修改各种界面元素。
做完上述工作之后,我们现在就差切换语言的代码了。为了切换语言,大家 可以使用下面
的一个单元[],单元中提供了两个函数,用来更换语言界面元素,其中
LoadNewResourceModule是用来修改文字信息等 等,ReinitializeForms用来重新刷新窗体
和控件以保证同步。
///文件名:
unit MaltiLan;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms;
procedure ReinitializeForms;
function LoadNewResourceModule(Locale: LCID): Longint;
implementation
type
TAsInheritedReader = class(TReader)
Public
procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer); Override;
end;
procedure efix(var Flags: TFilerFlags; var AChildPos: Integer);
begin
inherited ReadPrefix(Flags, AChildPos);
Include(Flags, ffInherited);
end;
function SetResourceHInstance(NewInstance: Longint): Longint;
var
CurModule: PLibModule;
begin
CurModule := LibModuleList;
Result := 0;
while CurModule <> nil do
begin
if ce = HInstance then
begin
if tance <> ce then
FreeLibrary(tance);
tance := NewInstance;
Result := NewInstance;
Exit;
end;
CurModule := ;
end;
end;
function LoadNewResourceModule(Locale: LCID): Longint;
var
FileName: array[0..260] of char;
P: PChar;
LocaleName: array[0..4] of Char;
NewInst: Longint;
begin
GetModuleFileName(HInstance, FileName, SizeOf(FileName));
GetLocaleInfo(Locale, LOCALE_SABBREVLANGNAME, LocaleName,
SizeOf(LocaleName));
P := PChar(@FileName) + lstrlen(FileName);
while (P^ <> '.') and (P <> @FileName) do Dec(P);
NewInst := 0;
Result := 0;
if P <> @FileName then
begin
Inc(P);
if LocaleName[0] <> #0 then
begin
// Then look for a potential language/country translation
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
if NewInst = 0 then
begin
// Finally look for a language only translation
LocaleName[2] := #0;
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
end;
end;
end;
if NewInst <> 0 then
Result := SetResourceHInstance(NewInst)
end;
function InternalReloadComponentRes(const ResName: string; HInst: THandle; var Instance:
TComponent): Boolean;
var
HRsrc: THandle;
ResStream: TResourceStream;
AsInheritedReader: TAsInheritedReader;
begin { avoid possible EResNotFound exception }
if HInst = 0 then HInst := HInstance;
HRsrc := FindResource(HInst, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
ResStream := (HInst, ResName, RT_RCDATA);
try
AsInheritedReader := (ResStream, 4096);
try
Instance := otComponent(Instance);
finally
;
end;
finally
;
end;
Result := True;
end;
function ReloadInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
function InitComponent(ClassType: TClass): Boolean;
begin
Result := False;
if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;
Result := InitComponent(arent);
Result := InternalReloadComponentRes(ame, FindResourceHInstance(
FindClassHInstance(ClassType)), Instance) or Result;
end;
begin
Result := InitComponent(ype);
end;
procedure ReinitializeForms;
var
Count: Integer;
I: Integer;
Form: TForm;
begin
Count := unt;
for I := 0 to Count - 1 do
begin
Form := [I];
ReloadInheritedComponent(Form, TForm);
end;
end;
end.
测试程序窗体单元文件如下:
///单元文件名:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Menus, MLanTool, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Exit1: TMenuItem;
Language1: TMenuItem;
Chese1: TMenuItem;
English1: TMenuItem; Button1: TButton; Memo1: TMemo; ListBox1: TListBox;
GroupBox1: TGroupBox; Panel1: TPanel; procedure Exit1Click(Sender: TObject);
procedure Chese1Click(Sender: TObject); procedure English1Click(Sender: TObject);
Private
{ Private declarations }
Public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const
ENGLISH = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
CHINESE = (SUBLANG_CHINESE_SIMPLIFIED shl 10) or LANG_CHINESE;
procedure 1Click(Sender: TObject);
begin
Close;
end;
procedure 1Click(Sender: TObject);
begin
if LoadNewResourceModule(CHINESE) <> 0 then
ReinitializeForms;
end;
procedure h1Click(Sender: TObject);
begin
if LoadNewResourceModule(ENGLISH) <> 0 then
ReinitializeForms;
end;
end.
如果要自动切换语言,只要在FormCreate事件中添加如下代码即可:
if LoadNewResourceModule(tLCID) <> 0 then
ReinitializeForms;
说明一点:在程序完成的时候,你应该用Luanguages子菜单的Update Resources DLL功能
更新所有的窗体和代码,然后用Build All Project编译所有的文件,这样才能保证你的程序
正常运行。
所有的源代码可以到下载。
后 记:其实用INI文件也可以实现多语言界面的切换,好处是可以方便大家随时添加不同
的语言文件,但是,对于一个大型的程序来说,用INI是不现实的。用 INI实现语言界面
的程序,大家可以到下载源代 码和测试程序。
干净搞定delphi多语言-兼论设计模式
随着全球化程度加深,软件越来越像蒲公英,到处飘散、扎根。这其中要解决
的是不同语言的显示问题。我们当然希望一套程序,可以不修改代码就可以支
持不同的语言,不要去维护很多的版本。
首先要谈到的一个问题是乱码问题,因为delphi win32到11.x版还
是不支持unicode,所以一般使用Ansi码,有这样几种情况会显示乱码:
使用的语言文字与系统当前设定的语言不一样;比如简体版QQ在繁体操作系统
(或简体操作系统的区域设置中“非Unicode程序的语言”设定为繁体)就是
乱码。即使改变t,某些元件仍然会出现乱码,如StatusBar。因
此,在越南文版的windows显示越南文,在伊朗文版的windows显示伊朗文,
不要在越南文版windows显示伊朗文,在伊朗文版windows显示越南文,这样
就能确保没有乱码问题。好在一般这样的错位用法也不多见。
系统没有安装你要显示的语言的语言包;
如果你要保证完全无乱码,必须考虑使用unicode码,使用成套的支
持unicode的元件,如tnt,但它在UI变现上比较单一,你不可能不使用别的
元件。
言归正传,首先,看看哪些地方的字串需要实现多语言,并来看看各
种实现方法的优劣。
1、界面上的元件,如TButton的Caption;
2、主动弹出的消息,如ShowMessage('Are you sure?'),Raise
('Error!');
3、例外错误举发的报告信息,如f/0引起的exception;
4、第3方元件包内部的上述字串;
实现多语言的方法很多,列举一二:
1、delphi自带的Resource生成工具
此工具把专案的dfm文件里的所有字串以及pas中定义为
ResourceString的字串列举出来,按不同的语言编译成不同的Resource,专案
编译前先选语言,每种语言编译成一个exe(此处有误, exe中是Base
Language 的资源, 每种语言编译成一个资源Dll, 文件名跟程序同名, 后缀不
是Dll, 而是locale的代码, 如enu, eng, 前两个表示语言, 第三个表示国家)。
这个工具使用很不方便,不是一个完整的解决方案,跟Borland的Midas的
demo一样(TClientDataSet通过ProviderName连接到RemoteDatamodule的
TDataSetProvider,实际开发Erp系统时,谁会放100个TDataSetProvider连
接到100个TDataSet?),只是一个原理尚通的示范。
首先,由于dfm本身也是资源文件的一部分,因此每次修改都要“Update
”,如修改Button1为Button2,如果你忘了,运行时就会
报“找不到资源Button1的错误”(这个好像并没有什么麻烦的);提供的字典
编辑画面中,除了字串,还有Left/Top等资料(这个确实不方便,东西太多了);
字典不能重用,在一个模组翻译了,在第2个模组还要再翻译相同的词。
其次,每种语言一个exe/bpl,如果你的系统是Package切割,bpl也是每种语
言一个,还要小心别把不同语言的bpl组合在了一起,到时候一个画面显示中
文,一个显示德文(有一个可能是乱码)就惨了。
再次,在作bpl组装的系统时,第3方元件如果没有提供多语言的方案,你
就需要修改第3方元件,但一般我们不这样干,因为第3方元件会随时更新,
难道每次人家更新你也再更新人家。
因此,一般都没有人使用Delphi本身提供的这个方案(除了作demo)。
2、Resource dll方式
用单独的ResourceDll,用LoadResString等函数获得翻译字
串,但你要到处写这个函数来一一替换,特别是Form上的字串,噢,会累死人。
字典可以重用。
3、网上讨论很多的ini文件方式
此方法是写个替换的引擎,在运行时从ini文件读取语言字
串来替换画面元件的显示文字。这个方法比第一种进步很多,不需要每种语言
编译一个exe了,只要提供不同的ini文件就好;画面修改时如果ini没有同
步更新也不会出现致命错误,最多就是某个文字没有转换;引擎也提供了字串
转换函数,因此也可以处理主动弹出的消息。这个方法在文件格式上有三种不
同的实现:
(1)、[编号]=[字串]
每个字串从1开始编号,1,2,3,4......,很麻烦,
代码要修改,当然运行时切换语言没问题。
(2)、[元件.属性]=[字串]
这种实现把元件instance一一对应,用RTTI来判断
属性,替换很精确,也可以运行时切换语言。不足之处是,略显呆板,多个元
件相同的字串会多次列出;没有扩展性,表现TListView的Columns等复杂元
件时比较吃力。 (3)、[旧字串]=[新字串]
不管元件的instance,ini是纯粹的语言对照表,或
者叫字典,扩展性、运行时切换语言可能在引擎里。不足之处是不能处理一词
多义。
总的说来,这种方式有很大进步,但为了用ini文件,大家
还要费力的破解64k的限制,更专业的方式是使用自定义的文件格式。
在简单性方面,无疑是这种自定义的转换引擎,[旧字串]
=[新字串]的文件格式来得方便,借助字典管理工具,字典文件可以重复使用,
也可以提供给专业翻译公司翻译。那么剩下的问题在引擎上,如何方便,最好
用户不写一行代码;如何扩展性强,支持任意的第三方元件;如何有弹性,同
一个画面有多种语言的文字,同一个词可以转换成不同的意思......
4、给每个元件类继承一个子类,在子类的Loaded方法里转换文字。由于要处
理的都是叶级元件(虽然TLabel、TPanel都是从TCustomControl来,但不能
只处理TCustomControl),工作量比较大;对旧有程序除了换元件无能为力。
5、为每个元件类注册一个转换函数,引擎遍历Container,为每个元件找
到血源最近的转换函数,调用这个函数转换这个元件的文字。这样可以不必处
理叶结点,只需在恰当的元件层上注册函数;不必改动旧有程序。設計時Form
上只需要放一個轉換元件,這個元件在Loaded后開始掃描Form上的元件,從
for I:=0 to ComponentCount-1或從for i:=0 to ControlCount-1遞歸,找
到一個元件就去查找其血緣最近的註冊函數,然後調用這個函數替換其文字。
因爲註冊函數是額外加上去的,所以不會動到舊的代碼,對任意第方元件都可
以擴展支持,且也不用去修改人家第3方元件的代碼。
我认为第5种方法很优雅,看起来比较干净。用GOF的设计模式来套,
这属于Mediator pattern(中介者模式)。多年前,我们使用一个叫TXPMenu的
元件来获得XP风格的界面,也是感觉到它很干净,一个元件就搞定一切,不用
TLabel换成TFlatLabel,TButton换成我记得《程序员》上
还有文章专门称赞这个元件。但那个元件没有使用中介者模式,不能很好的扩
展对第3方元件的支持。
最后,我们畅想一下,如果我是Borland,如何在Delphi里完整支援
多语言。Delphi提供了一个区块定义的关键字“ResourceString”,在这个区
块定义的字串常量,编译器会把它编译在exe文件的资源区,运行时用
LoadStringA这个Windows API来读取,因此有些外部转换工具可以直接从exe
文件读取这些资源字串,再写入转换后的字串;内嵌的转换引擎也可以拦截这
个API函数来转换文字。但是如果exe里的字串资源化不彻底,就无能为力,
这个不彻底恰恰来自Delphi的DFM文件,Delphi把DFM文件整个作为一项资
源放在exe里,其上的字串就没法决定是否要don`t resource(Delphi源码里
很多常量字串都有这个提示)了。
如果除了string,widestring,ansistring等等这些数据类型,delphi
增加一种数据类型multistring,然后修改vcl元件定义(拜托Borland连同
Unicode一起解决了吧),像n定义成MultiString,对
MultiString类型,有一种专门的处理方法,如类似ResourceString用
LoadString API来处理,每次读取就转换一次,但应该比这个内容更多,比如
要传出instance,然后提供一个全局的ApplicationMulti元件,类似
ApplicationEvent,让外面能捕捉到。至于字典,只能外部用户提供(当然可
以制定一个标准格式让delphi人都可以共享交换)。
此法看起来可行,但还有个效率问题要考虑,(1)每次读取都转换,
对频繁draw的东西效率低;(2)比如一个ToolBar有好多的ToolButton,批
次更新时一般都会用BeginUpdate/EndUpdate,vcl如何告知后代来提高这种效
率。(补记:效率看起来不是问题,对多次字串更改导致频繁draw,其实元件
自己已经会用beginupdate/endupdate处理,外部不会涉及)
版权声明:本文标题:关于DELPHI程序多语言支持的问题 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1721732517a893995.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论