Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4923390
  • 博文数量: 1169
  • 博客积分: 12961
  • 博客等级: 上将
  • 技术积分: 14058
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-09 11:25
  • 认证徽章:
个人简介

偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.

文章分类

全部博文(1169)

文章存档

2019年(145)

2018年(81)

2017年(80)

2016年(70)

2015年(52)

2014年(41)

2013年(51)

2012年(85)

2011年(45)

2010年(231)

2009年(288)

分类: WINDOWS

2011-06-19 10:56:45

随着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用来重新刷新窗体和控件以保证同步。

///文件名:MaltiLan.pas

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 TAsInheritedReader.ReadPrefix(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 CurModule.Instance = HInstance then

    begin

      if CurModule.ResInstance <> CurModule.Instance then

        FreeLibrary(CurModule.ResInstance);

      CurModule.ResInstance := NewInstance;

      Result := NewInstance;

      Exit;

    end;

    CurModule := CurModule.Next;

  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 := TResourceStream.Create(HInst, ResName, RT_RCDATA);

  try

    AsInheritedReader := TAsInheritedReader.Create(ResStream, 4096);

    try

      Instance := AsInheritedReader.ReadRootComponent(Instance);

    finally

      AsInheritedReader.Free;

    end;

  finally

    ResStream.Free;

  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(ClassType.ClassParent);

    Result := InternalReloadComponentRes(ClassType.ClassName, FindResourceHInstance(

      FindClassHInstance(ClassType)), Instance) or Result;

  end;



begin

  Result := InitComponent(Instance.ClassType);

end;



procedure ReinitializeForms;

var

  Count: Integer;

  I: Integer;

  Form: TForm;

begin

  Count := Screen.FormCount;

  for I := 0 to Count - 1 do

  begin

    Form := Screen.Forms[I];

    ReloadInheritedComponent(Form, TForm);

  end;

end;



end.

测试程序窗体单元文件如下:

///单元文件名:unit1.pas

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 TForm1.Exit1Click(Sender: TObject);

begin

  Close;

end;



procedure TForm1.Chese1Click(Sender: TObject);

begin

  if LoadNewResourceModule(CHINESE) <> 0 then

    ReinitializeForms;

end;



procedure TForm1.English1Click(Sender: TObject);

begin

  if LoadNewResourceModule(ENGLISH) <> 0 then

    ReinitializeForms;

end;



end.

如果要自动切换语言,只要在FormCreate事件中添加如下代码即可:

  if LoadNewResourceModule(SysLocale.DefaultLCID) <> 0 then

    ReinitializeForms;

说明一点:在程序完成的时候,你应该用Luanguages子菜单的Update Resources DLL功能更新所有的窗体和代码,然后用Build All Project编译所有的文件,这样才能保证你的程序正常运行。

所有的源代码可以到下载。

后记:其实用INI文件也可以实现多语言界面的切换,好处是可以方便大家随时添加不同的语言文件,但是,对于一个大型的程序来说,用INI是不现实的。用INI实现语言界面的程序,大家可以到下载源代码和测试程序
阅读(3714) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册