Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2003.02.27;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.008 c
1-87476
ALeXiuS
2003-02-16 22:43
2003.02.27
написать на экране когда идет игра


3-87274
Vladimir
2003-02-04 09:33
2003.02.27
ID вставленной записи


4-87743
E_Dimon
2003-01-13 21:20
2003.02.27
Hook+Mouse(что-то магическое...)


14-87617
Журналист
2003-02-09 11:34
2003.02.27
Опрос


14-87622
BlackTiger
2003-02-09 12:16
2003.02.27
Можно ли програмно





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский