Локализация программы

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Re: Локализация программы

Сообщение devels » 11.01.2011 21:44:41

yantux писал(а):Мне кажется локализация с po файлами не очень удобная. У обычного объекта lazarus есть поля caption, hint и им подобные. При смене пользователем языка, надо менять только их значения. Поэтому почему бы не сделать метод, который бы читал с помощью tmemo, текствой файл с нужной локализацией и прописывал значения полей caption, hint?

Что если сделать обычный текстовой файл eng.lng, ru.lng, fr.lng, в которых будет текст типа "settings=настойки". Т.е. сделать нечто похожее на словарь или базу данных соответствия английского слова национальному? Соответсвенно, в главном классе tform1 сделать метод, в который бы приваивал полям caption, hint новые значения при смене языка пользователем.


Этот вариант подходит тогда, когда надо делать перевод уже готовой программы и у тебя есть исходники, я так делал, для каждой формы свой ini файл, в каждом ини <название компонента>=<его перевод>.

Но тут же появляются сложности с сообщениями и другими "некомпонентными" сущностями. Поэтому самый лучший вариант - это функция, которая переводит английскую фразу (или языковой код) на все языки.
devels
постоялец
 
Сообщения: 137
Зарегистрирован: 01.09.2010 12:14:38

Re: Локализация программы

Сообщение yantux » 11.01.2011 22:08:49

По поводу локалзации у меня созрел ещё один вариант: использование TIniPropStorage, он на панели Misc:

Код: Выделить всё
procedure TForm1.set_lng;
begin
   self.IniPropStorage_load_lng_text.IniFileName:='./local.txt';
   self.IniPropStorage_load_lng_text.IniSection:=self.ComboBox_select_lng.Items.Strings[self.ComboBox_select_lng.ItemIndex];

   self.Caption:=self.IniPropStorage_load_lng_text.ReadString(self.name+'.caption','self.Caption');
   self.Hint:=self.IniPropStorage_load_lng_text.ReadString(self.name+'.hint','self.Hint');

end;


В поле self.IniPropStorage_load_lng_text.IniSection записываем имя языка.

Я думаю буду использовать этот способ в своём SimpleCalc. Ну может self.name+'.caption' и self.name+'.hint' поменяю на 'self.hint' и 'self.caption'. Тогда при смене языка можно вызвать одну функцию и не париться. А весь перевод в файле с несколькими языками в формате ini файла. По моему должно быть элегантно и практично. Какие будут мнения?
yantux
постоялец
 
Сообщения: 133
Зарегистрирован: 29.10.2007 16:02:33
Откуда: Санкт-Петербург

Re: Локализация программы

Сообщение zub » 11.01.2011 23:27:05

По поводу локалзации у меня созрел ещё один вариант:

Зачем? все описаные выше способы прекрасно реализуются на PO. У PO кроме того плюсом встроенная поддержка лазарем и наличие программ для редактирования.
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Локализация программы

Сообщение yantux » 13.01.2011 23:46:05

zub писал(а):Зачем? все описаные выше способы прекрасно реализуются на PO. У PO кроме того плюсом встроенная поддержка лазарем и наличие программ для редактирования.


Мне кажется, с ini файлами всё понятнее и проще.
yantux
постоялец
 
Сообщения: 133
Зарегистрирован: 29.10.2007 16:02:33
Откуда: Санкт-Петербург

Re: Локализация программы

Сообщение alexs » 14.01.2011 14:23:19

yantux писал(а):Мне кажется, с ini файлами всё понятнее и проще.

Я по началу тоже их использовал. Но когда попробовал po-файлы - понял, что был не прав.
Кол-во кода уменьшается, а читабельность выше.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Re: Локализация программы

Сообщение zub » 14.01.2011 15:42:58

У меня в результате получилось так:
всем фразам что читаются из вне присваивается уникальный идентификатор в зависимости от ситуации, напримет имя acnion+'~'+имя параметра. фразы переводятся с учетом этого идентификатора, т.е. у одной и тойже фразы в разных местах может быть разный перевод

Кусок непереведенного PO файла с caption и hint для экшена команды LOAD
#: ACN_LOAD~caption
msgid "Open ..."
msgstr ""

#: ACN_LOAD~hint
msgid "Open drawing"
msgstr ""



при запуск программы с ключем UpdatePO - в момент перевода проверяется наличие идентификатора в PO файле, если его нет - фраза добавляется,
а перед записью файла на диск из него выкидываются все фразы перевод которых небыл запрошен в данной сессии программы, кроме фраз добавленых лазарем (различаются по идентификатору - в моих разделитель "~" у лазаря ".").
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Локализация программы

Сообщение yantux » 16.01.2011 02:02:08

Лично я пока буду делать в таком духе, по крайней мере я понимаю, что происходит и контролирую процесс, ро файлы пока переварить не могу

Код: Выделить всё
procedure TForm_SimpleCalc.set_lng( in_name : string );
begin

   self.IniPropStorage_load_lng_text.IniSection:=in_name;

   self.Caption:=self.IniPropStorage_load_lng_text.ReadString('Form_SimpleCalc.caption','Form_SimpleCalc.Caption');
   self.Hint:=self.IniPropStorage_load_lng_text.ReadString('Form_SimpleCalc.hint','Form_SimpleCalc.Hint');

   self.MenuItem_file.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.MenuItem_file.Caption','self.MenuItem_file.Caption');
   self.MenuItem_settings.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.MenuItem_settings.Caption','self.MenuItem_settings.Caption');
   self.MenuItem_help.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.MenuItem_help.Caption','self.MenuItem_help.Caption');
   self.MenuItem_about.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.MenuItem_about.Caption','self.MenuItem_about.Caption');

   self.GroupBox_inout.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.GroupBox_inout.Caption','self.GroupBox_inout.Caption');

   self.Memo_log.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.Memo_log.Hint','self.Memo_log.Hint');

   self.Edit_input_data.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.Edit_input_data.Hint','self.Edit_input_data.Hint');

   self.Button_log_reset.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.Button_log_reset.Caption','self.Button_log_reset.Caption');
   self.Button_log_reset.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.Button_log_reset.Hint','self.Button_log_reset.Hint');

   self.TabSheet_func_arif.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_func_arif.Caption','self.TabSheet_func_arif.Caption');
   self.TabSheet_func_arif.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_func_arif.hint','self.TabSheet_func_arif.hint');

   self.TabSheet_func_trinogom.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_func_trinogom.Caption','self.TabSheet_func_trinogom.Caption');
   self.TabSheet_func_trinogom.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_func_trinogom.hint','self.TabSheet_func_trinogom.hint');

   self.TabSheet_convert_data.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_convert_data.Caption','self.TabSheet_convert_data.Caption');
   self.TabSheet_convert_data.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_convert_data.hint','self.TabSheet_convert_data.hint');

   self.TabSheet_const.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_const.Caption','self.TabSheet_const.Caption');
   self.TabSheet_const.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_const.hint','self.TabSheet_const.hint');

   self.TabSheet_logarifmy.Caption:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_logarifmy.Caption','self.TabSheet_logarifmy.Caption');
   self.TabSheet_logarifmy.Hint:=self.IniPropStorage_load_lng_text.ReadString('self.TabSheet_logarifmy.hint','self.TabSheet_logarifmy.hint');

end;
yantux
постоялец
 
Сообщения: 133
Зарегистрирован: 29.10.2007 16:02:33
Откуда: Санкт-Петербург

Re: Локализация программы

Сообщение alexs » 16.01.2011 12:18:39

yantux
Не красиво.
Если не хочешь po - посмотри как я раньше делал (в японских кросвордах на шаманграде до 20-й ревизии этот код)

Код: Выделить всё
  Caption:=Localize('Params');
  ShowStartupNewCheckBox.Caption:=Localize('Open new game at startup');

т.е. в функцию я передавал именно тот текст который подлежал переводу.
а сама функция была:

Код: Выделить всё
var
  LngList:TStringList = nil;

function Localize(const Caption: string): string;
begin
  Result:=LngList.Values[Caption];
  if Result='' then
    Result:=Caption;
end;

procedure LoadLocalize(const AFileName: string);
var
  S:string;
begin
  S:=ExtractFileDir(ParamStr(0))+DirectorySeparator+'languages'+DirectorySeparator+AFileName;
  if FileExists(S) then
  begin
    LngList.Clear;
    LngList.LoadFromFile(S);
    LanguageFN:=AFileName;
  end;
end;

и образец файла локализации:
Код: Выделить всё
Params=Параметры
Exit=Выход
Print=Печать
System=Система
;
Easy=Легко

для каждой локали - свой файл
ещё там есть в параметрах выбор файла для локализации
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Re: Локализация программы

Сообщение VAshot » 18.01.2011 11:09:09

Есть небольшая программка. Потребовалось добавить поддержку локализации.
Включил опцию i18n, в некоторые модули добавил ResourceString, создал дополнительно модуль специально для хранения всех ResourceString.

Ситуация.
- Не для всех форм создаются файлы *.lrt;
- Не все созданные *.rst добавляются к .po файлу.

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

Это делал... всё равно остались формы без этих файлов ((

Как заставить включать в .po все файлы .rst?

Что делаю не так или что не делаю так?


0.9.28.2, Win7 64.
Аватара пользователя
VAshot
постоялец
 
Сообщения: 128
Зарегистрирован: 01.11.2007 12:31:21
Откуда: Пермь

Re: Локализация программы

Сообщение zub » 18.01.2011 12:08:37

- Не все созданные *.rst добавляются к .po файлу.

файлы к проекту добавлены? (shift+F11)
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Локализация программы

Сообщение VAshot » 19.01.2011 14:35:17

zub писал(а):
- Не все созданные *.rst добавляются к .po файлу.

файлы к проекту добавлены? (shift+F11)

Эх.. .lrt действительно не создавались из-за этого.
К проекту нужно добавить сами .rst? Сейчас в .po добавляется .rst только от главной формы.
Аватара пользователя
VAshot
постоялец
 
Сообщения: 128
Зарегистрирован: 01.11.2007 12:31:21
Откуда: Пермь

Re: Локализация программы

Сообщение zub » 19.01.2011 14:47:32

к проекту нужно добавить все *.pas в которых присутствуют ResourceString и пересобрать. загляни в проект\инспектор проекта - там видно что добавлено
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Локализация программы

Сообщение VAshot » 19.01.2011 14:58:34

И по переводу еще.
Сделал следующее.
Код: Выделить всё
unit UResourceStrings;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Translations, gettext, PoTranslator, FileUtil;

  function MyTranslateUnitResourceStrings(unitName: String): boolean;
  function MyTranslate(unitName, lang: String): boolean;

var
  varLang          : String;
  varFallbackLang  : String;
  varCurrentPOPath : String;

const
  constPODir       = 'languages';
  constPOSmallName = 'izhitsa.%s';
  constPOName      = constPOSmallName + '.po';
  constPOPath      = constPODir + '/' + constPOName;

ResourceString
  rsFileVersion            = '%s от %s';
  rsLabelAuthor            = 'Автор:';
  rsLabelVersion           = 'Версия:';

implementation

function MyTranslateUnitResourceStrings(unitName: String): boolean;
var
  r: TLResource;
  POFile: TPOFile;
  POStream: TStream;
begin
  GetLanguageIDs(varLang, varFallbackLang);

  MyTranslate(unitName, varFallbackLang);
end;


function MyTranslate(unitName, lang: String): boolean;
var
  r: TLResource;
  POFile: TPOFile;
  //POStream: TStream;
begin
  r := LazarusResources.Find(Format(constPOSmallName, [lang]), 'PO');

  // Если найден ресурс с переводом на нужный язык
  if (r <> nil) then begin
    POFile := TPOFile.Create;
    //POStream := TStream.Create;
    try
      POFile.ReadPOText(r.Value);
      Translations.TranslateUnitResourceStrings(unitName, POFile);
      LRSTranslator := TPoTranslator.Create(Format(constPOPath, [lang])); //TODO: сделать загрузку из ресурса
      //LRSTranslator := TPoTranslator.Create(POStream);
    finally
      POFile.Free;
      //POStream.Free;
    end;
  end;

  // Если не найден ресурс с переводом на нужный язык, ищем файл
  if (r = nil) then begin
    if (FileExistsUTF8(Format(constPOPath, [lang]))) then begin
      TranslateUnitResourceStrings(unitName, Format(constPOPath, [lang]));
      LRSTranslator := TPoTranslator.Create(Format(constPOPath, [lang]));
    end
    else begin
      // Если нет ни ресурса ни файла на нужном языке, то ищем перевод на английский
      if (lang <> 'en') then begin
        MyTranslate(unitName, 'en');
      end;
    end
  end;
end;

initialization
  {$I uresourcestrings.lrs}

end.


И вызываю MyTranslateUnitResourceStrings
Код: Выделить всё
initialization
  {$I main.lrs}
  MyTranslateUnitResourceStrings('Main');


Самое интересное, что переводится интерфейс и на других формах, хотя в их коде нет ни слова о переводе (все формы не проверял, но с одной именно так).
Как так? Т.е. там не нужно делать вызов LRSTranslator := TPoTranslator.Create ?


Помогите, пожалуйста. Как преобразовать вызов (код PoTranslator из http://wiki.lazarus.freepascal.org/Tran ... rograms/ru)
Код: Выделить всё
LRSTranslator := TPoTranslator.Create(Format(constPOPath, [lang]));

чтобы он воспринял переменную POFile? Точнее как её записать в TStream.

Добавлено спустя 7 минут 39 секунд:
zub писал(а):к проекту нужно добавить все *.pas в которых присутствуют ResourceString и пересобрать. загляни в проектинспектор проекта - там видно что добавлено

Это сделана. Нашел баг. Ресурсы добавляются только из корневого каталога или каталога, где лежит главная форма (не проверял).
А у меня главная форма в корне, а все остальные ресурсы в подкаталоге....

Есть такой у кого баг?

Добавлено спустя 5 минут 35 секунд:
Проверил, у меня добавляются только из корневого каталога файлы ресурсов.
Это у всех так или у меня версия IDE старовата? Иль настройки какие виноваты?
Аватара пользователя
VAshot
постоялец
 
Сообщения: 128
Зарегистрирован: 01.11.2007 12:31:21
Откуда: Пермь

Re: Локализация программы

Сообщение Mr.Smart » 19.01.2011 15:12:19

VAshot писал(а):чтобы он воспринял переменную POFile? Точнее как её записать в TStream.


Измените класс TPoTranslator с возможностью чтения из потока:
Код: Выделить всё
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);
  constructor Create(aStream: TStream);
  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;

constructor TPoTranslator.Create(aStream: TStream);
begin
  inherited Create;
  FPOFile:=TPOFile.Create(aSteram);
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.


Добавлено спустя 7 минут:
В таблице перевода строки хранятся для всего проекта. Т.ч. достаточно один раз инициализировать LRSTranslator и вызвать функцию перевода.
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Локализация программы

Сообщение VAshot » 19.01.2011 15:29:09

Mr.Smart писал(а):Измените класс TPoTranslator с возможностью чтения из потока:

До этого сам дошел, но меня поставило в тупик как PDOfile (POFile.ReadPOText(r.Value);) преобразовать в "объект" (поток), который нужно подставить в LRSTranslator := TPoTranslator.Create(???) ?

Mr.Smart писал(а):Добавлено спустя 7 минут:
В таблице перевода строки хранятся для всего проекта. Т.ч. достаточно один раз инициализировать LRSTranslator и вызвать функцию перевода.

Понял, спасибо.
Аватара пользователя
VAshot
постоялец
 
Сообщения: 128
Зарегистрирован: 01.11.2007 12:31:21
Откуда: Пермь

Пред.След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 15

Рейтинг@Mail.ru