惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
F
Fortinet All Blogs
U
Unit 42
F
Full Disclosure
雷峰网
雷峰网
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
罗磊的独立博客
D
DataBreaches.Net
C
Check Point Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
O
OpenAI News
C
CXSECURITY Database RSS Feed - CXSecurity.com
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Hacker News
The Hacker News
Webroot Blog
Webroot Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
N
News | PayPal Newsroom
P
Proofpoint News Feed
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
MyScale Blog
MyScale Blog
腾讯CDC

博客园 - chance

SQLServer 2008的数据库镜像实施笔记 报表服务器无法打印 人生值得学习的81句话 人件之名言警句 将 BLOB 值写入 SQL Server 时保留资源 Reporting Service 在打印或预览时可能会导致出现空白页的原因 asp.net中如何打印ReportViewer报表 - chance - 博客园 [翻译]使用ASP.NET2.0的ReportViewer查看RDLC报表 基于消息与.Net Remoting的分布式处理架构 C#程序实现动态调用DLL的研究 javascript事件列表解说 js 操作IE游览器 window.external... 微型项目实践感悟 Delphi - 利用INI文件实现界面多语言切换 SQLServer2000的数据库容量是多大? MessageBox对话框 项目经理是这样当的 SQL Server 存储过程的分页方案比拼 动态调用Webservice
DELPHI:利用INI文件实现界面无闪烁多语言切换
chance · 2006-09-10 · via 博客园 - chance

一、引言

越来越多的程序使用了多国语言切换,虽然DELPHI自带多语言包的添加和配置,但是那种方法在切换语言时界面会出现闪烁,而且实现起来很麻烦,这里我介绍给大家的是利用INI文件来读取界面的语种文字,用这种方法,不但简单易行,而且在切换的时候不会出现界面的闪烁。

二、InI文件格式说明

1、语种定义

[Language] //语言

DefaultLang = ChineseGB //界面的默认语种(InI文件名,不带扩展名)

Language = 语种 //菜单标题

ChineseGB = 简体中文 //相应语种文件名

ChineseBig = 繁体中文

English = 英文

⑴ 如果DefaultLang项没有设置,则为语种目录中所寻找到的InI的第一个文件,做为当前界面语种,所有Ini文件都要设置成一样;

⑵ Language为菜单界面上所显示的标题,不设置时,默认为‘Language’;

⑶ 如果Language以下项没有设置,则相应语种的菜单标题为文件名(不带扩展名)。

2、窗体界面定义

[FormName] //需要变换的窗口

.Caption = 演示窗体 //本窗口的属性,表示为:[窗口名].属性名 = 语言

Button1.Hint = 按钮说明 //控件的属性,表示为:控件名.属性名 = 语言

ComboBox1.Items = 简体中文||英文 //其中“||”为分隔符

注:⑴ 在InI文件中每行“//”开始的说明文字不要输入;

⑵ 对继承自TStrings的属性,其各个项目之间需用“||”分隔;

⑶ 设计思想来源于KeyZhang所编写的《多语言切换函数 For Delphi》组件。

三、实现原理

RTTI的基本设计思想是在运行时访问给定数据类型的信息,该类型可以是类也可以是简单的Pascal数据类型(int、string、char等)。本文便是通过RTTI在运行期,获取组件的相应属性,并进行动态设置,达到多语言切换功能。

四、各个函数的定义及实现

1、程序运行时,我们查找语种目录下所有的语言配置文件(*.ini),为了达到这个目的,我编写了如下的函数搜索目录下所有的语言配置文件的文件名,然后将文件名去掉ini扩展名保存返回:

procedure SearchLanguagePack (lngStrings: TStrings);

var

DosError: Integer;

SearchRec: TsearchRec;

begin

DosError := FindFirst (ExtractFilePath (ParamStr (0)) 'Language\*.ini', faAnyFile, SearchRec);

while DosError = 0 do

begin

{ 返回的文件名并去掉末尾的.ini字符 }

lngStrings.Add (ChangeFileExt (SearchRec.Name, ''));

DosError := FindNext (SearchRec);

end;

FindClose(SearchRec);

end;

2、实现界面的多语言切换函数:

function ChangeLanguage (Form: TForm; Lngfile: string): Boolean;

var

INIF: TIniFile; //InI文件

SL: TStringList; // InI文件中读出的相应窗体的信息

TmpSL: TStringList; //将每一行信息,以“=”分隔的信息

Key: string; //“=”左边的信息

Value: string; //“=”右边的信息

CompName: string; //组件名

CompProp: string; //组件属性名

I: Integer;

AComponent: TComponent; //相应组件


procedure Split (S: string; SplitChar: string; R: TStrings); //将字符串分隔

var

P, L, C: Integer;

begin

R.Clear;

L := Length (S);

//确定分隔符的长度,以便删除字符串中的相应字符

C := Length (SplitChar);

//循环分隔字符串

repeat

P := Pos (SplitChar, S);

//如果在字符串中没有找到分隔符,说明为最后一个

if P = 0 then C := 1;

R.Add (Copy (S, P C, L));

Delete(S, P, L);

until P = 0;

end;


//设置相应属性值

procedure SetPropertyValue (AComponent: TComponent; sProperty, sValue: string);

var

PropInfo: PPropInfo;

AStrings: TStringList;

begin

if AComponent <> nil then

begin

PropInfo := GetPropInfo(AComponent, sProperty);

if PropInfo <> nil then

begin

case PropInfo.PropType^.Kind of

tkString, tkLString: //字符串属性

SetStrProp(AComponent, PropInfo, sValue);

tkInteger: //序数属性

SetOrdProp(AComponent, PropInfo, StrToInt(sValue));

else

if PropInfo.PropType^.Name = 'TStrings' then //列表属性

begin

AStrings := TStringList.Create;

try

Split (sValue, '||', AStrings);

SetOrdProp (AComponent, PropInfo, Integer (AStrings));

finally

AStrings.Free;

end; // end try

end; // end if PropInfo.PropType^.Name = 'TStrings'

end; // end case

end; // end if PropInfo <> nil

end; // end if AComponent <> ni

end;


begin

Result := True;

SL := TStringList.Create;

try

INIF := TIniFile.Create (LngFile); // 打开语种初始化文件

try

INIF.ReadSectionValues (Form.Name, SL); // 读取相应窗体信息

finally

INIF.Free;

end;

TmpSL := TStringList.Create;

try

for I := 0 to SL.Count - 1 do // 分隔每一行信息

begin

Split (SL.Strings[I], '=', TmpSL);

Key := Trim (TmpSL.Strings[1]);

CompName := Trim (Copy(Key, 1, Pos ('.', Key) - 1));

if CompName = '' then // 确定组件

AComponent := Form

else

AComponent := Form.FindComponent (CompName);

CompProp := Trim (Copy (Key, Pos ('.', Key) 1, Length (Key)));

Value := Trim (TmpSL.Strings[0]);

SetPropertyValue (AComponent, CompProp, Value); // 设置属性

end;

finally

TmpSL.Free;

end;

finally

SL.Free;

end;

end;

3、在Form显示的事件中添加代码,将目录下所有的语言文件名加入菜单项:

procedure TForm1.FormShow(Sender: TObject);

var

lngStrings, tmpStrings: TStrings;

I: Integer;

lngMenu: TMenuItem;

Filename: string;

IniF: TIniFile;

begin

lngStrings := TString.Create;

try

SearchLanguagePack (lngStrings); // 查找语种文件

if lngStrings.Count = 0 then // 没有找到语种文件直接退出

exit;

Filename := ExtractFilePath (ParamStr (0)) 'Language\' lngStrings.Strings[0] '.ini';

IniF := TIniFile.Create (Filename);

Filename := IniF.ReadString('Language', ' DefaultLang ', '');// 设置为默认语种

if Filename <> '' then

begin

Filename := ExtractFilePath (ParamStr (0)) 'Language\' Filename '.ini';

if FileExists (Filename) then

begin

IniF.Free;

IniF := TIniFile.Create (Filename);

end;

end;

lngMenu := TMenuItem.Create (self);

lngMenu.Name := 'Language';

lngMenu.Caption := IniF.ReadString('Language', 'Language', 'Language');

MainMenu1.Items[MainMenu1.Items. Count].Add (lngMenu); // 语种菜单放到主菜单的最后

tmpStrings := TString.Create;

try

for I := 0 to lngStrings.Count – 1 do // 动态创建菜单项

begin

lngMenu := TMenuItem.Create (self);

lngMenu.Name := lngStrings.Strings[I]; // 将菜单项的名称赋予文件名

lngMenu.Caption := IniF.ReadString('Language', lngStrings.Strings[I], lngStrings.Strings[I]');

lngMenu.onClick := lngMenuClick; // 菜单事件

Language.Insert (Language.Items.Count, lngMenu); // 添加到语种菜单的最后一项上

end;

finally

tmpStrings.Free;

end;

finally

lngStrings.Free;

end;

ChangeLanguage (Form1, IniF); // 改变界面语种

IniF.Free;

end;

4、窗体中语种菜单的事件响应:

procedure TForm1.lngMenuClick (Sender: TObject);

var

Filename: string;

begin

if Sender is TMenuItem then

begin

// 确定语种文件,菜单项名称为语种文件名

Filename := ExtractFilePath (ParamStr (0)) 'Language\' (Sender as TMenuItem).Name '.ini';

ChangeLanguage (Form1, Filename);

end;

end;

五、结束语

通过本次学习,让读者初步了解Delphi编程中运行时类型信息的应用技巧,同时掌握如何在程序运行时动态创建菜单。