Translations / i18n / localizations for programs/ru

From Free Pascal wiki
Revision as of 11:06, 3 August 2011 by Chronos (talk | contribs)

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) 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' встречается у нескольких строковых констант: <Delphi>

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

</Delphi> После преобразования из .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 что и добавляемая, то считается, что добавляемая запись уже переведена, и она просто не добавляется.

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

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

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

 dlgEdColor = 'Syntax highlight';

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

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

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

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

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

 dlgEdColor = 'Colors';

</Delphi> в результате запись в .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) в разделе инициализации одного из модулей.

<Delphi> ... uses

 ...
 LResources;

... ... initialization

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

</Delphi>

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

<Delphi> 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. </Delphi>

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

<Delphi> ... uses

  ...
  DefaultTranslator;

</Delphi>

который будет автоматически искать .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 для перевода каждого модуля. Например:

<pascal>

   {Прежде всего добавьте модули "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;

</pascal>

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

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

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

Создастся файл unit1.lrs начинающийся строкой <pascal> LazarusResources.Add('unit1.ru','PO',[

 ...

</pascal>

  • Добавьте в код:

<pascal> 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}

</pascal>

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

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

Файлы

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

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

Переводчики

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

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

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

Future work / ToDos

IDE Development: Translations, i18n, lrt, po files