Поддержка многоязычного интерфейса

Previous  Top  Next

 

    
 

 

Поддержка многоязычного интерфейса.

Подчас бывает актуально встроить в разрабатываемую программу поддержку нескольких языков. Существует множество средств и компонентов для осуществления подобных задач. У всех этих средств один недостаток - они слишком сложны и тяжеловесны. Предлагаем рассмотреть, как можно обеспечить поддержку многоязычности используя более простой и прозрачный метод.
 
Первое, что нужно выяснить - это язык, на котором разрабатывать интерфейс первоначально. Есть веские причины за то, чтобы использовать для этого именно тот язык, на котором написана эта статья. Дело в том, что русский язык менее лаконичен других европейских языков. При переводе на английский или немецкий 90% фраз будет компактнее и интерфейс вашей программы искажен не будет.
 
Для поддержки нескольких языков предлагается следующий простой подход. Интерфейс оформляется на родном языке - русском. Для всех остальных языков составляется словарь в виде:

Строка на языке 1=Строка на языке 2

Строка на языке 2=Строка на языке 2

...

 

Например:

Файл=File

Выход=Exit

Отмена=Cancel

 

И так для всех ресурсов приложения. Словарь поместим в отдельный текстовый файл.
 
Далее, нам необходимо для каждого текстового свойства любого компонента приложения поискать перевод в нашем словаре. Здесь не обойтись без Delphi RTTI. Через Component.ClassInfo получим ссылку на информацию типа, а затем GetTypeData(TypeInf) даст нам указатель на структуру с его описанием.

Code:

TypeInf := Component.ClassInfo;

AName := TypeInf^.Name;

TypeData := GetTypeData(TypeInf);

NumProps := TypeData^.PropCount;

 
Далее проходимся по всем свойствам данного (классового) типа.

Code:

GetMem(PropList, NumProps*sizeof(pointer));

 

try

   GetPropInfos(TypeInf, PropList);

 

   for i := 0 to NumProps-1 do

   begin

     PropName := PropList^[i]^.Name;

 

     PropTypeInf := PropList^[i]^.PropType^;

     PropInfo := PropList^[i];

 

 

     case PropTypeInf^.Kind of

       tkString, tkLString:  //... это то, что нам нужно

       if PropName <> 'Name' then { Переводить свойство Name не следует }

       begin

         { Получение значения свойства и поиск перевода в словаре }

         StringPropValue := GetStrProp( Component, PropInfo );

         SetStrProp( Component, PropInfo, TranslateString(StringPropValue) );

       end;

...

...

 
Отдельный случай - списки TStrings и коллекции типа TTReeNodes и TListItems. Их придется обработать персонально.
 

Code:

tkClass:

       begin

         PropObject := GetObjectProp(Component, PropInfo{, TPersistent});

 

         if Assigned(PropObject)then

         begin

           { Для дочерних свойств-классов вызов просмотра свойств }

           if (PropObject is TPersistent) then

            UpdateComponent(PropObject as TPersistent);

 

           { Индивидуальный подход к некоторым классам }

           if (PropObject is TStrings) then

           begin

             for j := 0 to (PropObject as TStrings).Count-1 do

               TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);

           end;

           if (PropObject is TTreeNodes) then

           begin

             for j := 0 to (PropObject as TTreeNodes).Count-1 do

               TTreeNodes(PropObject).Item[j].Text :=

                TranslateString(TTreeNodes(PropObject).Item[j].Text);

           end;

           if (PropObject is TListItems) then

           begin

             for j := 0 to (PropObject as TListItems).Count-1 do

               TListItems(PropObject).Item[j].Caption

                 := TranslateString(TListItems(PropObject).Item[j].Caption);

           end;

           { Здесь можно добавить обработку остальных классов }

         end;

 

       end;

 
Объединяя все написанное, получим компонент для перевода строковых ресурсов.

Code:

{

Globus Delphi VCL Extensions Library

Freeware

Copyright (c) 1998,2001 Chudin A.V, chudin@yandex.ru

===================================================================

glLanguageLoader Unit 04.2001             component TglLanguageLoader

===================================================================

Load new string resources from file to components

 

Словарь в виде текста вида:

Строка на языке 1=Строка на языке 2

...

Строка на языке 1=Строка на языке 2

===================================================================

}

unit glLanguageLoader;

 

interface

{$I glDEF.INC}

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, comctrls, grids;

 

type

TLanguageLoaderOptions = set of (lofTrimSpaces);

{опция удаления начальных и завершающих пробелов}

 

TglLanguageLoader = class(TComponent)

private

   sl: TStringList;

   FOptions: TLanguageLoaderOptions;

   function TranslateString(sString: string): string;

protected

   procedure UpdateComponent(Component: TPersistent); virtual;

public

   procedure LoadLanguage(Component: TComponent; FileName: string); {main function}

published

   property Options: TLanguageLoaderOptions read FOptions write FOptions;

end;

 

procedure LoadLanguage(Component: TComponent; FileName: string; Options: TLanguageLoaderOptions);

 

procedure Register;

 

implementation

uses TypInfo, dsgnintf;

 

procedure Register;

begin

RegisterComponents('Gl Components', [TglLanguageLoader]);

end;

 

{Ф-ия для загрузки словаря без предварительного создания компонента}

procedure LoadLanguage(Component: TComponent; FileName: string; Options: TLanguageLoaderOptions);

var

LanguageLoader: TglLanguageLoader;

begin

LanguageLoader := TglLanguageLoader.Create(nil);

try

     LanguageLoader.LoadLanguage(Component, FileName);

finally

   LanguageLoader.Free;

end;

end;

 

{ TglLanguageLoader }

 

{  Загрузка словаря, обход указанного компонента и  }

{  всех его дочерних компонентов                    }

procedure TglLanguageLoader.LoadLanguage(Component: TComponent; FileName: string);

procedure UpdateAllComponents(Component: TComponent);

var i: integer;

begin

   { обработка своцств компонента }

   UpdateComponent(Component);

   for i := 0 to Component.ComponentCount-1 do

     UpdateAllComponents(Component.Components[i]);

end;

begin

sl := TStringList.Create;

try

   { Загрузка словаря из заданного файла }

   sl.LoadFromFile(FileName);

   sl.Sorted := true;

   UpdateAllComponents(Component);

finally

   sl.Free;

end;

end;

 

{ Проход по всем свойствам компонента                        }

{ Для всех строковых свойств - загрузка перевода из сооваря  }

procedure TglLanguageLoader.UpdateComponent(Component: TPersistent);

var

PropInfo: PPropInfo;

TypeInf, PropTypeInf: PTypeInfo;

TypeData: PTypeData;

i, j: integer;

AName, PropName, StringPropValue: string;

PropList: PPropList;

NumProps: word;

PropObject: TObject;

begin

{ Playing with RTTI }

TypeInf := Component.ClassInfo;

AName := TypeInf^.Name;

TypeData := GetTypeData(TypeInf);

NumProps := TypeData^.PropCount;

 

GetMem(PropList, NumProps*sizeof(pointer));

 

try

   GetPropInfos(TypeInf, PropList);

 

   for i := 0 to NumProps-1 do

   begin

     PropName := PropList^[i]^.Name;

 

     PropTypeInf := PropList^[i]^.PropType^;

     PropInfo := PropList^[i];

 

 

     case PropTypeInf^.Kind of

       tkString, tkLString:

       if PropName <> 'Name' then { Переводить свойство Name не следует }

       begin

         { Получение значения свойства и поиск перевода в словаре }

         StringPropValue := GetStrProp( Component, PropInfo );

         SetStrProp( Component, PropInfo, TranslateString(StringPropValue) );

       end;

       tkClass:

       begin

         PropObject := GetObjectProp(Component, PropInfo{, TPersistent});

 

         if Assigned(PropObject)then

         begin

           { Для дочерних свойств-классов вызов просмотра свойств }

           if (PropObject is TPersistent) then

            UpdateComponent(PropObject as TPersistent);

 

           { Индивидуальный подход к некоторым классам }

           if (PropObject is TStrings) then

           begin

             for j := 0 to (PropObject as TStrings).Count-1 do

               TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);

           end;

           if (PropObject is TTreeNodes) then

           begin

             for j := 0 to (PropObject as TTreeNodes).Count-1 do

               TTreeNodes(PropObject).Item[j].Text :=

                 TranslateString(TTreeNodes(PropObject).Item[j].Text);

           end;

           if (PropObject is TListItems) then

           begin

             for j := 0 to (PropObject as TListItems).Count-1 do

               TListItems(PropObject).Item[j].Caption :=

                 TranslateString(TListItems(PropObject).Item[j].Caption);

           end;

           { Здесь можно добавить обработку остальных классов }

         end;

 

       end;

 

     end;

   end;

finally

   FreeMem(PropList, NumProps*sizeof(pointer));

end;

end;

 

{ Поиск перевода для заданной строки в словаре }

function TglLanguageLoader.TranslateString(sString: string): string;

begin

if lofTrimSpaces in Options then sString := trim(sString);

if sString = '' then

begin

   Result := '';

   exit;

end;

if sl.IndexOfName(sString) <> -1 then Result := sl.Values[sString] else Result := sString;

end;

 

 

end.

составление статьи: Андрей Чудин, ЦПР ТД Библио-Глобус.

 

Взято из http://delphi.chertenok.ru

©Drkb::04556