是否可行:奇思异想的简繁双语程序的实现方式,是否可行,如何办到 [300+](300分)

  • 主题发起人 主题发起人 rogue
  • 开始时间 开始时间
R

rogue

Unregistered / Unconfirmed
GUEST, unregistred user!
大家好,
本人一直为找到一个好的开发简繁双语的程序方法,而头痛!
普通的用INI或是资源文件的方式,真得是烦透了
因为大家都知道,开发的ERP有几百个FORM,都通去做这样的翻译,真是费时费力啊

正题:
我是想把简繁转换分成两个部分
一个是界面上的转换,一个是代码中的转换!
代码中的转换已经简单实现,即是在每一个出现字符串的位置上,都冠以一个转换函数
这个函数的功能就是判断当前是否为繁体系统,如果是转换,否则不转换

而界面的转换部分,未实现,大概的思路是:
在FORM装入'*.DFM'资源之前,对DFM资源进行简繁转换!
大家都知道,DFM文件本身也可以变成文本格式,如果能在装入DFM文件的代码上改动一下
让它在装入之前做转换,然后再把 字体啊,字符集啊,做些转换,那就非常通用了

如果不能正规的实现,改系统的PAS也无所谓啊,这样一来,你的所有程序都可以简繁转换了
哈,想着就挺美

但是小弟不才,翻了一会,forms.pas,竟然没有找到装入DFM的代码所在,
所以在此请高手出山了.......

为了双语程序,兄弟们,帮我顶一顶啊
 
帮你顶。收藏,学习...
 
楼主好想法.
可惜偶不是高手.
帮你顶.
一定要顶出高手来.
 
对此问题感兴趣的人咋就这少哩
没有高人来指点一下吗????

要是俺自已搞定了,这里帮顶的人都会有一份源码的,
不帮忙的人就没有了
 
妈的,今天早上电脑和手机都被人偷了
心里不爽,心情不好!

问题也没有解决
 
我刚刚在做一个简繁的系统

可以加qq 29119380交流一下
 
你有没有考虑过,如果哪天要你软件可以中英文切换你怎么办?
呵呵 我的软件就是用INI文件实现,FORM也多 很方便啊,没有任何问题啊
 
也许是你的软件比较大,我的软件现在确定不可能有英文版
简繁不能共用,所以不用切换

所以.我的应用要求简单明了,想用最简单的办法实现
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3524443
 
楼主高手也
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3185897
 
前段时间刚好给台湾佬做了个双语的程序,提供思路楼主参考:
1、无论代码内的字符还是窗体上的字符,统一由简繁函数转换。
2、程序初始化时检测系统为繁体还是简体而相应显示字体。
3、转换时对当前窗体列举所有控件翻译控件的相应Caption或Text,其他窗体显示前先转换字体。
4、改造MessageDlg,根据当前的字体设置而显示相应字体。
 
这样做的一个优点是对原来单语的程序不需要做太大改动就可以实现双语,
给出主要的单元,部分代码网上获取,部分原创,希望对大家有帮助。

unit Cm_Language;

interface

uses
Windows, Forms, Classes, StdCtrls, Menus, Controls, ExtCtrls;

type
TLanguage = (
tNone,
tEnglish,
tGBChs,
tBig5,
tGBCht
);

TCaptionControl = class(TControl)
public
property Caption;
end;

function GetSysDefaultLg: TLanguage;
function Big5ToGB(BIG5Str: string): AnsiString;
function GBToBig5(GBStr: string): AnsiString;
function GBChtToChs(GBStr: string): AnsiString;
function GBChsToCht(GBStr: string): AnsiString;

function TranLanguage(SrcStr: string; SrcLan, DesLan: TLanguage): string;
procedure SetFormLanguage(sForm: TForm; SrcLan, DesLan: TLanguage);

implementation

//获取操作系统默认语言
// -1、无效 0、英语 1、简体中文 3、繁体中文
function GetSysDefaultLg: TLanguage;
var
LangID: Integer;
begin
Result := tNone;
LangID := GetSystemDefaultLangID;
if LangID = ((SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH) then
Result := tEnglish //美国英语
else if LangID = ((SUBLANG_CHINESE_SIMPLIFIED shl 10) or LANG_CHINESE) then
Result := tGBChs //简体中文
else if LangID = ((SUBLANG_CHINESE_TRADITIONAL shl 10) or LANG_CHINESE) then
Result := tBig5; //繁体中文
end;

function Big5ToGB(BIG5Str: string): AnsiString;
{进行big5转GB内码}
var
Len: Integer;
pBIG5Char: PChar;
pGBCHSChar: PChar;
pGBCHTChar: PChar;
pUniCodeChar: PWideChar;
begin
//String -> PChar
pBIG5Char := PChar(BIG5Str);
Len := MultiByteToWideChar(950, 0, pBIG5Char, -1, nil, 0);
GetMem(pUniCodeChar, Len * 2);
ZeroMemory(pUniCodeChar, Len * 2);
//Big5 -> UniCode
MultiByteToWideChar(950, 0, pBIG5Char, -1, pUniCodeChar, Len);
Len := WideCharToMultiByte(936, 0, pUniCodeChar, -1, nil, 0, nil, nil);
GetMem(pGBCHTChar, Len * 2);
GetMem(pGBCHSChar, Len * 2);
ZeroMemory(pGBCHTChar, Len * 2);
ZeroMemory(pGBCHSChar, Len * 2);
//UniCode->GB CHT
WideCharToMultiByte(936, 0, pUniCodeChar, -1, pGBCHTChar, Len, nil, nil);
//GB CHT -> GB CHS
LCMapString($804, LCMAP_SIMPLIFIED_CHINESE, pGBCHTChar, -1, pGBCHSChar, Len);
Result := string(pGBCHSChar);
FreeMem(pGBCHTChar);
FreeMem(pGBCHSChar);
FreeMem(pUniCodeChar);
end;

function GBToBig5(GBStr: string): AnsiString;
{进行GB转BIG5内码}
var
Len: Integer;
pGBCHTChar: PChar;
pGBCHSChar: PChar;
pUniCodeChar: PWideChar;
pBIG5Char: PChar;
begin
pGBCHSChar := PChar(GBStr);
Len := MultiByteToWideChar(936, 0, pGBCHSChar, -1, nil, 0);
GetMem(pGBCHTChar, Len * 2 + 1);
ZeroMemory(pGBCHTChar, Len * 2 + 1);
//GB CHS -> GB CHT
LCMapString($804, LCMAP_TRADITIONAL_CHINESE, pGBCHSChar, -1, pGBCHTChar, Len * 2);
GetMem(pUniCodeChar, Len * 2);
ZeroMemory(pUniCodeChar, Len * 2);
//GB CHT -> UniCode
MultiByteToWideChar(936, 0, pGBCHTChar, -1, pUniCodeChar, Len * 2);
Len := WideCharToMultiByte(950, 0, pUniCodeChar, -1, nil, 0, nil, nil);
GetMem(pBIG5Char, Len);
ZeroMemory(pBIG5Char, Len);
//UniCode -> Big5
WideCharToMultiByte(950, 0, pUniCodeChar, -1, pBIG5Char, Len, nil, nil);
Result := string(pBIG5Char);
FreeMem(pBIG5Char);
FreeMem(pGBCHTChar);
FreeMem(pUniCodeChar);
end;

function GBChtToChs(GBStr: string): AnsiString;
{进行GBK繁体转简体}
var
Len: Integer;
pGBCHTChar: PChar;
pGBCHSChar: PChar;
begin
pGBCHTChar := PChar(GBStr);
Len := MultiByteToWideChar(936, 0, pGBCHTChar, -1, nil, 0);
GetMem(pGBCHSChar, Len * 2 + 1);
ZeroMemory(pGBCHSChar, Len * 2 + 1);
//GB CHS -> GB CHT
LCMapString($804, LCMAP_SIMPLIFIED_CHINESE, pGBCHTChar, -1, pGBCHSChar, Len * 2);
Result := string(pGBChsChar);
//FreeMem(pGBCHTChar);
FreeMem(pGBCHSChar);
end;

function GBChsToCht(GBStr: string): AnsiString;
{进行GBK简体转繁体}
var
Len: Integer;
pGBCHTChar: PChar;
pGBCHSChar: PChar;
begin
pGBCHSChar := PChar(GBStr);
Len := MultiByteToWideChar(936, 0, pGBCHSChar, -1, nil, 0);
GetMem(pGBCHTChar, Len * 2 + 1);
ZeroMemory(pGBCHTChar, Len * 2 + 1);
//GB CHS -> GB CHT
LCMapString($804, LCMAP_TRADITIONAL_CHINESE, pGBCHSChar, -1, pGBCHTChar, Len * 2);
Result := string(pGBCHTChar);
FreeMem(pGBCHTChar);
//FreeMem(pGBCHSChar);
end;

procedure SetFormLanguage(sForm: TForm; SrcLan, DesLan: TLanguage);
var
TmpComp: TComponent;
TmpControl: TControl;
i, j, Len, Index: Integer;
TmpCap: string;
begin
with sForm do
begin
for i := 0 to ComponentCount - 1 do
begin
TmpComp := Components;
//控件Caption
if TmpComp is TControl then
begin
TmpControl := TmpComp as TControl;
Len := TmpControl.GetTextLen;
if Len <> 0 then
begin
SetString(TmpCap, PChar(nil), Len);
TmpControl.GetTextBuf(Pointer(TmpCap), Len + 1);
TmpControl.SetTextBuf(PChar(TranLanguage(TmpCap, SrcLan, DesLan)));
end;
end;
//其他情况
if TmpComp is TComboBox then
begin
with TmpComp as TComboBox do
begin
Index := ItemIndex;
for j := 0 to Items.Count - 1 do
Items[j] := TranLanguage(Items[j], SrcLan, DesLan);
ItemIndex := Index;
end;
end;

if TmpComp is TLabeledEdit then
begin
with TmpComp as TLabeledEdit do
EditLabel.Caption := TranLanguage(EditLabel.Caption, SrcLan, DesLan);
end;
end;
end;
end;

function TranLanguage(SrcStr: string; SrcLan, DesLan: TLanguage): string;
begin
Result := '';
if SrcLan = DesLan then
begin
Result := SrcStr;
exit;
end;

case SrcLan of
tNone, tEnglish:
exit;
tGBChs:
case DesLan of
tGBCht:
Result := GBChsToCht(SrcStr);
tBig5:
Result := GBToBig5(SrcStr);
end;
tGBCht:
case DesLan of
tGBChs:
Result := GBChtToChs(SrcStr);
tBig5:
Result := GBToBig5(SrcStr);
end;
tBig5:
case DesLan of
tGBChs:
Result := Big5ToGB(SrcStr);
tGBCht:
Result := GBChsToCht(Big5ToGB(SrcStr));
end;
end;
end;

end.

function Mt_MessageDlg(const Msg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;
begin
MessageDlg(TranLanguage(Msg, tGBChs, Language_Select), DlgType, Buttons, HelpCtx);
end;
 
好贴!谢谢楼上的
 
是啊,楼上的长贴者
这种方法俺也想过,用过,有几点不好的地方

1.要列举所有控件,性能上有问题:这个是真的,当一个非常复杂的窗口做遍历时,有点慢
2.每个控件的属性都是不同的,不能做到统一,有的控件需要繁译的属性在好几层子属性中,你咋整 如: dbgrideh.columneh.title

而如果我的方法能够实现,将会在LOAD资源后一次性转换,性能开销小,而且还不会有遗漏.

请大家讨论我说的是否可行

昨天看了好久的VCL SOURCE,找到一个疑似核心函数叫: InternalReadComponentRes这个好象就是装入资源文件的全局函数,不知道能不能在这个方法上做手脚


function InternalReadComponentRes(const ResName: string; HInst: THandle; var Instance: TComponent): Boolean;
var
HRsrc: THandle;
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;
with TResourceStream.Create(HInst, ResName, RT_RCDATA) do
//这句已经读出来了,在这里能做转换就好了,对stream进行处理
try
Instance := ReadComponent(Instance);
finally
Free;
end;
Result := True;
end;
 
to 楼主:
不错,这样转换的最麻烦的地方是要找出所有控件可能有字符的属性,对于复杂的界面有点费事,不过对于用INI那样的方法已经简化很多了,呵呵。
速度上应该不会慢,一个最最复杂的界面也只不过几百个可视控件吧,对几百个对象的字符属性进行替换,都在内存中进行,不需要读文件什么的,应该不太费资源的,而且只是在创建窗体时需要替换字符。
楼主的方法有可行性,主要难点在读出资源流中的字符并进行替换,替换不难,关键是找出需要替换的字符。
还有一个问题,如何进行语种切换,有可能需要重新运行程序才能实现。
 
顶啊!
解决了大家都实用哩
 
不用這麼麻煩吧, 用個簡繁轉換軟件(比如綺達)把工程轉換一下, 重新分別編譯成簡繁兩個exe文件.
再在程序中判斷系統字符集自動切換程序就可以了.
 
直接用簡繁部分是可以的,但不會很理想。
代碼部分實現很簡單,用了轉換程式就可以。
難就難在介面上和報表兩部分。
這主要是簡繁內碼不一樣,所以直接用轉時會出亂碼。
我目前做了三種形式,Ini 文件/資源文件/直接代碼簡繁體,不管那種都會累人。不過也只能這樣做。
 
使用ini文件做比较方便,但很大的缺点是不能隐藏信息,比如公司名称之类的,可能只能做成图片加载。。。
 
后退
顶部