Sign in Join
Wiki copytemporary

Translations / i18n / localizations for programs/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Translations / i18n / localizations for programs/ru

OldNewLine
1+## Введение
2+
3+В данном тексте речь пойдёт о том, как сделать, чтобы программа могла использовать разные строки для разных языков. То есть, как локализовать свою программу для использования с различными языками интерфейса. Для трансляции приложения необходимо выполнить следующие действия:
4+
5+- Все отображаемые строки добавить в секцию resourcestrings для создания при компиляции .rst и/или .po файлов (IDE преобразование .rst в .po делает автоматически);
6+- Для каждого языка создать перевод полученных .po файлов (есть бесплатный редактор);
7+- Воспользоваться функциями LCL для автоматического перевода при запуске приложения.
8+
9+## Формат даты, времени и чисел
10+
11+В Linux, BSD, Mac OS X для локализации формата даты, времени и разделителя тысяч в числе, есть специальный модуль clocale. Чтобы инициализировать RTL локализованными системными параметрами необходимо в файл проекта (.lpr файл) добавить модуль clocale.
12+
13+## Строковые ресурсы
14+
15+Пример
16+
17+` resourcestring`\
18+`   Caption1 = 'Some text';`\
19+`   HelloWorld1 = 'Hello World';`
20+
21+Все строки объявленные в секции resourcestring являются обыкновенными строковыми константами, это значит, что вы можете их присвоить любой строке. Например
22+
23+` Label1.Caption := HelloWorld1;`
24+
25+Во время компиляции FPC создаёт для каждого модуля содержащего секцию resourcestring файл **unitname.rst**, в котором будут все константы секции в виде имя + значение.
26+
27+## .po файлы
28+
29+Есть много свободных утилит для редактирования .po файлов, которые являются просто текстовыми как и .rst файлы. Утилиты зачастую позволяют добавлять дополнительную информацию к файлам, такую как автор перевода, кодировка, язык и дата. В дистрибутиве FPC есть утилита **rstconv** (в Windows: rstconv.exe), которая позволяет преобразовать .rst файл в .po. IDE делает преобразование автоматически.
30+
31+Пример свободных утилит: kbabel, poedit.
32+
33+Пример использования rstconv:
34+
35+` rstconv -i unit1.rst -o unit1.po`
36+
37+## Перевод
38+
39+Для каждого языка должен быть создан свой файл .po. В LCL используются стандартные коды для поиска языков (en=английский, de=немецкий, ru=русский, ...). Например, в русском переводе unit1.po будет unit1.ru.po. Это означает, что нужно скопировать файл unit1.po в unit1.ru.po, unit1.de.po и т.д. для всех языков, которые вы хотите использовать, а затем переводчики могут редактировать .po файл для конкретного языка.
40+
41+## Настройки IDE для автоматического обновления .po файлов
42+
43+- Модули, содержащие строки ресурсов должны быть добавлены в пакет или проект.
44+- Вы должны создать каталог для .po файлов. Например: создать подкаталог **языка** в каталоге проекта/пакета. Для проектов перейти в меню IDE Проект\>Параметры проекта и на вкладке i18n выбрать Включить i18n и указать созданный вами каталог. В случае пакета соответственно Параметры пакета.
45+
46+При включённом i18n IDE автоматически создаёт или обновляет .po файлы используя информацию содержащуюся в .rst и .lrt файлах (использовать в этом случае утилиту rstconv не нужно). Процесс обновления начинается со сбора всех записей из файлов .rst и .lrt и запись их в .po файл, так же автоматически обновляются и все файлы с переводами .xx.po.
47+
48+### Удаление устаревших записей
49+
50+Записи файла .po не найденные в .rst и .lrt файлах автоматически удаляются. Также они удаляются из файлов перевода .xx.po, потому что нет смысла переводить записи, которые не используются.
51+
52+### Повторяющиеся записи
53+
54+Повторяющиеся записи могут появиться, если один и тоже текст используется для разных строковых ресурсов. Например, возьмём файл lazarus/ide/lazarusidestrconst.pas в нём текст 'Gutter' встречается у нескольких строковых констант:
55+
56+``` pascal
57+ dlfMouseSimpleGutterSect = 'Gutter';
58+ dlgMouseOptNodeGutter = 'Gutter';
59+ dlgGutter = 'Gutter';
60+ dlgAddHiAttrGroupGutter = 'Gutter';
61+```
62+
63+После преобразования из .rst файла содержимое .po файла будет примерно следующее:
64+
65+`#: lazarusidestrconsts.dlfmousesimpleguttersect`\
66+`msgid "Gutter"`\
67+`msgstr ""`\
68+`#: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
69+`msgid "Gutter"`\
70+`msgstr ""`\
71+`и т.д.`\
72+
73+Если строка начинается с "#:" то она считается комментарием и инструменты, используемые для перевода увидев многократное повторение msgid "Gutter" будут считать их дублирующимися записями и выдавать ошибку или предупреждение. Повторяющиеся записи считаются нормальными для .po файла, если они имеют контекст определённый ключевым словом msgctxt. Данное ключевое слово добавляет контекст дублирующимся записям для автоматического обновления и перевода. Имя для контекста берётся из строки после префикса "#:". Смотрите пример ниже:
74+
75+`#: lazarusidestrconsts.dlfmousesimpleguttersect`\
76+`msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"`\
77+`msgid "Gutter"`\
78+`msgstr ""`\
79+`#: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
80+`msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"`\
81+`msgid "Gutter"`\
82+`msgstr ""`\
83+`и т.д.`\
84+
85+В переведённые файлы .xx.po дублирующиеся записи автоматически не добавляются. Если в файле присутствует запись с таким же msgid что и добавляемая, то считается, что добавляемая запись уже переведена, и она просто не добавляется.
86+
87+Автоматическое обнаружение дублируемых строк ещё не совершенно. Дубликаты как элементы добавляются в список, и может случиться так, что некоторые непереведённые записи могут прочитаться первыми.
88+
89+### Нечёткие записи
90+
91+Изменения в строковых ресурсах влияет на переводы, например, если строка сначала бала определена так:
92+
93+``` pascal
94+ dlgEdColor = 'Syntax highlight';
95+```
96+
97+после преобразования получим следующую .po запись
98+
99+`#: lazarusidestrconsts.dlgedcolor`\
100+`msgid "Syntax higlight"`\
101+`msgstr ""`
102+
103+которые после перевода на русский язык примет, следующий вид
104+
105+`#: lazarusidestrconsts.dlgedcolor`\
106+`msgid "Syntax higlight"`\
107+`msgstr "Подсветка синтаксиса"`
108+
109+Предположим, что позднее строка была изменена на
110+
111+``` pascal
112+ dlgEdColor = 'Colors';
113+```
114+
115+в результате запись в .po файле будет
116+
117+`#: lazarusidestrconsts.dlgedcolor`\
118+`msgid "Colors"`\
119+`msgstr ""`
120+
121+Обратите внимание, что хотя идентификатор остаётся неизменным lazarusidestrconsts.dlgedcolor строку изменили с 'Syntax highlight' на 'Colors', так как строку уже перевели, перевод будет не совпадать с новым смыслом. Утилита автоматического обновления сообщит об этом факте и запишет запись следующего вида:
122+
123+`#: lazarusidestrconsts.dlgedcolor`\
124+`#, fuzzy`\
125+`#| msgid "Syntax highlight"`\
126+`msgctxt "lazarusidestrconsts.dlgedcolor"`\
127+`msgid "Colors"`\
128+`msgstr "Подсветка синтаксиса"`
129+
130+С точки зрения формата .po файла префикс "#," означает, что запись имеет флаг (нечёткой) и программа перевода может оповестить пользователя, что нынешний перевод является сомнительным и необходимо повторно перевести данную запись. В свою очередь префикс "#\|" указывает предыдущую непереведённую строку.
131+
132+## Перевод Форм, Модулей данных и Фреймов
133+
134+Когда опция i18n включена IDE автоматически создаёт .lrt файлы для каждой формы, модуля данных и фрейма. Если вы включили эту опцию в первый раз, то вы должны открыть каждую форму и немножко переместить её для того чтобы можно было сохранить её. После сохранения автоматически создаются .lrt фалы (если модуль с формой unit1.pas, то создастся соответственно файл unit1.lrt). При компиляции будет создан файл projectname.po или packagename.po в каталоге .po файлов, в данный файл будут помещены все записи из всех .lrt фалов проекта.
135+
136+Для форм, которые должны быть переведены во время выполнения, вы должны назначить транслятор LRSTranslator (определён в модуле LResources) в разделе инициализации одного из модулей.
137+
138+``` pascal
139+...
140+uses
141+ ...
142+ LResources;
143+...
144+...
145+initialization
146+ LRSTranslator:=TPoTranslator.Create('/path/to/the/po/file');
147+```
148+
149+Однако в LCL нет никакого класса TPoTranslator (т.е. класса который умеет читать .po файлы). Его можно создать самому, подсмотрев реализацию класса TDefaultTranslator в модуле DefaultTranslator.pas.
150+
151+``` pascal
152+unit PoTranslator;
153+
154+{$mode objfpc}{$H+}
155+
156+interface
157+
158+uses
159+ Classes, SysUtils, LResources, typinfo, Translations;
160+
161+type
162+
163+ { TPoTranslator }
164+
165+ TPoTranslator=class(TAbstractTranslator)
166+ private
167+ FPOFile:TPOFile;
168+ public
169+ constructor Create(POFileName:string);
170+ destructor Destroy;override;
171+ procedure TranslateStringProperty(Sender:TObject;
172+ const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
173+ end;
174+
175+implementation
176+
177+{ TPoTranslator }
178+
179+constructor TPoTranslator.Create(POFileName: string);
180+begin
181+ inherited Create;
182+ FPOFile:=TPOFile.Create(POFileName);
183+end;
184+
185+destructor TPoTranslator.Destroy;
186+begin
187+ FPOFile.Free;
188+ inherited Destroy;
189+end;
190+
191+procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
192+ const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
193+var
194+ s: String;
195+begin
196+ if not Assigned(FPOFile) then exit;
197+ if not Assigned(PropInfo) then exit;
198+{Нужно ли нам это?}
199+ if Instance is TComponent then
200+ if csDesigning in (Instance as TComponent).ComponentState then exit;
201+{:)}
202+ if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
203+ s:=FPOFile.Translate(Content, Content);
204+ if s<>'' then Content:=s;
205+end;
206+
207+end.
208+```
209+
210+Или же вы можете конвертировать файл .po в .mo используя утилиту msgfmt и просто использовать модуль DefaultTranslator
211+
212+``` pascal
213+...
214+uses
215+ ...
216+ DefaultTranslator;
217+```
218+
219+который будет автоматически искать .mo фалы в нескольких стандартных местах. (Недостатком является то что вам придется с программой поставлять сразу два файла: .mo для модуля DefaultTranslator и .po файл для TranslateUnitResourceStrings). Если вы все-таки решили воспользоваться DefaultTranslator. Определение языка будет производиться автоматически по переменной окружения LANG или по параметру командной строки --lang. .mo файлы должны иметь следующие и мена и каталоги размещения:
220+
221+- \<Application Directory\>/LANG/\<Application Filename\>.mo
222+- \<Application Directory\>/languages/LANG/\<Application Filename\>.mo
223+- \<Application Directory\>/locale/LANG/\<Application Filename\>.mo
224+- \<Application Directory\>/locale/LC_MESSAGES/LANG/\<Application Filename\>.mo
225+
226+в Unix-подобных система файл может ещё находится в
227+
228+- /usr/share/locale/LANG/LC_MESSAGES/\<Application Filename\>.mo
229+
230+а также поиск будет производиться по короткому идентификатору языка (например, если локаль "ru_RU" или "ru_RU.UTF-8" и файла с данным языком не найдено, то поиск также будет производиться для "RU").
231+
232+## Перевод при старте программы
233+
234+Для каждого .po файла вы должны вызвать функцию TranslateUnitResourceStrings для перевода каждого модуля. Например:
235+
236+``` pascal
237+ {Прежде всего добавьте модули "gettext" и "translations"}
238+ procedure TForm1.FormCreate(Sender: TObject);
239+ var
240+ PODirectory, Lang, FallbackLang: String;
241+ begin
242+ PODirectory := '/path/to/lazarus/lcl/languages/';
243+ GetLanguageIDs(Lang, FallbackLang); // определено в модуле gettext
244+ TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
245+ MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
246+ end;
247+```
248+
249+## Добавление .po файлов в ресурсы программ
250+
251+Если вы хотите поместить .po файлы в исполняемый файл, то выполните следующие действия:
252+
253+- Создайте новый модуль (не модуль формы!).
254+- Конвертируйте .po файл(ы) в .lrs с помощью утилиты tools/lazres:
255+
256+ ./lazres unit1.lrs unit1.ru.po
257+
258+Создастся файл unit1.lrs начинающийся строкой
259+
260+``` pascal
261+LazarusResources.Add('unit1.ru','PO',[
262+ ...
263+```
264+
265+- Добавьте в код:
266+
267+``` pascal
268+uses LResources, Translations;
269+
270+resourcestring
271+ MyCaption = 'Caption';
272+
273+function TranslateUnitResourceStrings: boolean;
274+var
275+ r: TLResource;
276+ POFile: TPOFile;
277+begin
278+ r:=LazarusResources.Find('unit1.ru','PO');
279+ POFile:=TPOFile.Create;
280+ try
281+ POFile.ReadPOText(r.Value);
282+ Result:=Translations.TranslateUnitResourceStrings('unit1',POFile);
283+ finally
284+ POFile.Free;
285+ end;
286+end;
287+
288+initialization
289+ {$I unit1.lrs}
290+```
291+
292+- Выполните функцию TranslateUnitResourceStrings в начале программы. Вы можете это сделать в секции initialization.
293+
294+## Локализация IDE
295+
296+### Файлы
297+
298+Файлы .po находятся в каталогах исходников IDE Lazarus:
299+
300+- lazarus/languages перевод для IDE
301+- lcl/languages/ перевод для LCL
302+- ideintf/languages/ перевод для интерфейса IDE
303+
304+### Переводчики
305+
306+- Немецкий перевод поддерживает Joerg Braun.
307+- Финский перевод поддерживает Seppo Suurtarla
308+- Русский перевод поддерживает Максим Ганецкий
309+
310+Если вы хотите начать новый перевод, задайте вопрос в рассылку, может уже кто-то над ним работает.
311+
312+Пожалуйста, прочитайте внимательно: [[Lazarus Documentation|Переводы]]
313+
314+## Future work / ToDos
315+
316+[[IDE Development/ru|IDE Development: Переводы, i18n, lrt файлы, po файлы]]
317+
318+---
319+*From the Free Pascal wiki, page "Translations / i18n / localizations for programs/ru", as it stood on 2020-03-01 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history