Содержание статьи
Всё чаще и чаще наши разработчики представляют свои программные продукты всему миру. Это не только дает возможность нашим соотечественникам хорошо заработать, но и ставит перед ними задачу разработки модуля многоязычной поддержки. Я уверен, что, например, англичанин явно не будет изучать русский или, тем более, украинский что бы понять, что написано на кнопках управления в популярной отечественной программе. Приходим к выводу, что реализация многоязычной поддержки далеко не последнее дело в процессе создания программы, от которого зависит её популярность.
Этой достаточно актуальной проблеме я и посвящаю статью, ставя перед собой следующую задачу: создать программный код, который бы не сильно влиял на размер программы и позволял осуществлять перевод заголовков элементов управления продукта на другие иностранные языки без вмешательства разработчика программы и ее повторной компиляции при добавлении нового языка.
Как это делают другие?
Я решил всерьез заняться вопросом поиска оптимального алгоритма и, прежде всего, захотел узнать, какой формат имеют языковые модули известных программ. Для изучения была выбрана программа «Архивариус 3000». В каталоге программы оказалась папка Languages с файлами: Ukrainian.txt, Latvian.txt... Открыв файл Ukrainian.txt, я увидел типичную структуру ini файла. Приведу пример одной из строк файла:
SFormAbout = "Про программу"
Как можно предположить SFormAbout – это название кнопки, а «Про программу» – её заголовок. То есть структура файла имеет вид:
Name=Caption
где Name – имя компонента, а Caption – заголовок компонента. Что нам мешает попробовать также организовать Russian.txt и English.txt для своей программы?
Этим мы и займёмся, максимально автоматизировав все процессы.
Программирование
Итак, создадим кнопку и назовём её CNew. При её нажатии:
procedure TForm1.CNewClick(Sender: TObject);
var
b:byte;
begin
f:=TStringList.Create;
for b:=0 to Form1.ComponentCount-1 do
begin
f.Add(Form1.Components[b].Name+'=')
end;
f.SaveToFile('NewLang.txt');
end;
TStringList — объект подобный TMemo, только не VCL (не имеет графического представления), поэтому он не сильно влияет на размер программы. Строка f:=TStringList.Create;
— инициализирует переменную f и присваивает ей тип. Так как на форме находится только BNew в сохранённом файле NewLang.txt будет только одна строка:
BNew=
Напишем следующую функцию:
function lang(k:string):string;
begin
lang:=copy(k,pos('=',k)+1,length(k)-pos(k,'='));
end;
В параметр ей передаётся строка, в которой присутствует знак «=». Результат функции – текст, содержащийся после знака =
. То есть, если параметр k имеет значение CNew=Кнопка
, то результатом функции будет строка: Кнопка
. Создайте AComboBox1, задайте Items[1]=Russian
и Items[2]= English
(имена языковых файлов). Создайте событие OnChange для этого объекта и напишите следующий код:
procedure TForm1.AComboBox1Change(Sender: TObject);
var
b:byte;
CompType:char;
begin
f:=TStringList.Create;
f.LoadFromFile(AComboBox1.Text+'.txt'); // открываем файл выбранного языка + расширение txt
for b:=0 to f.Count-1 do
begin
CompType:=copy(f.Strings[b],1,1)[1];
if CompType='C' then
TCheckBox(Form1.Components[b]).Caption:=lang(f.Strings[b])
else
if CompType='T' then
TEdit(Form1.Components[b]).Text:=lang(f.Strings[b])
end;
end;
Теперь мы должны согласиться с таким правилом: если при изменении языка у компонента должно изменится поле Caption, то Name компонента должен начинаться с символа C
, если меняется Text – с символа T
. Например, создадим на форме кнопку CButton1, CButton2, CButton3, метку CSample, опцию CheckBox и поле TSample. Из всех этих компонентов только поле имеет параметр Text, поэтому его имя начинается на T
.
Как вы, наверное, уже заметили, TComboBox мы назвали AComboBox1. Это необходимо для того, что б при смене языка ни Caption (которого у этого компонента вообще нет), ни Text не менялись.
Дальше нажмите кнопку BNew – создастся файл NewLang.txt, переименуйте его в Russian.txt и создайте ещё один такой же English.txt. Содержимое этих файлов должно быть следующим:
CSample=
CNew=
AComboBox1=
TSample=
CButton1=
CButton2=
CButton3=
CheckBox1=
Измените содержимое Russian.txt:
CSample=Текст метки
CNew=Создать новый язык
AComboBox1=
TSample=Текст поля
CButton1=Поиск
CButton2=Индекс
CButton3=Помощь
CheckBox1=Опция
И English.txt:
CSample=Label text
CNew=To create a new language
AComboBox1=
CESampel=Edit text
CButton1=Search
CButton2=Index
CButton3=Help
CheckBox1=Option
Как вы уже поняли, мы пишем перевод после знака =
. Теперь запустите программу и в ComboBox`e выберите English – язык интерфейса изменится:
Выбрав Russian, результат будет следующий:
Кнопка создания нового языка, как вы понимаете, не обязательна. Но она удобна в процессе отладки программы. Поэтому, когда вы завершите свой проект – смело её удаляйте.
Как всё работает
Доступ к компоненту на форме можно получить, используя функцию Form1.Components[b]
, где b – номер компонента на форме. Всего таких компонентов на форме Form1.ComponentCount. При сохранении в файл NewLang.txt, номер строки соответствует номеру компонента на форме. Тогда, при смене языка, нужно определить что менять b-тому компоненту и на что менять. Что менять узнаём из имени, а на что – считывая b-ю строку файла с этим языком. Заметьте, что при считывании очень важен порядок, и если вместо:
CSample=Текст метки
CNew=Создать новый язык
написать:
CNew=Создать новый язык
CSample=Текст метки
то текст метки будет «Создать новый язык». Поэтому называть компоненты необходимо так, что бы при создании файла языка было ясно, какой элемент будет носить присвоенный нами ему заголовок.
P.S. Теперь мы можем добавлять на форму сколько угодно Label`ов и всего прочего, не изменяя алгоритм смены языка, а только меняя и создавая файлы языка и правильно называя созданные компоненты. Заметьте, эта статья – только наброски для вашего проекта, поэтому программу можно и нужно модернизировать. Например, остались некоторые недоработки: заголовок главного окна необходимо будет менять вручную. Так же, хорошо было бы сделать автоматический поиск файлов языка в директории. Но это уже будет домашним заданием…