Форум: "Потрепаться";
Текущий архив: 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.007 c