Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2003.02.27;
Скачать: CL | DM;

Вниз

Локализация приложения   Найти похожие ветки 

 
Paladin ©   (2003-02-06 13:33) [0]

Привет!
Предлагаю обсудить интересную тему!
Если вы созаете хорошую программу, то рано или поздно вам придется задаться вопросом перевода ее на другие языки, т.е. локализовать программу. Вопрос очень интересный и важный.
Очень важно учитывать этот момент при проектирвании программы для того, чтобы потом не пришлось ее переписывать заново.
Вопросы:
Какие стандартные способы локализации существуют?
В чем их различие, их достоинстава и недостатки.
По ходу обсуждения наверняка всплывут другие вопросы.


 
Ru ©   (2003-02-06 14:02) [1]

стандартная возможность предусмотрена в Дельфи.
есть компонент лингвист (не помню чем он мне не понравился)
есть статьи по этому поводу на каждом сайте.


 
Danilka ©   (2003-02-06 14:06) [2]

>Какие стандартные способы локализации существуют?
использование resourcestring


 
Paladin ©   (2003-02-06 14:45) [3]

Ru> стандартная возможность предусмотрена в Дельфи.

Да, но не удобно работать с исходным кодом, приходится все делать в ручную, а должны быть соответствующие инструменты.
Можнт быть есть готовые решения?

Ru> есть статьи по этому поводу на каждом сайте.

Если не сложно, то подскажите, где есть такие статьи?


 
Ru ©   (2003-02-06 15:00) [4]

вот пока нашел дома статью:
Наверняка, вам хоть раз в жизни приходилось разрабатывать многоязычное приложение. Например, вам нужно, чтобы ваше приложение Lругалось¦ как на английском, так и на русском языке. В таком случае, обычно, один язык встраивается в приложение, как правило, английский, а потом пользователю предоставляется возможность выбрать язык интерфейса программы.
Для этих целей существует много коммерческих и бесплатных компонент-переводчиков, однако использовать их довольно неудобно, поскольку, необходимо помещать копию компонента на каждую форму приложения. Я предлагаю совершенно другой подход, а именно v использовать модуль-переводчик, который будет переводить все формы приложения. При достаточно высокой скорости перевода интерфейса, размер программы увеличиться всего на 3Кб (именно столько Lвесит¦ модуль)
Алгоритм работы такой: функция TranslateForm обходит все компоненты формы и переводит их на указанный язык, используя для этого специальный словарь. Словарь имеет формат Ini-файла, поэтому, как и для всех остальных Ini-файлов, для этого файла существует ограничение размера v не более 64 Кб, если я, конечно, не ошибаюсь. Решить эту проблему можно достаточно просто v использовать несколько словарей для разных языков.
Приведу раздел объявления нашего модуля (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 v выполняет перевод. Переводятся текстовые свойства компонент, например, Caption и Hint. При вызове функции нужно указать следующие параметры:

Form : TForm; Lang : string; DictFileName : string

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

Пример 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;

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

< To be ... >


 
Ru ©   (2003-02-06 15:02) [5]

<... be>

Пример 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;

< to be ...>


 
Ru ©   (2003-02-06 15:03) [6]

< ... be >

Примечания

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

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

ExtractFileDir(Application.EXEName+"\trans.dic")
3. Исходный код модуля-переводчика может распространятся согласно положениям лицензии GPL, текст которой вы можете прочитать на моем сайте v http://dkws.narod.ru.


< happy end >


 
veb   (2003-02-06 23:23) [7]

Мое мнение: стандартная возможность, реализованная в Delphi через Resourse DLL наиболее оптимальная. Перевод всего приложения стоит использовать только в случае когда приложение писалось без учета локализации.


 
Вадим ©   (2003-02-06 23:52) [8]

caption на контролах перевезти легко, а вот переменные, MessageBox"ы и т.п...


 
Ru ©   (2003-02-07 09:43) [9]

>Вадим © (06.02.03 23:52)

не-а легко. Хранишь все рядом в текстовике и нет проблем


 
stone ©   (2003-02-07 09:57) [10]

....

> 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 not (GetPropInfo(Obj, "Caption")=nil) then
SetPropValue(Obj, "Caption", Dict.ReadString(Lang, GetPropValue(Obj, "Caption"), ""));

то же самое для Hint и Title

и не надо громоздить такую кучу кода

примерно так


 
DiamondShark ©   (2003-02-07 10:49) [11]

Блин, нормально Integrated Translation Environment переводит.
Все эти компоненты левые, которые на форму надо кидать -- фигня полная. Есть подозрение, что и появились они от незнания возможностей среды.
Нет, интересный все-таки народ... Упорно не хочет зубы через рот лечить ;-(

Что касается подготовки приложения к локализации, в справке все доступно описано. Но наш программер справку не читает, он сразу в инет за компонентом лезет -- это считается продвинутый программер, который знает, что все проблемы решаются компонентом.

Вот что говорит справка о подготовке приложения к локализации
1 You must enable your code to handle strings from international character sets.
2 You need to design your user interface so that it can accommodate the changes that result from localization.
3 You need to isolate all resources that need to be localized.

Честное слово, мамой клянусь, это действительно всё, что надо сделать, и это нисколько не смертельно, особенно если помнить об этом с начала создания приложения.

Даже использование сторонних библиотек без исходников не проблема. Грамотно написанные библиотеки уже содержат строки, вынесенные в resourcestring, а dfm для внутренних форм просто нельзя не поставлять.

Граждане! Учите свой инструмент!


 
Anatoly Podgoretsky ©   (2003-02-07 10:57) [12]

И не делать распространенной ошибки, не локализировать менб смены локализации!


 
Real ©   (2003-02-07 14:30) [13]

Может я невнимательно читал другие предложения, но везде предлагается или стандартное решение (от Борланд) или черезмерное обилие кода. А не проще вот так:

Procedure SaveLang;
var
i: integer;
f: TextFile;
begin
// тут открыли файл
for i:=0 to ComponentCount do
begin
if (Components[i] is TControl)
then begin
Writeln(f,Components[i].Name+"="+Components[i].Caption;
Writeln(f,Components[i].Name+"="+Components[i].Hint;
..
// и другие локализуемые свойства
end;
end;
CloseFile(f);
end;

После этого, у нас файл вида:

Close_Button=Выход
Label_About=О программе..

Конечно, имена контролов нужно давать нормальные, хотя и необязательно. Можно вместо name использовать ClassName и потом просто не пользоваться TIniFiles для чтения.

и т.д. Ведь изначально интерфейс имеет язык. Считывание - аналогично. Смена языка - F4 и заменяй... А в проге список языков связан с массивом имен файлов языков... и все!

Что касается других ресурсов (типа Сообщений и т.д.) то если неохота DLL делать, в конце этого же файла хранить и их, а при работе держать в массиве или TStrings.


 
DiamondShark ©   (2003-02-07 15:08) [14]


> Real © (07.02.03 14:30)


То, что Вы предлагаете -- это и есть чрезмерное обилие кода.
И вообще, откуда такое недоверие к стандартным инструментам? Ах, да... Их же изучать надо, справку читать...


 
MrBeer ©   (2003-02-07 16:26) [15]


> DiamondShark © (07.02.03 15:08)


vse horosho poka vi sami vse perevodite - ploho kogda perevodit kto-to drugoi (naprimer useri shareware programmi). Tut *.lng faili samoe udobnoe reshenie.

P.S menshe emocii


 
Paladin ©   (2003-02-07 18:30) [16]

Действительно, стандартные инструменты это похоже оптимальное решение. Но у этого метода есть недостатки:

1. MrBeer> Tut *.lng faili samoe udobnoe reshenie. А такого удобного файла в текстовом формате стандартный метод не создает.

2. При помощи стандартного не очень красиво получается решить с текстом, встречающимся в исходном коде. Приходится заводить отдлельный ресурс *.rc
STRINGTABLE
{
1, "Hello world"
2, "AstalaVista Baby"
3, "Hi, Vasya"
}

Затем можно в коде писать так:
вместо label1.Caption := ‘Hello world’;
писать label1.Caption := LoadStr(1);
Но плохи дела с номерами, их не запомнишь.
Придется создавать:
unit ResConst;
interface
Const
Hello = 1;
Baby = 2;
Vasya = 3;
implementation
end.

Тогда можно будет писать так:
label1.Caption := LoadStr(Hello);
Или еще есть стандартные методы решения этого вопроса?

3. Все ресурсы, находятся не в одном месте, а в разных. Придется локализовывать не только то, что нужно локализовать стандартными средствами, но и STRINGTABLE в *.rc. При таком подходе на большом проекте тяжело вести, отслеживать и поддерживать сразу все эти моменты.

4. Сразу приходит мысль создать инструмент, встроенный в Delphi IDE для работы с этими файлами. Чтобы при кодировании, горячей комбинацией кнопок вызвал эту форму, а на ней строка поиска, вводишь ключевые слова, и она находит соответствующий ресурс у себя, т.е. находит название константы, содержащей указанные ключевые слова. Если он подходит, то нажимаешь кнопку подбросить и в исходный код, там, где стоял курсор, подбрасывается название выбранной константы. Если не находит нужный ресурс, то тут же можно его добавить и потом уже выбрать. При добавлении соответственно происходит работа с файлами ресурсов и т.д.
Так может это уже создано и решено?
--------------------------------
Получается у стандартного подхода есть недостатки:
1. Ресурсы подлежащие локализации находятся в разных местах
2. Не удобно учитывать требования локализации при работе с исходным кодом.






 
Mystic ©   (2003-02-07 18:58) [17]

2.
resourcestring
SHello = "Hello world!";

3. Все ресурсы хранятся в одном файле (например, Project1.UKR, Project1.RUS, Project1.ENG)

4. Есть инструмент, встроенный в Delphi, а есть и внешний для просмотра и редактироваия этих файлов.

Кроме того, "переводить" можно не только строки, но и такие вещи, как Font.Charset, ...


 
Anatoly Podgoretsky ©   (2003-02-07 19:57) [18]

Не только это, но и многое другое, например разные размеры и положение надписей, кнопок, различные битамы, в зависимости от языка.
И что еще интересно, можно локализировать не только свои ресурсы, но и некоторые другие ресусры использованых компонент, например сообщения и надписи скажем QuickReport это в нагрузку дается и это очень интересно.

Недостатком является некотороя сложность оснвоение на начальном этапе, пока не поймешь идеологии и как с этим правильно работать, но как правило достаточно двух дней ругани, а после этого силой не оттащись от использование ITE


 
Vlad__   (2003-02-07 20:37) [19]

У меня маячит локализация моей проги. Так вот мне борландовский вариант совсем не нравится, в смысле вроде все хорошо, но не могу я японцу делфю дать чтоб он смог подготовить ресурсную длл.
>Mystic ©
а что за внешний инструмент? я все экзешники запускал но Translation Manager так и не нашел.


 
MrBeer ©   (2003-02-07 21:01) [20]


> Paladin © (07.02.03 18:30)


> А такого удобного файла в текстовом формате стандартный
> метод не создает.


ja ved" ne govoril chto eto standartnii metod, prosto delaem fail s soderzhaniem tipa (ruchkami ili avtomatom)

[TabSheet4.Caption]
Eng Dual
Deu Doppel
Spa Dual
Por Duplo

Nu i pishem neskolko funkcii po parsingu etogo faila + podmenu znachenii v runtime.

Udobstv malo, no po otnosheniju k perevodu so storoni usera maximalnaja gibkost" t.k. ne nuzhen ne izhodnik i kompiljator.



 
veb   (2003-02-08 00:09) [21]

to Mystic
My> resourcestring
My> SHello = "Hello world!";
Предложение замечательное. Только файл имеет расширение Pas, а это никак не ресурсный файл, а следовательно не может быть подключен в ресурсную DLL. Так что Paladin прав выхода нет, надо тащить два файла и следить за соответствием :(



 
Vlad   (2003-02-08 14:24) [22]

Не хочу два файла, как сделать в одном?


 
Mystic ©   (2003-02-10 19:25) [23]

>> а что за внешний инструмент? я все экзешники запускал но
Translation Manager так и не нашел. <<


($DELPHI)\Bin\etm60.exe


>>следовательно не может быть подключен в ресурсную DLL<<

Значит в Inprise совершили невозможное :) Можешь создать resource DLL и убедиться, что все строки, описанные в разделе resourcestring помещаются в эту DLL.



>>Получается у стандартного подхода есть недостатки:
>>1. Ресурсы подлежащие локализации находятся в разных местах
>>2. Не удобно учитывать требования локализации при работе с исходным кодом.


1. Все ресурсы приложения (а не только подлежащие локализации) расположены в одной ресурсной DLL.
2. Можно абсолютно не учитывать требования к локализации при работе с исходным кодом, а выполнить ее в любой момент.
3. Можно динамически переключаться между разными resource DLL


>MrBeer

Пожалуй, самый большой недостаток. С другой стороны можно написать утилиту (ноподобие res-хакера), которая пробужится по файлу ресурсов и вытащит строковые ресурсы в отдельный ini-файл.




 
Palladin ©   (2003-02-10 20:22) [24]


> veb (08.02.03 00:09)

Уважаемый,не несите чушь, Вы никогда не слышали о строковых ресурсах в DLL?

сабж.
ИМХО ("волшбные" солова :) )
Муторное, да и не нужное это дело, создавать поддержку локализаций приложения.
Многие пользователи уже привыкли к стандартным file, open, save, edit etc.
и иногда им кажутся непонятными русские аналоги этих надписей.
Однажды пришла мне в голову мысль локализировать проект.
Русскоязычные юзвери неделю прохода не давали. А документацию (тоже локализированую) никто и не читал.

Да и у меня, локализированая версия корела 10, вызывает нервную дрожь и 90%ную потерю ориентации в проге. :)



Страницы: 1 вся ветка

Текущий архив: 2003.02.27;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.029 c
14-87702
Esc
2003-02-09 13:54
2003.02.27
Резидентность в win2000


1-87473
Stager
2003-02-19 00:27
2003.02.27
Проверка строки по маске


1-87443
яяяяяяя
2003-02-18 16:31
2003.02.27
Types of actual and and formal var parameters must be identical


3-87300
lightix
2003-02-10 11:27
2003.02.27
Программное создание DBF для FoxPro...


6-87588
kolobok
2003-01-07 18:36
2003.02.27
Аналог Hyper Terminal