求救一下BCB开发多语言支持的问题! ( 积分: 100 )

  • 主题发起人 haoshuaioo
  • 开始时间
H

haoshuaioo

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟在网上搜索了N天了,关于多语言开发有很多说法,
最后我是这么做的:创建一个 "Resource DLL Wizard" (和Project->Language->Add...同样性质)添加语言(中文(台湾)、中文(中国)),也生成了对应目录和文件(Project1.CHS,Project1.CHT)但是我把程序放在繁体环境下运行还是无法转换语言。
请教各位朋友有什么方法可以用一个工程编译出2种不同语言的程序。底分100+,不够我再给!
补充:INI 的方法就不要了 谢谢!!
 
小弟在网上搜索了N天了,关于多语言开发有很多说法,
最后我是这么做的:创建一个 "Resource DLL Wizard" (和Project->Language->Add...同样性质)添加语言(中文(台湾)、中文(中国)),也生成了对应目录和文件(Project1.CHS,Project1.CHT)但是我把程序放在繁体环境下运行还是无法转换语言。
请教各位朋友有什么方法可以用一个工程编译出2种不同语言的程序。底分100+,不够我再给!
补充:INI 的方法就不要了 谢谢!!
 
转载
多语言界面的实现
用 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 用来重新刷新窗体和控件以保证同步。
/// 文件名 :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 <> nildo

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 - 1do

begin

Form := Screen.Forms;
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 编译所有的文件 ,这样才能保证你的程序正常运行。
所有的源代码可以到 <http://kingron.myetang.com/soft/MultiLan.rar> 下载。
后记 : 其实用 INI 文件也可以实现多语言界面的切换 ,好处是可以方便大家随时添加不同的语言文件 ,但是 ,对于一个大型的程序来说 ,用 INI 是不现实的。用 INI 实现语言界面的程序 ,大家可以到 <http://kingron.myetang.com/soft/winupx.rar> 下载源代码和测试程序。
 
利用 IE 的多语言动态库实现程序中多语言的自由切换
All Systems (Win 95+ and WinNT4+) with MS Internet Explorer 4 and newer have a library named mlang.dll in the Winnt/System32 directory. Usually you can tell Delphi to simply import these COM Libraries. This one however, Delphi did not. I started to convert the "most wanted" interface for myself. The results I present you here.

First I give you the code for the conversion unit, that allows you simply convert any text from code page interpretation into another one. Following I will shortly discuss the code and give you a sample of how to use it.

uCodePageConverter
==================

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Unit Name : uCodePageConverter
* Autor : Daniel Wischnewski
* Copyright : Copyright &amp;copy;
2002 by gate(n)etwork. All Right Reserved.
* Urheber : Daniel Wischnewski
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}

unit uCodePageConverter;

interface

uses
Windows;

const
IID_MLangConvertCharset: TGUID = '{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}';
CLASS_MLangConvertCharset :TGUID = '{D66D6F99-CDAA-11D0-B822-00C04FC9B31F}';

type
tagMLCONVCHARF = DWORD;

const
MLCONVCHARF_AUTODETECT: tagMLCONVCHARF = 1;
MLCONVCHARF_ENTITIZE : tagMLCONVCHARF = 2;

type
tagCODEPAGE = UINT;

const
CODEPAGE_Thai : tagCODEPAGE = 0874;
CODEPAGE_Japanese : tagCODEPAGE = 0932;
CODEPAGE_Chinese_PRC : tagCODEPAGE = 0936;
CODEPAGE_Korean : tagCODEPAGE = 0949;
CODEPAGE_Chinese_Taiwan : tagCODEPAGE = 0950;
CODEPAGE_UniCode : tagCODEPAGE = 1200;
CODEPAGE_Windows_31_EastEurope : tagCODEPAGE = 1250;
CODEPAGE_Windows_31_Cyrillic : tagCODEPAGE = 1251;
CODEPAGE_Windows_31_Latin1 : tagCODEPAGE = 1252;
CODEPAGE_Windows_31_Greek : tagCODEPAGE = 1253;
CODEPAGE_Windows_31_Turkish : tagCODEPAGE = 1254;
CODEPAGE_Hebrew : tagCODEPAGE = 1255;
CODEPAGE_Arabic : tagCODEPAGE = 1256;
CODEPAGE_Baltic : tagCODEPAGE = 1257;

type
IMLangConvertCharset = interface
['{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}']
function Initialize(
uiSrcCodePage: tagCODEPAGE;
uiDstCodePage: tagCODEPAGE;
dwProperty: tagMLCONVCHARF
): HResult;
stdcall;
function GetSourceCodePage(
out puiSrcCodePage: tagCODEPAGE
): HResult;
stdcall;
function GetDestinationCodePage(
out puiDstCodePage: tagCODEPAGE
): HResult;
stdcall;
function GetProperty(out pdwProperty: tagMLCONVCHARF): HResult;
stdcall;
functiondo
Conversion(
pSrcStr: PChar;
pcSrcSize: PUINT;
pDstStr: PChar;
pcDstSize: PUINT
): HResult;
stdcall;
functiondo
ConversionToUnicode(
pSrcStr: PChar;
pcSrcSize: PUINT;
pDstStr: PWChar;
pcDstSize: PUINT
): HResult;
stdcall;
functiondo
ConversionFromUnicode(
pSrcStr: PWChar;
pcSrcSize: PUINT;
pDstStr: PChar;
pcDstSize: PUINT
): HResult;
stdcall;
end;

CoMLangConvertCharset = class
class function Create: IMLangConvertCharset;
class function CreateRemote(const MachineName: string): IMLangConvertCharset;
end;

implementation

uses
ComObj;

{ CoMLangConvertCharset }

class function CoMLangConvertCharset.Create: IMLangConvertCharset;
begin

Result := CreateComObject(CLASS_MLangConvertCharset) as IMLangConvertCharset;
end;

class function CoMLangConvertCharset.CreateRemote(
const MachineName: string
): IMLangConvertCharset;
begin

Result := CreateRemoteComObject(
MachineName, CLASS_MLangConvertCharset
) as IMLangConvertCharset;
end;

end.


As you can see, I did translate only one of the many interfaces, however this one is the most efficient (according to Microsoft) and willdo
the job. Further I added some constants to simplify the task of finding the most important values.

When using this unit todo
any code page conersions you must not forget, that the both code pages (source and destination) must be installed and supported on the computer thatdo
es the translation. OIn the computer that is going to show the result only the destination code page must be installed and supported.

To test the unit simple create a form with a memo and a button. Add the following code to the buttons OnClick event. (Do not forget to add the conversion unit to the uses clause!)

SAMPLE
======

procedure TForm1.Button1Click(Sender: TObject);
var
Conv: IMLangConvertCharset;
Source: PWChar;
Dest: PChar;
SourceSize, DestSize: UINT;
begin

// connect to MS multi-language lib
Conv := CoMLangConvertCharset.Create;
// initialize UniCode Translation to Japanese
Conv.Initialize(CODEPAGE_UniCode, CODEPAGE_Japanese, MLCONVCHARF_ENTITIZE);
// load source (from memo)
Source := PWChar(WideString(Memo1.Text));
SourceSize := Succ(Length(Memo1.Text));
// prepare destination
DestSize := 0;
// lets calculate size needed
Conv.DoConversionFromUnicode(Source, @SourceSize, nil, @DestSize);
// reserve memory
GetMem(Dest, DestSize);
try
// convert
Conv.DoConversionFromUnicode(Source, @SourceSize, Dest, @DestSize);
// show
Memo1.Text := Dest;
finally
// free memory
FreeMem(Dest);
end;

end;
 
我用楼上的方法生成了 CHS(中文简体) 和 CHT(中文繁体) 这两个目录和对应的文件,我想请教一下如何使用这2个生成的文件 来实现语言的切换呢? 如果有朋友可以加我QQ聊的话我愿意单独给分, 谢谢了!
QQ:80848088
 
如果要自动切换语言 ,只要在 FormCreate 事件中添加如下代码即可 :
if LoadNewResourceModule(SysLocale.DefaultLCID) <> 0 then

ReinitializeForms;
说明一点 : 在程序完成的时候 ,你应该用 Luanguages 子菜单的 Update Resources DLL 功能更新所有的窗体和代码 ,然后用 Build All Project 编译所有的文件 ,这样才能保证你的程序正常运行。
 
Beyondbill 你好。 我在BCB 中找不到这些函数
LoadNewResourceModule 和 ReinitializeForms 。
应该如何做?
 
对于信息框的多语言化,好象这个办法不是很方便,字符串也不集中,是不是使用ini文件较好,动态加载。
 
多人接受答案了。
 

Similar threads

顶部