IDEA - 'Universal' translation method

Discuss and announce AkelPad language modules
  • Author
  • Message
Offline
Posts: 147
Joined: Fri Feb 08, 2008 6:41 pm
Location: British Columbia, Canada

IDEA - 'Universal' translation method

Post by Surveyor »

Instructor,

Current translations are compiled into resource files (English/Russian are built-in), but plugins are only in English(?). I have seen a method used by some programs which uses a text file for each language similar to:

Code: Select all

English - translation strings
str001="&File"
...
str013="Pa&ge setup..."
etc.
Strings are loaded at startup (or as needed?) from the appropriate language flie. If this method were used, plugins could also be incorporated and additional translations or corrections could be EASILY made by anyone, without a re-compile! Text file would have to be 1200 UTF-16 LE (or similar) to allow foreign language characters.

Problem is, of course, that this would mean a major(?) change to AkelPad and plugin structure. Might be just too much work, or not practical...

Just a thought for possible future versions... Comments, anyone?

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

EN (sorry... it's Google-translate text :oops: )

This idea is already hovering in the brain ...)

1. Create a list of all the phrases used by the program, save them to file for a particular language.
For example it can be done in the image and likeness of INI-file:

Code: Select all

IDC_COMMANDNAME = "name of command" or IDN_COMMANNUMBER = "name of command"
example:

Code: Select all

IDC_NEW = "New" or IDN_4101 = "New"
------------

Code: Select all

IDL_LABLE = "lable"
example:

Code: Select all

IDL_MENU_FILE = "File"
------------

Code: Select all

IDMC_MESSAGECAPTION = "title of the message box" 
example:

Code: Select all

IDMC_AKELPAD = "AkelPad message ..."
------------

Code: Select all

IDMT_MESSAGETEXT = "text message" 
------------
etc. ..

2. Put in folder AkelFiles\Langs these files for different languages.
If files are missing, the program takes the English (probably need to keep fnutri, as now).

3. Use in the program, plugins, scripts defined messages from lng-files.
Add to Scripts-plug method, which returns to the text on ID._. .., respectively, the current language, for example:

Code: Select all

var txt = AkelPad.GetLangString (IDMT_QREPLACEFILE);
txt enrolled in the variable value to the message: "File already exists, replace it it?"
By the way, could be in the messages indicate variables, such as:

Code: Select all

IDMT_QREPLACEFILE = "File %VAR1% already exists, replace it?"
and then, having received the message already in the program/plugin/script to replace %VAR1%...n on corresponding values.

In my opinion, not a bad idea :wink:

P.S. Here, I once suggested that the format of the menu ... but this work is more serious and global.


RU
Подобная идея уже витала в мозгу...)

1. Создать список всех фраз, используемых программой, сохранять их в файле для определённого языка.
Например это можно сделать по образу и подобию INI-файлов:

Code: Select all

IDC_COMMANDNAME = "Имя команды" или IDN_COMMANNUMBER = "Имя команды"
т.е. это выгледело бы так:

Code: Select all

IDC_NEW = "Новый" или IDN_4101 = "Новый"
------------

Code: Select all

IDL_LABLE = "надпись"
пример:

Code: Select all

IDL_MENU_FILE = "File"
------------

Code: Select all

IDMC_MESSAGECAPTION = "заголовок окна сообщения"
пример:

Code: Select all

IDMC_AKELPAD = "AkelPad message..."
------------

Code: Select all

IDMT_MESSAGETEXT = "текст сообщения"
------------
и т.д...

2. Положить в папке AkelFiles\Langs эти файлы для разных языков.
Если файлов нет, то программа берёт английский (видимо надо хранить фнутри, как и сейчас).

3. Использовать в программе, плагинах, скриптах установленные сообщения из lng-файлов.
Добавить в Scripts-плагин метод, который возвращал бы текст по ID._..., соответственно текущему языку; например:

Code: Select all

var txt = AkelPad.GetLangString(IDMT_QREPLACEFILE);
в переменную txt записалось бы значение сообщения: "Файл уже существует, заменить его?"
Кстати, можно было бы в сообщениях указывать переменные, например:

Code: Select all

IDMT_QREPLACEFILE = "Файл %VAR1% уже существует, заменить его?"
а затем, получив текст сообщения, уже в программе/плагине/скрипте заменять %VAR1%...n на соотвтетствующие значения.

По моему, неплохая идея ;)

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

VladSh, подобным приемом пользуется Миранда, только значительно гибким.
Ты предлагаешь давать текстовым надписям идентификаторы, а внутри искать по ним подгружать строку, а в случае отсутствия такой - брать где-то строку по умолчанию. Значит нужно хранить и идентификатор, и значение по умолчанию. Не экономно это.
Потому Миранда делает так: она не хранит никаких идентификаторов вообще. Если ей нужно вывести какой-то локализированный текст, ключом для поиска перевода этого текста служит сам текст, и если он в переводе не найдет - подставляется оригинал. Таким образом не хранятся нигде лишние данные. Файлы локализации - обычные текстовые файлы лежащие в корне приложения, и имеющие простой ini-подобный формат.

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

FeyFre
Таким принципом пользуются почти все проги, в которых есть локализация.

Более гибким или менее - это смотря с какой стороны посмотреть.
Конечно, только для целей локализации непонятно, зачем идентификаторы... если разобрать, к примеру, меню AutoCAD'а, то видно, что за идентификаторами команд идут не только наименования (они - только малая часть общего механизма), а и связь с соответствующим лейблом (как здесь), с текстом описания, выводимым в строке состояния при наведении курсора мыши, когда пункты меню становятся недоступными, также можно напротив идентификатора команды писать лёгкие скрипты на макроязыках или указывать вызов внешних скриптов/прог. Потом для вызова команды не надо ничего искать, - она просто вызывается по единственному идентификатору, а весь этот "паровоз" тянется следом.
Всё это становится понятным, когда пункты меню со своей кучей настроек используется в разных меню.

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

P.S. Кроме AutoCAD'а можно посмотреть как устроены файлы lng во многих прогах: Opera, KMPlayer и т.п., просто у него они лучше всего проработаны.

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

VladSh
Если же смотреть с точки зрения удобства, то оперирование в программном коде огромными "стрингами" вместо идентификаторов - сомнительное удовольствие.
А $define зачем? C - без препроцессора в практичном применении не возможен. Поэтому оперирование огромными "стрингами" сводится к оперированию дефайнов их заменяющих.
Дэфайны ложатся в отдельном H-файле, в котором только они и находятся, типа этого:

Code: Select all

#ifndef __TRANSLATE_H__
#define __TRANSLATE_H__
#define MESSAGE_NOMOREFOUND _T("No more found")
#define MESSAGE_NOFILEFOUND _T("File not found. Create?")
....
#endif //__TRANSLATE_H__
Это файл инкладится в исходник и компилируется.
Также этот же файл является входящим файлом для утилиты генерации шаблонов файлов перевода(файл структурирован и распрасить его не проблема для составления шаблона в который осталось ввести перевод)

Offline
Posts: 57
Joined: Sun Jul 27, 2008 6:47 am
Location: slovakia

Post by pvagner »

I am not sure your approach considers it but I think if we are about to introduce kind of language files, then they should be stored in UTF-8 encoded files and then they can be properly displayed on any operating system.
Currently even russian plugins need seperate language strings for unicode and ansi versions of Windows.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

Можно подумать насчет унифицированного метода локализации Акеля и плагинов, желательно без жесткого вкомпиливания ресурсов. К примеру, проверенный временем gettext.

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

Fr0sT
Подумать то можно, и уже думалось(мной точно), но всё упирается в отсутствии надобности "по зарез" а значит и наличия самого воплотителя.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

Ну, я думаю, иностранные не-англоязычные пользователи были бы крайне рады, если бы плагины можно было переводить на их языки.
Чем хорош gettext - английский присутствует всегда, даже если все файлы с локализацией затерялись.

Можно такую полумеру сделать:
1) Локализация хранится в ресурсах, в string table - только для одного языка. В бинарниках, скачиваемых в виде архивов, стоит дефолтная локализация.
2) Переводы поставляются в виде скомпилированных res файлов, содержащих опять же string table.
3) Вместе с ними поставляется маленькая утилита, которая заменяет ресурсы в exe/dll на содержимое res файла.
4) В инсталлятор зашита эта утилита и все локализации соответственно основному языку, после установки он запускает утилиту, заменяя строковые ресурсы

Плюсы:
1) Переделать понадобится только GetLangString, плюс выделить строки в отдельный ресурс.
2) Сделать утилиту по замене ресурсов и слегка дополнить инсталлятор тоже нетрудно
3) С помощью ResHacker и подобных утилит любой сможет сделать локализацию для своего языка
4) С помощью утилиты замены также можно будет вручную локализовать любой плагин (если ставится не через инсталлятор)

Минусы:
1) Ну, единственное, что я вижу, - это затруднится синхронизация изменений. Но тут уж ничего не поделаешь.

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

1) По методу GETTEXT перевод в ресурсы в таблицу строк не всунешь.GETTEXT работает со строковым ключом, а таблица строк - целое число.
Поэтому остальные пункты сразу отпадают.
Тут скорее всего уместно сделать в виде пользовательских ini-файлов. Типа как в Миранде.

Code: Select all

[строка для перевода]
язык1=перевод1
язык2=перевод2
Все ini файлы кучковать в одном месте, и обрабатывать пачкой. Тогда даже секртетарь сможет сделать себе перевод.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

FeyFre
ну я предложил либо gettext, либо промежуточную меру с ресурсами. Эти два метода никак не связаны между собой.
Что мне не нравится в предложенном тобой методе:
1) Дублирование ключей в коде и в ini. Кроме того, при изменении ключевой строки надо будет также исправить ВСЕ переводы. Не знаю точно насчет gettext, но вроде бы у него есть утилиты-сканеры, которые выдирают из исходника все локализуемые строки. Проблема с необходимостью изменения всех переводов, тем не менее, остается.
2) Строковый ключ ужасно медленный. Тогда надо будет ещё и хэш прикручивать, что уже реализовано в gettext (зачем изобретать велосипед?)

Плюс его - в легкости редактирования, это да.

Если же использовать ресурсы, то для каждого бинарника можно сделать так:
== LangDefs.inc ==

Code: Select all

#define LANG_STR_FOO 1
#define LANG_STR_BAR 2
...
== LangRes.rc ==

Code: Select all

#include "LangDefs.inc"
STRINGTABLE DISCARDABLE
BEGIN
  LANG_STR_FOO "foo"
  LANG_STR_BAR "bar"
END
В таком случае в коде идентификация будет по кодам LANG_STR_* (будет подключаться LangDefs.inc), а переводы будут предоставляться переводчиками в виде LangRes.rc, которые для юзеров потом будут компилиться в *.res

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

2) Строковый ключ ужасно медленный. Тогда надо будет ещё и хэш прикручивать, что уже реализовано в gettext (зачем изобретать велосипед?)
Мы что собираемся делать одновременно 1млн. запросов на перевод? Жалко одной микросекунды?
Строковый ключ ужасно медленный. Тогда надо будет ещё и хэш прикручивать, что уже реализовано в gettext (зачем изобретать велосипед?)
Ресурсы ограниченны только 65534 значениями - перспективы ноль. Истощение в обозримом будущем.
Дублирование ключей в коде и в ini.
Не страшно. Не думаю что максимум лишнего мегабайта при сегодняшних объемах кому-то позарез не будет хватать.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

Ресурсы ограниченны только 65534 значениями - перспективы ноль. Истощение в обозримом будущем.
На каждый тип ресурса или всего? Потому что если на каждый, то это фигня. Файл перевода в Опере имеет всего 4.5 тысячи строк. А это ого-го какая немаленькая программа.
Не страшно
Не в размере дело, а в усложнении накатывания изменений

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

Шо тут думать, прыгать надо. Вверху предлагались ini-файлы, хоть даже вариант FeyFre (он удобнее тем, что нагляднее).
То есть сделать микс из 2-х вариантов:
- делать локализованную версию на определённый язык или как это сделано сейчас в АкелПаде (я до сих пор ни разу не ставил многоязычную версию, но предполагаю, что каким-то образом это там работает);
- и одновременно, если в специальной папке лежат файлы других языков, то в проге давать выбирать и из этих языков, так многие проги делают.
В проге переключил на каракалпакский, и р-раз, плаги тоже начали на каракалпакском отображать, а где нету, ну значит английский. А кто возмущаться начнёт, что ему не нравится, ты ему "А ты записался в добровольцы? Ты чем помог фронту?"
То есть, как вариант, в проге 1 язык, который вшит, а если пользователь хочет чесать левой ногой правое ухо, то пусть ini-файлы добавляет и выбирает. Да хоть пусть скрипт напишет и при каждой загрузке проги рандомно язык подставляет.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

В многоязычном Акеле строки и диалоги подгружаются из языковой dll. Едва ли этот способ подходит для плагинов.
Post Reply