Difference between revisions of "Translations / i18n / localizations for programs/ru"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(11 intermediate revisions by 6 users not shown)
Line 10: Line 10:
 
==Формат даты, времени и чисел==
 
==Формат даты, времени и чисел==
  
В Linux, BSD, Mac OS X для локализации формата даты, времени и разделителя тысяч в числе, есть специальный модуль <b>clocale</b>. Чтобы инициализировать RTL локализованными системами параметрами необходимо в файл проекта (.lpr файл) добавить модуль <b>clocale</b>.
+
В Linux, BSD, Mac OS X для локализации формата даты, времени и разделителя тысяч в числе, есть специальный модуль <b>clocale</b>. Чтобы инициализировать RTL локализованными системными параметрами необходимо в файл проекта (.lpr файл) добавить модуль <b>clocale</b>.
  
 
==Строковые ресурсы==
 
==Строковые ресурсы==
Line 51: Line 51:
  
 
Повторяющиеся записи могут появиться, если один и тоже текст используется для разных строковых ресурсов. Например, возьмём файл <i>lazarus/ide/lazarusidestrconst.pas</i> в нём текст 'Gutter' встречается у нескольких строковых констант:
 
Повторяющиеся записи могут появиться, если один и тоже текст используется для разных строковых ресурсов. Например, возьмём файл <i>lazarus/ide/lazarusidestrconst.pas</i> в нём текст 'Gutter' встречается у нескольких строковых констант:
<Delphi>
+
<syntaxhighlight lang=pascal>
 
   dlfMouseSimpleGutterSect = 'Gutter';
 
   dlfMouseSimpleGutterSect = 'Gutter';
 
   dlgMouseOptNodeGutter = 'Gutter';
 
   dlgMouseOptNodeGutter = 'Gutter';
 
   dlgGutter = 'Gutter';
 
   dlgGutter = 'Gutter';
 
   dlgAddHiAttrGroupGutter  = 'Gutter';   
 
   dlgAddHiAttrGroupGutter  = 'Gutter';   
</Delphi>
+
</syntaxhighlight>
 
После преобразования из <b>.rst</b> файла содержимое <b>.po</b> файла будет примерно следующее:
 
После преобразования из <b>.rst</b> файла содержимое <b>.po</b> файла будет примерно следующее:
  
Line 86: Line 86:
  
 
Изменения в строковых ресурсах влияет на переводы, например, если строка сначала бала определена так:
 
Изменения в строковых ресурсах влияет на переводы, например, если строка сначала бала определена так:
<Delphi>
+
<syntaxhighlight lang=pascal>
 
   dlgEdColor = 'Syntax highlight';
 
   dlgEdColor = 'Syntax highlight';
</Delphi>
+
</syntaxhighlight>
 
после преобразования получим следующую <b>.po</b> запись
 
после преобразования получим следующую <b>.po</b> запись
 
  #: lazarusidestrconsts.dlgedcolor
 
  #: lazarusidestrconsts.dlgedcolor
Line 98: Line 98:
 
  msgstr "Подсветка синтаксиса"
 
  msgstr "Подсветка синтаксиса"
 
Предположим, что позднее строка была изменена на
 
Предположим, что позднее строка была изменена на
<Delphi>
+
<syntaxhighlight lang=pascal>
 
   dlgEdColor = 'Colors';
 
   dlgEdColor = 'Colors';
</Delphi>
+
</syntaxhighlight>
 
в результате запись в <b>.po</b> файле будет
 
в результате запись в <b>.po</b> файле будет
 
  #: lazarusidestrconsts.dlgedcolor
 
  #: lazarusidestrconsts.dlgedcolor
Line 121: Line 121:
 
Для форм, которые должны быть переведены во время выполнения, вы должны назначить транслятор LRSTranslator (определён в модуле LResources) в разделе инициализации одного из модулей.
 
Для форм, которые должны быть переведены во время выполнения, вы должны назначить транслятор LRSTranslator (определён в модуле LResources) в разделе инициализации одного из модулей.
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
uses
 
uses
Line 130: Line 130:
 
initialization
 
initialization
 
   LRSTranslator:=TPoTranslator.Create('/path/to/the/po/file');
 
   LRSTranslator:=TPoTranslator.Create('/path/to/the/po/file');
</Delphi>
+
</syntaxhighlight>
  
 
Однако в LCL нет никакого класса TPoTranslator (т.е. класса который умеет читать <b>.po</b> файлы). Его можно создать самому, подсмотрев реализацию класса TDefaultTranslator в модуле DefaultTranslator.pas.
 
Однако в LCL нет никакого класса TPoTranslator (т.е. класса который умеет читать <b>.po</b> файлы). Его можно создать самому, подсмотрев реализацию класса TDefaultTranslator в модуле DefaultTranslator.pas.
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
unit PoTranslator;
 
unit PoTranslator;
  
Line 191: Line 191:
  
 
end.
 
end.
</Delphi>
+
</syntaxhighlight>
  
 
Или же вы можете конвертировать файл <b>.po</b> в <b>.mo</b> используя утилиту msgfmt и просто использовать модуль DefaultTranslator
 
Или же вы можете конвертировать файл <b>.po</b> в <b>.mo</b> используя утилиту msgfmt и просто использовать модуль DefaultTranslator
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
uses
 
uses
 
   ...
 
   ...
 
   DefaultTranslator;
 
   DefaultTranslator;
</Delphi>
+
</syntaxhighlight>
  
который будет автоматически искать <b>.mo</> фалы в нескольких стандартных местах. (Недостатком является то что вам придется с программой поставлять сразу два файла: <b>.mo</b> для модуля DefaultTranslator и <b>.po</b> файл для TranslateUnitResourceStrings).
+
который будет автоматически искать <b>.mo</b> фалы в нескольких стандартных местах. (Недостатком является то что вам придется с программой поставлять сразу два файла: <b>.mo</b> для модуля DefaultTranslator и <b>.po</b> файл для TranslateUnitResourceStrings).
 
Если вы все-таки решили воспользоваться DefaultTranslator. Определение языка будет производиться автоматически по переменной окружения <b>LANG</b> или по параметру командной строки <b>--lang</b>. <b>.mo</b> файлы должны иметь следующие и мена и каталоги размещения:
 
Если вы все-таки решили воспользоваться DefaultTranslator. Определение языка будет производиться автоматически по переменной окружения <b>LANG</b> или по параметру командной строки <b>--lang</b>. <b>.mo</b> файлы должны иметь следующие и мена и каталоги размещения:
  
Line 220: Line 220:
 
Для каждого <b>.po</b> файла вы должны вызвать функцию TranslateUnitResourceStrings для перевода каждого модуля. Например:
 
Для каждого <b>.po</b> файла вы должны вызвать функцию TranslateUnitResourceStrings для перевода каждого модуля. Например:
  
<pascal>
+
<syntaxhighlight lang=pascal>
 
     {Прежде всего добавьте модули "gettext" и "translations"}
 
     {Прежде всего добавьте модули "gettext" и "translations"}
 
     procedure TForm1.FormCreate(Sender: TObject);
 
     procedure TForm1.FormCreate(Sender: TObject);
Line 231: Line 231:
 
       MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
 
       MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
 
     end;
 
     end;
</pascal>
+
</syntaxhighlight>
  
 
==Добавление .po файлов в ресурсы программ==
 
==Добавление .po файлов в ресурсы программ==
Line 243: Line 243:
 
</pre>
 
</pre>
 
Создастся файл unit1.lrs начинающийся строкой
 
Создастся файл unit1.lrs начинающийся строкой
<pascal>
+
<syntaxhighlight lang=pascal>
 
LazarusResources.Add('unit1.ru','PO',[
 
LazarusResources.Add('unit1.ru','PO',[
 
   ...
 
   ...
</pascal>
+
</syntaxhighlight>
 
*Добавьте в код:
 
*Добавьте в код:
<pascal>
+
<syntaxhighlight lang=pascal>
 
uses LResources, Translations;
 
uses LResources, Translations;
  
Line 272: Line 272:
 
   {$I unit1.lrs}
 
   {$I unit1.lrs}
  
</pascal>
+
</syntaxhighlight>
 
*Выполните функцию TranslateUnitResourceStrings в начале программы. Вы можете это сделать в секции initialization.
 
*Выполните функцию TranslateUnitResourceStrings в начале программы. Вы можете это сделать в секции initialization.
  
Line 278: Line 278:
  
 
===Файлы===
 
===Файлы===
Файлы <b>.po</b> находятся в каталогах исподников IDE Lazarus:
+
Файлы <b>.po</b> находятся в каталогах исходников IDE Lazarus:
 
*lazarus/languages перевод для IDE
 
*lazarus/languages перевод для IDE
 
*lcl/languages/ перевод для LCL
 
*lcl/languages/ перевод для LCL
Line 294: Line 294:
 
==Future work / ToDos==
 
==Future work / ToDos==
  
[[IDE_Development#Translations.2C_i18n.2C_lrt_files.2C_po_files|IDE Development: Translations, i18n, lrt, po files]]
+
[[IDE_Development/ru#Переводы.2C_i18n.2C_lrt_файлы.2C_po_файлы|IDE Development: Переводы, i18n, lrt файлы, po файлы]]

Latest revision as of 11:31, 1 March 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) polski (pl) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN)

Введение

В данном тексте речь пойдёт о том, как сделать, чтобы программа могла использовать разные строки для разных языков. То есть, как локализовать свою программу для использования с различными языками интерфейса. Для трансляции приложения необходимо выполнить следующие действия:

  • Все отображаемые строки добавить в секцию resourcestrings для создания при компиляции .rst и/или .po файлов (IDE преобразование .rst в .po делает автоматически);
  • Для каждого языка создать перевод полученных .po файлов (есть бесплатный редактор);
  • Воспользоваться функциями LCL для автоматического перевода при запуске приложения.

Формат даты, времени и чисел

В Linux, BSD, Mac OS X для локализации формата даты, времени и разделителя тысяч в числе, есть специальный модуль clocale. Чтобы инициализировать RTL локализованными системными параметрами необходимо в файл проекта (.lpr файл) добавить модуль clocale.

Строковые ресурсы

Пример

 resourcestring
   Caption1 = 'Some text';
   HelloWorld1 = 'Hello World';

Все строки объявленные в секции resourcestring являются обыкновенными строковыми константами, это значит, что вы можете их присвоить любой строке. Например

 Label1.Caption := HelloWorld1;

Во время компиляции FPC создаёт для каждого модуля содержащего секцию resourcestring файл unitname.rst, в котором будут все константы секции в виде имя + значение.

.po файлы

Есть много свободных утилит для редактирования .po файлов, которые являются просто текстовыми как и .rst файлы. Утилиты зачастую позволяют добавлять дополнительную информацию к файлам, такую как автор перевода, кодировка, язык и дата. В дистрибутиве FPC есть утилита rstconv (в Windows: rstconv.exe), которая позволяет преобразовать .rst файл в .po. IDE делает преобразование автоматически.

Пример свободных утилит: kbabel, poedit.

Пример использования rstconv:

 rstconv -i unit1.rst -o unit1.po

Перевод

Для каждого языка должен быть создан свой файл .po. В LCL используются стандартные коды для поиска языков (en=английский, de=немецкий, ru=русский, ...). Например, в русском переводе unit1.po будет unit1.ru.po. Это означает, что нужно скопировать файл unit1.po в unit1.ru.po, unit1.de.po и т.д. для всех языков, которые вы хотите использовать, а затем переводчики могут редактировать .po файл для конкретного языка.

Настройки IDE для автоматического обновления .po файлов

  • Модули, содержащие строки ресурсов должны быть добавлены в пакет или проект.
  • Вы должны создать каталог для .po файлов. Например: создать подкаталог языка в каталоге проекта/пакета. Для проектов перейти в меню IDE Проект>Параметры проекта и на вкладке i18n выбрать Включить i18n и указать созданный вами каталог. В случае пакета соответственно Параметры пакета.

При включённом i18n IDE автоматически создаёт или обновляет .po файлы используя информацию содержащуюся в .rst и .lrt файлах (использовать в этом случае утилиту rstconv не нужно). Процесс обновления начинается со сбора всех записей из файлов .rst и .lrt и запись их в .po файл, так же автоматически обновляются и все файлы с переводами .xx.po.

Удаление устаревших записей

Записи файла .po не найденные в .rst и .lrt файлах автоматически удаляются. Также они удаляются из файлов перевода .xx.po, потому что нет смысла переводить записи, которые не используются.

Повторяющиеся записи

Повторяющиеся записи могут появиться, если один и тоже текст используется для разных строковых ресурсов. Например, возьмём файл lazarus/ide/lazarusidestrconst.pas в нём текст 'Gutter' встречается у нескольких строковых констант:

  dlfMouseSimpleGutterSect = 'Gutter';
  dlgMouseOptNodeGutter = 'Gutter';
  dlgGutter = 'Gutter';
  dlgAddHiAttrGroupGutter   = 'Gutter';

После преобразования из .rst файла содержимое .po файла будет примерно следующее:

#: lazarusidestrconsts.dlfmousesimpleguttersect
msgid "Gutter"
msgstr ""
#: lazarusidestrconsts.dlgaddhiattrgroupgutter
msgid "Gutter"
msgstr ""
и т.д.

Если строка начинается с "#:" то она считается комментарием и инструменты, используемые для перевода увидев многократное повторение msgid "Gutter" будут считать их дублирующимися записями и выдавать ошибку или предупреждение. Повторяющиеся записи считаются нормальными для .po файла, если они имеют контекст определённый ключевым словом msgctxt. Данное ключевое слово добавляет контекст дублирующимся записям для автоматического обновления и перевода. Имя для контекста берётся из строки после префикса "#:". Смотрите пример ниже:

#: lazarusidestrconsts.dlfmousesimpleguttersect
msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"
msgid "Gutter"
msgstr ""
#: lazarusidestrconsts.dlgaddhiattrgroupgutter
msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"
msgid "Gutter"
msgstr ""
и т.д.

В переведённые файлы .xx.po дублирующиеся записи автоматически не добавляются. Если в файле присутствует запись с таким же msgid что и добавляемая, то считается, что добавляемая запись уже переведена, и она просто не добавляется.

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

Нечёткие записи

Изменения в строковых ресурсах влияет на переводы, например, если строка сначала бала определена так:

  dlgEdColor = 'Syntax highlight';

после преобразования получим следующую .po запись

#: lazarusidestrconsts.dlgedcolor
msgid "Syntax higlight"
msgstr ""

которые после перевода на русский язык примет, следующий вид

#: lazarusidestrconsts.dlgedcolor
msgid "Syntax higlight"
msgstr "Подсветка синтаксиса"

Предположим, что позднее строка была изменена на

  dlgEdColor = 'Colors';

в результате запись в .po файле будет

#: lazarusidestrconsts.dlgedcolor
msgid "Colors"
msgstr ""

Обратите внимание, что хотя идентификатор остаётся неизменным lazarusidestrconsts.dlgedcolor строку изменили с 'Syntax highlight' на 'Colors', так как строку уже перевели, перевод будет не совпадать с новым смыслом. Утилита автоматического обновления сообщит об этом факте и запишет запись следующего вида:

#: lazarusidestrconsts.dlgedcolor
#, fuzzy
#| msgid "Syntax highlight"
msgctxt "lazarusidestrconsts.dlgedcolor"
msgid "Colors"
msgstr "Подсветка синтаксиса"

С точки зрения формата .po файла префикс "#," означает, что запись имеет флаг (нечёткой) и программа перевода может оповестить пользователя, что нынешний перевод является сомнительным и необходимо повторно перевести данную запись. В свою очередь префикс "#|" указывает предыдущую непереведённую строку.

Перевод Форм, Модулей данных и Фреймов

Когда опция i18n включена IDE автоматически создаёт .lrt файлы для каждой формы, модуля данных и фрейма. Если вы включили эту опцию в первый раз, то вы должны открыть каждую форму и немножко переместить её для того чтобы можно было сохранить её. После сохранения автоматически создаются .lrt фалы (если модуль с формой unit1.pas, то создастся соответственно файл unit1.lrt). При компиляции будет создан файл projectname.po или packagename.po в каталоге .po файлов, в данный файл будут помещены все записи из всех .lrt фалов проекта.

Для форм, которые должны быть переведены во время выполнения, вы должны назначить транслятор LRSTranslator (определён в модуле LResources) в разделе инициализации одного из модулей.

...
uses
  ...
  LResources;
...
...
initialization
  LRSTranslator:=TPoTranslator.Create('/path/to/the/po/file');

Однако в LCL нет никакого класса TPoTranslator (т.е. класса который умеет читать .po файлы). Его можно создать самому, подсмотрев реализацию класса TDefaultTranslator в модуле DefaultTranslator.pas.

unit PoTranslator;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, typinfo, Translations;

type

 { TPoTranslator }

 TPoTranslator=class(TAbstractTranslator)
 private
  FPOFile:TPOFile;
 public
  constructor Create(POFileName:string);
  destructor Destroy;override;
  procedure TranslateStringProperty(Sender:TObject; 
    const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
 end;

implementation

{ TPoTranslator }

constructor TPoTranslator.Create(POFileName: string);
begin
  inherited Create;
  FPOFile:=TPOFile.Create(POFileName);
end;

destructor TPoTranslator.Destroy;
begin
  FPOFile.Free;
  inherited Destroy;
end;

procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
  const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
  s: String;
begin
  if not Assigned(FPOFile) then exit;
  if not Assigned(PropInfo) then exit;
{Нужно ли нам это?}
  if Instance is TComponent then
   if csDesigning in (Instance as TComponent).ComponentState then exit;
{:)}
  if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
  s:=FPOFile.Translate(Content, Content);
  if s<>'' then Content:=s;
end;

end.

Или же вы можете конвертировать файл .po в .mo используя утилиту msgfmt и просто использовать модуль DefaultTranslator

...
uses
   ...
   DefaultTranslator;

который будет автоматически искать .mo фалы в нескольких стандартных местах. (Недостатком является то что вам придется с программой поставлять сразу два файла: .mo для модуля DefaultTranslator и .po файл для TranslateUnitResourceStrings). Если вы все-таки решили воспользоваться DefaultTranslator. Определение языка будет производиться автоматически по переменной окружения LANG или по параметру командной строки --lang. .mo файлы должны иметь следующие и мена и каталоги размещения:

  • <Application Directory>/LANG/<Application Filename>.mo
  • <Application Directory>/languages/LANG/<Application Filename>.mo
  • <Application Directory>/locale/LANG/<Application Filename>.mo
  • <Application Directory>/locale/LC_MESSAGES/LANG/<Application Filename>.mo

в Unix-подобных система файл может ещё находится в

  • /usr/share/locale/LANG/LC_MESSAGES/<Application Filename>.mo

а также поиск будет производиться по короткому идентификатору языка (например, если локаль "ru_RU" или "ru_RU.UTF-8" и файла с данным языком не найдено, то поиск также будет производиться для "RU").

Перевод при старте программы

Для каждого .po файла вы должны вызвать функцию TranslateUnitResourceStrings для перевода каждого модуля. Например:

    {Прежде всего добавьте модули "gettext" и "translations"}
    procedure TForm1.FormCreate(Sender: TObject);
    var
      PODirectory, Lang, FallbackLang: String;
    begin
      PODirectory := '/path/to/lazarus/lcl/languages/';
      GetLanguageIDs(Lang, FallbackLang); // определено в модуле gettext
      TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
      MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
    end;

Добавление .po файлов в ресурсы программ

Если вы хотите поместить .po файлы в исполняемый файл, то выполните следующие действия:

  • Создайте новый модуль (не модуль формы!).
  • Конвертируйте .po файл(ы) в .lrs с помощью утилиты tools/lazres:
./lazres unit1.lrs unit1.ru.po

Создастся файл unit1.lrs начинающийся строкой

LazarusResources.Add('unit1.ru','PO',[
  ...
  • Добавьте в код:
uses LResources, Translations;

resourcestring
  MyCaption = 'Caption';

function TranslateUnitResourceStrings: boolean;
var
  r: TLResource;
  POFile: TPOFile;
begin
  r:=LazarusResources.Find('unit1.ru','PO');
  POFile:=TPOFile.Create;
  try
    POFile.ReadPOText(r.Value);
    Result:=Translations.TranslateUnitResourceStrings('unit1',POFile);
  finally
    POFile.Free;
  end;
end;

initialization
  {$I unit1.lrs}
  • Выполните функцию TranslateUnitResourceStrings в начале программы. Вы можете это сделать в секции initialization.

Локализация IDE

Файлы

Файлы .po находятся в каталогах исходников IDE Lazarus:

  • lazarus/languages перевод для IDE
  • lcl/languages/ перевод для LCL
  • ideintf/languages/ перевод для интерфейса IDE

Переводчики

  • Немецкий перевод поддерживает Joerg Braun.
  • Финский перевод поддерживает Seppo Suurtarla
  • Русский перевод поддерживает Максим Ганецкий

Если вы хотите начать новый перевод, задайте вопрос в рассылку, может уже кто-то над ним работает.

Пожалуйста, прочитайте внимательно: Переводы

Future work / ToDos

IDE Development: Переводы, i18n, lrt файлы, po файлы