Недавно добавленные исходники

•  DeLiKaTeS Tetris (Тетрис)  174

•  TDictionary Custom Sort  3 345

•  Fast Watermark Sources  3 095

•  3D Designer  4 853

•  Sik Screen Capture  3 351

•  Patch Maker  3 556

•  Айболит (remote control)  3 665

•  ListBox Drag & Drop  3 020

•  Доска для игры Реверси  81 750

•  Графические эффекты  3 948

•  Рисование по маске  3 253

•  Перетаскивание изображений  2 633

•  Canvas Drawing  2 763

•  Рисование Луны  2 586

•  Поворот изображения  2 196

•  Рисование стержней  2 173

•  Paint on Shape  1 570

•  Генератор кроссвордов  2 242

•  Головоломка Paletto  1 771

•  Теорема Монжа об окружностях  2 238

•  Пазл Numbrix  1 685

•  Заборы и коммивояжеры  2 060

•  Игра HIP  1 282

•  Игра Go (Го)  1 232

•  Симулятор лифта  1 477

•  Программа укладки плитки  1 219

•  Генератор лабиринта  1 549

•  Проверка числового ввода  1 369

•  HEX View  1 497

•  Физический маятник  1 359

 
скрыть


Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Delphi Sources

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



Программирование на С++ у госслужащих - как секс у подростков:
- все об этом думают;
- все об этом говорят;
- все думают, что их ближний это делает;
- почти никто этого не делает;
- тот, кто это делает, делает это плохо;
- все думают, что в следующий раз лучше получится;
- никто не принимает мер безопасности;
- любому стыдно признаться в том, что он чего-то не знает;
- если у кого-то что-то получается, от этого всегда много шума.

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

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

Для поддержки нескольких языков предлагается следующий простой подход. Интерфейс оформляется на родном языке - русском. Для всех остальных языков составляется словарь в виде:


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

Например:


Файл=File
Выход=Exit
Отмена=Cancel

И так для всех ресурсов приложения. Словарь поместим в отдельный текстовый файл.

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


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;
...
...

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


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;

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


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
    {main function}
    procedure LoadLanguage(Component: TComponent; FileName: string);
  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.








Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi World FAQ

Группа ВКонтакте