Sign in Join
Wiki copytemporary

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

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

OldNewLine
11## Введение
22
33В данном тексте речь пойдёт о том, как сделать, чтобы программа могла использовать разные строки для разных языков. То есть, как локализовать свою программу для использования с различными языками интерфейса. Для трансляции приложения необходимо выполнить следующие действия:
44
55- Все отображаемые строки добавить в секцию resourcestrings для создания при компиляции .rst и/или .po файлов (IDE преобразование .rst в .po делает автоматически);
66- Для каждого языка создать перевод полученных .po файлов (есть бесплатный редактор);
77- Воспользоваться функциями LCL для автоматического перевода при запуске приложения.
88
99## Формат даты, времени и чисел
1010
1111В Linux, BSD, Mac OS X для локализации формата даты, времени и разделителя тысяч в числе, есть специальный модуль clocale. Чтобы инициализировать RTL локализованными системными параметрами необходимо в файл проекта (.lpr файл) добавить модуль clocale.
1212
1313## Строковые ресурсы
1414
1515Пример
1616
17-` resourcestring`\
18-`   Caption1 = 'Some text';`\
19-`   HelloWorld1 = 'Hello World';`
17+```text
18+ resourcestring
19+ Caption1 = 'Some text';
20+ HelloWorld1 = 'Hello World';
21+```
2022
2123Все строки объявленные в секции resourcestring являются обыкновенными строковыми константами, это значит, что вы можете их присвоить любой строке. Например
2224
23-` Label1.Caption := HelloWorld1;`
25+``` pascal
26+ Label1.Caption := HelloWorld1;
27+```
2428
2529Во время компиляции FPC создаёт для каждого модуля содержащего секцию resourcestring файл **unitname.rst**, в котором будут все константы секции в виде имя + значение.
2630
2731## .po файлы
2832
2933Есть много свободных утилит для редактирования .po файлов, которые являются просто текстовыми как и .rst файлы. Утилиты зачастую позволяют добавлять дополнительную информацию к файлам, такую как автор перевода, кодировка, язык и дата. В дистрибутиве FPC есть утилита **rstconv** (в Windows: rstconv.exe), которая позволяет преобразовать .rst файл в .po. IDE делает преобразование автоматически.
3034
3135Пример свободных утилит: kbabel, poedit.
3236
3337Пример использования rstconv:
3438
35-` rstconv -i unit1.rst -o unit1.po`
39+```text
40+ rstconv -i unit1.rst -o unit1.po
41+```
3642
3743## Перевод
3844
3945Для каждого языка должен быть создан свой файл .po. В LCL используются стандартные коды для поиска языков (en=английский, de=немецкий, ru=русский, ...). Например, в русском переводе unit1.po будет unit1.ru.po. Это означает, что нужно скопировать файл unit1.po в unit1.ru.po, unit1.de.po и т.д. для всех языков, которые вы хотите использовать, а затем переводчики могут редактировать .po файл для конкретного языка.
4046
4147## Настройки IDE для автоматического обновления .po файлов
4248
4349- Модули, содержащие строки ресурсов должны быть добавлены в пакет или проект.
4450- Вы должны создать каталог для .po файлов. Например: создать подкаталог **языка** в каталоге проекта/пакета. Для проектов перейти в меню IDE Проект\>Параметры проекта и на вкладке i18n выбрать Включить i18n и указать созданный вами каталог. В случае пакета соответственно Параметры пакета.
4551
4652При включённом i18n IDE автоматически создаёт или обновляет .po файлы используя информацию содержащуюся в .rst и .lrt файлах (использовать в этом случае утилиту rstconv не нужно). Процесс обновления начинается со сбора всех записей из файлов .rst и .lrt и запись их в .po файл, так же автоматически обновляются и все файлы с переводами .xx.po.
4753
4854### Удаление устаревших записей
4955
5056Записи файла .po не найденные в .rst и .lrt файлах автоматически удаляются. Также они удаляются из файлов перевода .xx.po, потому что нет смысла переводить записи, которые не используются.
5157
5258### Повторяющиеся записи
5359
5460Повторяющиеся записи могут появиться, если один и тоже текст используется для разных строковых ресурсов. Например, возьмём файл lazarus/ide/lazarusidestrconst.pas в нём текст 'Gutter' встречается у нескольких строковых констант:
5561
5662``` pascal
5763 dlfMouseSimpleGutterSect = 'Gutter';
5864 dlgMouseOptNodeGutter = 'Gutter';
5965 dlgGutter = 'Gutter';
6066 dlgAddHiAttrGroupGutter = 'Gutter';
6167```
6268
6369После преобразования из .rst файла содержимое .po файла будет примерно следующее:
6470
65-`#: lazarusidestrconsts.dlfmousesimpleguttersect`\
66-`msgid "Gutter"`\
67-`msgstr ""`\
68-`#: lazarusidestrconsts.dlgaddhiattrgroupgutter`\
69-`msgid "Gutter"`\
70-`msgstr ""`\
71-`и т.д.`\
71+```text
72+#: lazarusidestrconsts.dlfmousesimpleguttersect
73+msgid "Gutter"
74+msgstr ""
75+#: lazarusidestrconsts.dlgaddhiattrgroupgutter
76+msgid "Gutter"
77+msgstr ""
78+и т.д.
79+```
7280
7381Если строка начинается с "#:" то она считается комментарием и инструменты, используемые для перевода увидев многократное повторение msgid "Gutter" будут считать их дублирующимися записями и выдавать ошибку или предупреждение. Повторяющиеся записи считаются нормальными для .po файла, если они имеют контекст определённый ключевым словом msgctxt. Данное ключевое слово добавляет контекст дублирующимся записям для автоматического обновления и перевода. Имя для контекста берётся из строки после префикса "#:". Смотрите пример ниже:
7482
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-`и т.д.`\
83+```text
84+#: lazarusidestrconsts.dlfmousesimpleguttersect
85+msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"
86+msgid "Gutter"
87+msgstr ""
88+#: lazarusidestrconsts.dlgaddhiattrgroupgutter
89+msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"
90+msgid "Gutter"
91+msgstr ""
92+и т.д.
93+```
8494
8595В переведённые файлы .xx.po дублирующиеся записи автоматически не добавляются. Если в файле присутствует запись с таким же msgid что и добавляемая, то считается, что добавляемая запись уже переведена, и она просто не добавляется.
8696
8797Автоматическое обнаружение дублируемых строк ещё не совершенно. Дубликаты как элементы добавляются в список, и может случиться так, что некоторые непереведённые записи могут прочитаться первыми.
8898
8999### Нечёткие записи
90100
91101Изменения в строковых ресурсах влияет на переводы, например, если строка сначала бала определена так:
92102
93103``` pascal
94104 dlgEdColor = 'Syntax highlight';
95105```
96106
97107после преобразования получим следующую .po запись
98108
99-`#: lazarusidestrconsts.dlgedcolor`\
100-`msgid "Syntax higlight"`\
101-`msgstr ""`
109+```text
110+#: lazarusidestrconsts.dlgedcolor
111+msgid "Syntax higlight"
112+msgstr ""
113+```
102114
103115которые после перевода на русский язык примет, следующий вид
104116
105-`#: lazarusidestrconsts.dlgedcolor`\
106-`msgid "Syntax higlight"`\
107-`msgstr "Подсветка синтаксиса"`
117+```text
118+#: lazarusidestrconsts.dlgedcolor
119+msgid "Syntax higlight"
120+msgstr "Подсветка синтаксиса"
121+```
108122
109123Предположим, что позднее строка была изменена на
110124
111125``` pascal
112126 dlgEdColor = 'Colors';
113127```
114128
115129в результате запись в .po файле будет
116130
117-`#: lazarusidestrconsts.dlgedcolor`\
118-`msgid "Colors"`\
119-`msgstr ""`
131+```text
132+#: lazarusidestrconsts.dlgedcolor
133+msgid "Colors"
134+msgstr ""
135+```
120136
121137Обратите внимание, что хотя идентификатор остаётся неизменным lazarusidestrconsts.dlgedcolor строку изменили с 'Syntax highlight' на 'Colors', так как строку уже перевели, перевод будет не совпадать с новым смыслом. Утилита автоматического обновления сообщит об этом факте и запишет запись следующего вида:
122138
123-`#: lazarusidestrconsts.dlgedcolor`\
124-`#, fuzzy`\
125-`#| msgid "Syntax highlight"`\
126-`msgctxt "lazarusidestrconsts.dlgedcolor"`\
127-`msgid "Colors"`\
128-`msgstr "Подсветка синтаксиса"`
139+```text
140+#: lazarusidestrconsts.dlgedcolor
141+#, fuzzy
142+#| msgid "Syntax highlight"
143+msgctxt "lazarusidestrconsts.dlgedcolor"
144+msgid "Colors"
145+msgstr "Подсветка синтаксиса"
146+```
129147
130148С точки зрения формата .po файла префикс "#," означает, что запись имеет флаг (нечёткой) и программа перевода может оповестить пользователя, что нынешний перевод является сомнительным и необходимо повторно перевести данную запись. В свою очередь префикс "#\|" указывает предыдущую непереведённую строку.
131149
132150## Перевод Форм, Модулей данных и Фреймов
133151
134152Когда опция i18n включена IDE автоматически создаёт .lrt файлы для каждой формы, модуля данных и фрейма. Если вы включили эту опцию в первый раз, то вы должны открыть каждую форму и немножко переместить её для того чтобы можно было сохранить её. После сохранения автоматически создаются .lrt фалы (если модуль с формой unit1.pas, то создастся соответственно файл unit1.lrt). При компиляции будет создан файл projectname.po или packagename.po в каталоге .po файлов, в данный файл будут помещены все записи из всех .lrt фалов проекта.
135153
136154Для форм, которые должны быть переведены во время выполнения, вы должны назначить транслятор LRSTranslator (определён в модуле LResources) в разделе инициализации одного из модулей.
137155
138156``` pascal
139157...
140158uses
141159 ...
142160 LResources;
143161...
144162...
145163initialization
146164 LRSTranslator:=TPoTranslator.Create('/path/to/the/po/file');
147165```
148166
149167Однако в LCL нет никакого класса TPoTranslator (т.е. класса который умеет читать .po файлы). Его можно создать самому, подсмотрев реализацию класса TDefaultTranslator в модуле DefaultTranslator.pas.
150168
151169``` pascal
152170unit PoTranslator;
153171
154172{$mode objfpc}{$H+}
155173
156174interface
157175
158176uses
159177 Classes, SysUtils, LResources, typinfo, Translations;
160178
161179type
162180
163181 { TPoTranslator }
164182
165183 TPoTranslator=class(TAbstractTranslator)
166184 private
167185 FPOFile:TPOFile;
168186 public
169187 constructor Create(POFileName:string);
170188 destructor Destroy;override;
171189 procedure TranslateStringProperty(Sender:TObject;
172190 const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
173191 end;
174192
175193implementation
176194
177195{ TPoTranslator }
178196
179197constructor TPoTranslator.Create(POFileName: string);
180198begin
181199 inherited Create;
182200 FPOFile:=TPOFile.Create(POFileName);
183201end;
184202
185203destructor TPoTranslator.Destroy;
186204begin
187205 FPOFile.Free;
188206 inherited Destroy;
189207end;
190208
191209procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
192210 const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
193211var
194212 s: String;
195213begin
196214 if not Assigned(FPOFile) then exit;
197215 if not Assigned(PropInfo) then exit;
198216{Нужно ли нам это?}
199217 if Instance is TComponent then
200218 if csDesigning in (Instance as TComponent).ComponentState then exit;
201219{:)}
202220 if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
203221 s:=FPOFile.Translate(Content, Content);
204222 if s<>'' then Content:=s;
205223end;
206224
207225end.
208226```
209227
210228Или же вы можете конвертировать файл .po в .mo используя утилиту msgfmt и просто использовать модуль DefaultTranslator
211229
212230``` pascal
213231...
214232uses
215233 ...
216234 DefaultTranslator;
217235```
218236
219237который будет автоматически искать .mo фалы в нескольких стандартных местах. (Недостатком является то что вам придется с программой поставлять сразу два файла: .mo для модуля DefaultTranslator и .po файл для TranslateUnitResourceStrings). Если вы все-таки решили воспользоваться DefaultTranslator. Определение языка будет производиться автоматически по переменной окружения LANG или по параметру командной строки --lang. .mo файлы должны иметь следующие и мена и каталоги размещения:
220238
221239- \<Application Directory\>/LANG/\<Application Filename\>.mo
222240- \<Application Directory\>/languages/LANG/\<Application Filename\>.mo
223241- \<Application Directory\>/locale/LANG/\<Application Filename\>.mo
224242- \<Application Directory\>/locale/LC_MESSAGES/LANG/\<Application Filename\>.mo
225243
226244в Unix-подобных система файл может ещё находится в
227245
228246- /usr/share/locale/LANG/LC_MESSAGES/\<Application Filename\>.mo
229247
230248а также поиск будет производиться по короткому идентификатору языка (например, если локаль "ru_RU" или "ru_RU.UTF-8" и файла с данным языком не найдено, то поиск также будет производиться для "RU").
231249
232250## Перевод при старте программы
233251
234252Для каждого .po файла вы должны вызвать функцию TranslateUnitResourceStrings для перевода каждого модуля. Например:
235253
236254``` pascal
237255 {Прежде всего добавьте модули "gettext" и "translations"}
238256 procedure TForm1.FormCreate(Sender: TObject);
239257 var
240258 PODirectory, Lang, FallbackLang: String;
241259 begin
242260 PODirectory := '/path/to/lazarus/lcl/languages/';
243261 GetLanguageIDs(Lang, FallbackLang); // определено в модуле gettext
244262 TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
245263 MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
246264 end;
247265```
248266
249267## Добавление .po файлов в ресурсы программ
250268
251269Если вы хотите поместить .po файлы в исполняемый файл, то выполните следующие действия:
252270
253271- Создайте новый модуль (не модуль формы!).
254272- Конвертируйте .po файл(ы) в .lrs с помощью утилиты tools/lazres:
255273
256274 ./lazres unit1.lrs unit1.ru.po
257275
258276Создастся файл unit1.lrs начинающийся строкой
259277
260278``` pascal
261279LazarusResources.Add('unit1.ru','PO',[
262280 ...
263281```
264282
265283- Добавьте в код:
266284
267285``` pascal
268286uses LResources, Translations;
269287
270288resourcestring
271289 MyCaption = 'Caption';
272290
273291function TranslateUnitResourceStrings: boolean;
274292var
275293 r: TLResource;
276294 POFile: TPOFile;
277295begin
278296 r:=LazarusResources.Find('unit1.ru','PO');
279297 POFile:=TPOFile.Create;
280298 try
281299 POFile.ReadPOText(r.Value);
282300 Result:=Translations.TranslateUnitResourceStrings('unit1',POFile);
283301 finally
284302 POFile.Free;
285303 end;
286304end;
287305
288306initialization
289307 {$I unit1.lrs}
290308```
291309
292310- Выполните функцию TranslateUnitResourceStrings в начале программы. Вы можете это сделать в секции initialization.
293311
294312## Локализация IDE
295313
296314### Файлы
297315
298316Файлы .po находятся в каталогах исходников IDE Lazarus:
299317
300318- lazarus/languages перевод для IDE
301319- lcl/languages/ перевод для LCL
302320- ideintf/languages/ перевод для интерфейса IDE
303321
304322### Переводчики
305323
306324- Немецкий перевод поддерживает Joerg Braun.
307325- Финский перевод поддерживает Seppo Suurtarla
308326- Русский перевод поддерживает Максим Ганецкий
309327
310328Если вы хотите начать новый перевод, задайте вопрос в рассылку, может уже кто-то над ним работает.
311329
312330Пожалуйста, прочитайте внимательно: [[Lazarus Documentation|Переводы]]
313331
314332## Future work / ToDos
315333
316334[[IDE Development/ru|IDE Development: Переводы, i18n, lrt файлы, po файлы]]
317335
318336---
319337*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