Многоязычный интерфейс приложений в Delphi

Наверняка, вам хоть раз в жизни приходилось разрабатывать многоязычное приложение. Например, вам нужно, чтобы ваше приложение "ругалось" как на английском, так и на русском языке. В таком случае, обычно, один язык встраивается в приложение, как правило, английский, а потом пользователю предоставляется возможность выбрать язык интерфейса программы.

Для этих целей существует много коммерческих и бесплатных компонент-переводчиков, однако использовать их довольно неудобно, поскольку, необходимо помещать копию компонента на каждую форму приложения. Я предлагаю совершенно другой подход, а именно - использовать модуль-переводчик, который будет переводить все формы приложения. При достаточно высокой скорости перевода интерфейса, размер программы увеличиться всего на 3Кб (именно столько <весит> модуль)

Алгоритм работы такой: функция TranslateForm обходит все компоненты формы и переводит их на указанный язык, используя для этого специальный словарь. Словарь имеет формат Ini-файла, поэтому, как и для всех остальных Ini-файлов, для этого файла существует ограничение размера - не более 64 Кб, если я, конечно, не ошибаюсь. Решить эту проблему можно достаточно просто - использовать несколько словарей для разных языков.

Приведу раздел объявления нашего модуля (interface):

Пример 1


interface  
  
uses SysUtils, Classes, Controls, Forms, Dialogs, Quickrpt, ... ;  
  
function Translate(Text : string; Lang : string; Dict : TIniFile):string;  
procedure TranslateForm(Form : TForm; Lang : string; DictFileName : string); 

Процедура TranslateForm, как я уже писал, обходит все компоненты формы, а функция Trasnlate - выполняет перевод. Переводятся текстовые свойства компонент, например, Caption и Hint. При вызове функции нужно указать следующие параметры:


Form : TForm; Lang : string; DictFileName : string  

Параметр Form определяет форму, которую нужно перевести. Параметр Lang - это язык на который нужно перевести свойство компонента. Тот язык, с которым работает программа в данный момент, будем называть ключевым. Подразумевается, что в данный момент (до перевода) программа работает с ключевым языком, потому что поиск (перевод) слов будет произведен с использованием ключевого языка. Чтобы было понятнее, приведу пример словаря:

Пример 2


[Rus]  
File=Файл  
Open=Открыть  
Translate=Перевести  
Application=Приложение  
New=Создать  
Open=Открыть  
Text=Текст  
Save=Сохранить  
Save As...=Сохранить как...  
Print=Печать  
Print Setup=Параметры печати  
Exit=Выход  
Edit=Правка  
Cut=Вырезать  
Copy=Копировать  
Paste=Вставить  
Undo=Отмена  
View=Вид  
Status bar=Панель состояния  
Tool bar=Панель инструментов  
Tools=Сервис  
Settings=Параметры  
...  

Для перевода интерфейса приложения с английского на русский язык нужно вызвать процедуру TrasnlateForm так:


TranslateForm(Form1, 'Rus', 'trans.dic');  

При этом ключевым языком считается английский (см. секцию Rus в примере 2). Аналогично можно создать секцию Eng и использовать русский язык в качестве ключевого.

Для того, чтобы перевести все формы приложения используйте следующие операторы:


for i:=0 to Screen.FormCount-1 do  
TranslateForm(Screen.Forms[i],'Rus','trans.dic');  

Если вы хотите изменить язык приложения во время его запуска, отредактируйте файл проекта так:


for i:=0 to Screen.FormCount-1 do  
TranslateForm(Screen.Forms[i],'Rus','trans.dic');  
Application.Run;  

То есть перевод форм нужно выполнить до выполнения метода Application.Run;

Теперь рассмотрим весь исходный код модуля-переводчика:

Пример 3. Файл trans.pas


unit trans;  
  
interface  
  
uses SysUtils, Classes, Controls, Forms, Dialogs, Quickrpt, QRCtrls, StdCtrls,  
 Buttons, ComCtrls, ExtCtrls, DBCtrls, DB, DBGrids, Menus, Grids, Mask,  
 IniFiles;  
  
function Translate(Text : string; Lang : string; Dict : TIniFile):string;  
procedure TranslateForm(Form : TForm; Lang : string; DictFileName : string);  
  
implementation  
  
{ Непосредственный перевод текста }  
function Translate(Text : string; Lang : string; Dict : TIniFile):string;  
var s : string;  
begin  
 Result:=Text;  
 S:=Dict.ReadString(Lang, Text, '');  
 if S='' then Dict.WriteString(Lang, Text, '');  
 if S<>'' then Result:=S;  
end;  
  
procedure TranslateForm(Form : TForm; Lang : string; DictFileName : string);  
var Dict : TIniFile;  
 I,J : Integer;  
 Obj : TObject;  
begin  
{ Открываем словарь. Перед вызовом функции Translate словарь должен быть открыт! }  
  
 Dict:=TIniFile.Create(DictFileName);  
  
 if not fileexists(DictFileName) then  
 ShowMessage('No exists' + DictFileName);  
  
 Form.Caption:=Translate(Form.Caption, Lang, Dict);  
 Application.Title:=Translate(Application.Title, Lang, Dict);  
  
 { Переводим компоненты формы}  
 For I:=0 to Form.ComponentCount-1 do  
 Begin  
  
 Obj:=Form.Components[i];  
 if Obj is TMenuItem then  
 begin  
 TMenuItem(Obj).Caption:=Translate(TMenuItem(Obj).Caption, Lang, Dict);  
 TMenuItem(Obj).Hint:=Translate(TMenuItem(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TButton then  
 begin  
 TButton(Obj).Caption:=Translate(TButton(Obj).Caption, Lang, Dict);  
 TButton(Obj).Hint:=Translate(TButton(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TBitBtn then  
 begin  
 TBitBtn(Obj).Caption:=Translate(TBitBtn(Obj).Caption, Lang, Dict);  
 TBitBtn(Obj).Hint:=Translate(TBitBtn(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TSpeedButton then  
 begin  
 TSpeedButton(Obj).Caption:=Translate(TSpeedButton(Obj).Caption, Lang, Dict);  
 TSpeedButton(Obj).Hint:=Translate(TSpeedButton(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TPanel then  
 begin  
 TPanel(Obj).Caption:=Translate(TPanel(Obj).Caption, Lang, Dict);  
 TPanel(Obj).Hint:=Translate(TPanel(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TGroupBox then  
 begin  
 TGroupBox(Obj).Caption:=Translate(TGroupBox(Obj).Caption, Lang, Dict);  
 TGroupBox(Obj).Hint:=Translate(TGroupBox(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TLabel then  
 begin  
 TLabel(Obj).Caption:=Translate(TLabel(Obj).Caption, Lang, Dict);  
 TLabel(Obj).Hint:=Translate(TLabel(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TStaticText then  
 begin  
 TStaticText(Obj).Caption:=Translate(TStaticText(Obj).Caption, Lang, Dict);  
 TStaticText(Obj).Hint:=Translate(TStaticText(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TQRLabel then  
 begin  
 TQRLabel(Obj).Caption:=Translate(TQRLabel(Obj).Caption, Lang, Dict);  
 TQRLabel(Obj).Hint:=Translate(TQRLabel(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TEdit then  
 TEdit(Obj).Hint:=Translate(TEdit(Obj).Hint, Lang, Dict);  
  
 if Obj is TMaskEdit then  
 TMaskEdit(Obj).Hint:=Translate(TMaskEdit(Obj).Hint, Lang, Dict);  
  
 if Obj is TMemo then  
 TMemo(Obj).Hint:=Translate(TMemo(Obj).Hint, Lang, Dict);  
  
 if Obj is TRadioGroup then  
 begin  
 TRadioGroup(Obj).Caption:=Translate(TRadioGroup(Obj).Caption, Lang, Dict);  
 TRadioGroup(Obj).Hint:=Translate(TRadioGroup(Obj).Hint, Lang, Dict);  
 for J:=0 to TRadioGroup(Obj).Items.Count-1 do  
 begin  
 TRadioGroup(Obj).Items[j]:=Translate(TRadioGroup(Obj).Items[j], Lang, Dict);  
 end;  
 end;  
  
 if Obj is TCheckBox then  
 begin  
 TCheckBox(Obj).Caption:=Translate(TCheckBox(Obj).Caption, Lang, Dict);  
 TCheckBox(Obj).Hint:=Translate(TCheckBox(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TField then  
 TField(Obj).DisplayLabel:=Translate(TField(Obj).DisplayLabel, Lang, Dict);  
  
 if Obj is TTabSheet then  
 begin  
 TTabSheet(Obj).Caption:=Translate(TTabSheet(Obj).Caption, Lang, Dict);  
 TTabSheet(Obj).Hint:=Translate(TTabSheet(Obj).Hint, Lang, Dict);  
 end;  
  
 if Obj is TOpenDialog then  
 TOpenDialog(Obj).Title:=Translate(TOpenDialog(Obj).Title, Lang, Dict);  
  
 if Obj is TSaveDialog then  
 TSaveDialog(Obj).Title:=Translate(TSaveDialog(Obj).Title, Lang, Dict);  
  
 if Obj is TPrintDialog then  
 TPrintDialog(Obj).Title:=Translate(TPrintDialog(Obj).Title, Lang, Dict);  
  
 if Obj is TPrinterSetupDialog then  
 TPrinterSetupDialog(Obj).Title:=Translate(TPrinterSetupDialog(Obj).Title, Lang, Dict);  
  
 End;  
  
{ освобождаем память}  
  
 Dict.Free;  
end;  
  
end.  

Естественно, перед использованием модуля его нужно добавить в оператор uses:

uses ..., trans;  

Примечания
1. Теперь нужно отметить особенность функции Translate. Если слова нет в словаре, она его автоматически добавляет. Потом вам только останется самому ввести перевод этого слова!

2. Напомню, что поиск словаря, так как он является ini-файлом, по умолчанию производится в каталоге C:\Windows (точнее, в каталоге, возвращаемом функцией GetWindowsDirectory), поэтому предварительно нужно поместить туда словарь или явно указать имя файла. При этом удобно использовать функцию ExtractFileDir так:


ExtractFileDir(Application.EXEName+'\trans.dic')  
Теги:
Многоязычный интерфейс
Добавлено: 04 Августа 2018 07:48:59 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...