基本原理:Delphi 的VCL是通过TReader 类来对控件进行读写的,再加上VCL的源码开放性,通过修改可以

它,使界面的资源从ini文件中读取:

1、在classes.pas中添加:

{!$IFDEF _MULTILANG}
function CL(const s: WideString): String;
begin
{$IFDEF _MULTILANG}
  Result := ConvertLanguage(s);
{$ELSE}
  Result := s;
{$ENDIF}
end;
{!$ENDIF}

2、TReader.ReadString:

function TReader.ReadString: string;
var
  L: Integer;
begin
  if NextValue in [vaWString, vaUTF8String] then
    Result := ReadWideString
  else
  begin
    L := 0;
    case ReadValue of
      vaString:
        Read(L, SizeOf(Byte));
      vaLString:
        Read(L, SizeOf(Integer));
    else
      PropValueError;
    end;
    SetLength(Result, L);
    Read(Pointer(Result)^, L);
    {$IFDEF _MULTILANG}
      Result := ConvertLanguage(Result);
    {$ENDIF}        
  end;
end;

3、TReader.ReadString:

function TReader.ReadWideString: WideString;
var
  L: Integer;
  Temp: UTF8String;
begin
  if NextValue in [vaString, vaLString] then
    Result := ReadString
  else
  begin
    L := 0;
    case ReadValue of
      vaWString:
        begin
          Read(L, SizeOf(Integer));
          SetLength(Result, L);
          Read(Pointer(Result)^, L * 2);
        end;
      vaUTF8String:
        begin
          Read(L, SizeOf(Integer));
          SetLength(Temp, L);
          Read(Pointer(Temp)^, L);
          Result := Utf8Decode(Temp);
        end;
    else                      
      PropValueError;
    end; 

    {$IFDEF _MULTILANG}
      Result := ConvertLanguage(Result);
    {$ENDIF}
  end;
end;

4.MultiLanguage.pas代码:


{$DEFINE _WRITE_LANGFILE}

unit MultiLang;

interface

const
  cLangFileName = 'language.ini';

  function ConvertLanguage(const s: WideString): WideString;

implementation

uses
  windows, SysUtils, classes, IniFiles;

const
  UTF8TextFileTag: string = #$EF#$BB#$BF;

var
  ClSrLst: TStringList=nil;
  ClDtLst: TStringList=nil;
  ClWrLst: TStringList=nil;

  FIsModify:Boolean=False;

function WideStrPos(const Patt, SearchStr: WideString): integer;
var
  PP, PS, PsBk: PWideChar;
  isHas: Boolean;
begin
  Result := 0;
  PP := PWideChar(Patt);
  PS := PWideChar(SearchStr);
  PsBk := PS;
  isHas := False;
  if (PP<>nil) and (PP^<>#0) and
     (PS <> nil) and (PS^ <> #0) then
  begin
    while (PS^ <> #0) do
    begin
      if(PS^ = PP^) then
      begin
        if not isHas then
        begin
          isHas := True;
          PsBk := PS;
        end;
        inc(PP);
        if PP^ = #0 then
        begin
          Result := (PsBk - PWideChar(SearChStr)) + 1;
          Break;
        end;
      end
      else if isHas then
      begin
        isHas := False;
        PP := PWideChar(Patt);
        PS := PsBk + 1;
      end;
      inc(PS);
    end;
  end;
end;

function WideStrCopy(const s: WideString; Offset, Len: Integer): WideString;
var
  P: PWideChar;
  aLen: Integer;
  i: integer;
begin
  Result := '';
  if s <> '' then
  begin
    aLen := Length(s) - Offset + 1;
    if Len < aLen then
      aLen := Len; 

    if aLen > 0 then
    begin
      SetLength(Result, aLen);
      p := PWideChar(s) + Offset - 1;
      for i := 1 to aLen do
      begin
        Result[i] := p^;
        inc(p);
      end;
    end;
  end;
end;

function WideStrReplace(const S, OldPattern, NewPattern: WideString;
  Flags: TReplaceFlags): WideString;
var
  SearchStr, Patt, NewStr: WideString;
  Offset: Integer;
begin
  if rfIgnoreCase in Flags then
  begin
    SearchStr := WideUpperCase(S);
    Patt := WideUpperCase(OldPattern);
  end else
  begin
    SearchStr := S;
    Patt := OldPattern;
  end;
  NewStr := S;
  Result := '';
  while SearchStr <> '' do
  begin
    Offset := WideStrPos(Patt, SearchStr);
    if Offset = 0 then
    begin
      Result := Result + NewStr;
      Break;
    end;
    Result := Result + WideStrCopy(NewStr, 1, Offset - 1) + NewPattern;
    NewStr := WideStrCopy(NewStr, Offset + Length(OldPattern), MaxInt);
    if not (rfReplaceAll in Flags) then
    begin
      Result := Result + NewStr;
      Break;
    end;
    SearchStr := WideStrCopy(SearchStr, Offset + Length(Patt), MaxInt);
  end;
end;  

function ECode(s: WideString): WideString;
var
  tmps: widestring; 
begin
  tmps := #13#10;
  s := WideStrReplace(s, tmps, '{#13#10}', [rfReplaceAll]);

  tmps := #10;  
  s := WideStrReplace(s, tmps, '{#13#10}', [rfReplaceAll]);

  s := WideStrReplace(s, '=',    '{#61}'   , [rfReplaceAll]);
  s := WideStrReplace(s, '"',    '{#34}'   , [rfReplaceAll]);
  s := WideStrReplace(s, '''',   '{#39}'   , [rfReplaceAll]);
  Result := s; 
end;

function DCode(s: WideString): WideString;
var
  tmps: widestring;
begin
  tmps := #13#10;
  s := WideStrReplace(s, '{#13#10}', tmps, [rfReplaceAll]);

  s := WideStrReplace(s, '{#61}'   ,    '=', [rfReplaceAll]);
  s := WideStrReplace(s, '{#34}'   ,    '"', [rfReplaceAll]);
  s := WideStrReplace(s, '{#39}'   ,    '''', [rfReplaceAll]);
  Result := s;
end;

function InternalCl(const Sr: WideString; var Dt: WideString): Boolean;
var
  Index: Integer;
begin
  Result := False;
  if ClSrLst.Find(Utf8Encode(ECode(Sr)), Index) then
  begin
    Index := Integer(ClSrLst.Objects[Index]);
    if Index > 0 then
    begin
      Dt := DCode(Utf8Decode(ClDtLst[Index]));
      Result := True;
    end;
  end;                     
end;

procedure Addc(s: WideString);
var
  Utf8s: UTF8String;
begin
  Utf8s := Utf8Encode(ECode(s)) + '='; 
  if clWrLst.IndexOf(s) = -1 then
  begin
    FIsModify := True;
    ClWrLst.Add(Utf8s);
  end;
end;

function ConvertLanguage(const s: WideString): WideString;
var
  Dt: WideString;
begin
  if InternalCl(s, Dt) then
  begin
    if Dt <> '' then
      Result := Dt
    else
      Result := s;
  end
  else
  begin
  {$IFDEF _WRITE_LANGFILE}
    Addc(s);
  {$ENDIF}
    result := s;
  end;
end;

procedure init;
var
  i, ci, Index: integer;
begin
  ClDtLst.Add('no use');
  for i := 0 to ClSrLst.Count - 1 do
  begin
    ci := Pos('=', ClSrLst[i]);
    if ci = 0 then
      Index := ClDtLst.Add('')
    else
      Index := ClDtLst.Add(Copy(ClSrLst[i], ci + 1, High(Integer)));

    ClSrLst.Objects[i] := TObject(Index);
    ClSrLst[i] := Copy(ClSrLst[i], 1, ci - 1);
  end;
end;

initialization

  ClSrLst := TStringList.Create;
  if FileExists(ExtractFilePath(ParamStr(0)) + cLangFileName) then
    ClSrLst.LoadFromFile(ExtractFilePath(ParamStr(0)) + cLangFileName);

  if Copy(ClSrLst.Text, 1, Length(UTF8TextFileTag)) = UTF8TextFileTag then
    ClSrLst.Text := Copy(ClSrLst.Text, Length(UTF8TextFileTag) + 1, high(Integer));

  ClDtLst := TStringList.Create;
  ClWrLst := TStringList.Create;
  ClWrLst.Sorted := True;

  init;

  ClSrLst.Sorted := True;

finalization

{$IFDEF _WRITE_LANGFILE}
  if FIsModify then
  begin
    try
      if FileExists(ExtractFilePath(ParamStr(0)) + cLangFileName) then
        ClSrLst.LoadFromFile(ExtractFilePath(ParamStr(0)) + cLangFileName);
    except
      On EFOpenError do;
    end;
    ClWrLst.AddStrings(ClSrLst);
    try
      ClWrLst.SaveToFile(ExtractFilePath(ParamStr(0)) + cLangFileName);
    except
    end;
  end;
{$ENDIF}

  FreeAndNil(ClWrLst);
  FreeAndNil(ClSrLst);
  FreeAndNil(ClDtLst);

end.