Текущая версия: 1.3.1
Программа позволяет выполнять автоматизированную локализацию для проектов.
Достоинства:
Недостатки:
В этом параграфе описывается порядок локализации в простейшей форме. Предполагается, что для многих проектов
этого будет достаточно. Если же нет, все тонкости и вся непонятная терминология объясняются дальше.
У вас должен быть файл проекта Microsoft Visual Studio 2003, 2005... ну и, наверное, выше (не проверял).
Либо должен быть просто список файлов в файле с расширением .lst. Определите в программе специальный
префикс:
Вызовите программу-локализатор:
Пример:
Среди параметров идет сначала префикс, потом файлы, связанные с локализацией. Смысл файла определяется
его расширением:
После того, как вы вызвали локализатор в первый раз, в исходном коде проекта произойдут изменения.
Вместо литералов вида
Теперь добавьте в проект файл *.cpp, упомянутый выше, и еще вот эти файлы загрузчика
(всего в проект надо добавить три файла - два *.cpp и один *.h).
Добавьте куда-нибудь в начало загрузку строк из одного из сгенерированных *.bin-файлов (по вашему выбору)
примерно вот так:
Теперь добейтесь успешной компиляции вашего проекта. Все, на этом локализация закончена.
После этого вы можете время от времени переводить строки в *.loc-файлах (в дополнительных, в эталонном
переводить не надо) или исправлять их и запускать локализатор с теми же параметрами.
Также вы можете добавлять в программу новые модули и помечать в них строки префиксом, и при
очередном запуске локализатора эти строки будут извлечены и распределены по файлам локализации.
Рекомендуется такой порядок действий. Сначала программируете как обычно,
в кавычках пишете текст не английском и помечаете префиксом то, что потребует перевода. Когда
таких строк накопится много, вызываете локализатор. Потом смотрите в дополнительные файлы
и переводите все, что помечено маркером /!TRANSLATE!/, убирая маркеры. Потом еще раз запускаете
локализатор и компилируете проект.
Вот и весь "быстрый старт". Дальше идут детали и подробности.
Префикс - это короткий идентификатор, например, дальше для определенности используется префикс LS.
По префиксу локализатор распознает локализованные строки и строки, подлежащие локализации.
Префикс однозначно определяет локализацию. В одной программе может быть несколько локализаций, которые
отличаются по префиксам.
Префикс должен быть объявлен где-нибудь в общем хэдере как пустой define:
Следующие файлы используются для локализатора.
[proj]. Файл проекта vcproj. Нужен для того, чтобы извлечь из него список файлов проекта.
[lst]. Список файлов, подлежащих локализации (просто имена файлов, каждый на отдельной строке).
Расширение .lst. Обычно извлекается локализатором из [proj].
[src]. Исходные файлы проекта. Обычные исходники, в которых могут быть локализованные строки.
Локализатор сканирует такие файлы, а их имена берет из [lst].
[loc]. Главный файл локализации. Расширение .loc. Этот файл содержит идентификаторы локализованных
строк и их содержимое. Формат файла:
Идентификатор - это последовательность символов, среди которых могут быть латинские буквы, цифры и
знак подчеркивания. Последовательность может начинаться с цифры. Внутри программы к этим идентификаторам
добавляется префикс и два знака подчеркивания. Например, идентификатор 'OK' превращается в 'LS__OK'.
Содержимое - произвольная строка. Здесь допускаются любые символы, включая '\0', переводы строк и прочее.
При загрузке в конец будет добавлен еще один '\0', так что вручную этого делать не нужно.
Комментарий - необязательная строка комментария. Каждая строка может сопровождаться одним комметарием
перед ней. Также программа может генерировать специальные комментарии вида:
Строка "\r\n/END/\r\n" - признак конца содержимого, такая строка не может присутствовать в содержимом.
Выше приведен так называемый "raw"-формат файла. В строках все спец-символы, включая нулевой, записываются
"as is". Это может быть иногда удобно, а иногда - нет (не всякий текстовый редактор сможет корректно
работать с таким файлом). Можно использовать альтернативный "escape"-формат:
Здесь содержимое строки кодируется обычной C/C++ строкой с применением escape-символа "\". Обрамляющие
кавычки не нужны, пробелы вокруг разделителя "=" оставлять не следует. При желании в одном файле локализации
можно смешивать строки в обоих форматах.
Главный файл локализации должен содержать содержимое локализованных строк для базового языка. Очень
желательно, чтобы базовым языком был английский. Тогда локализатор сможет автоматически сгенерировать
осмысленные идентификаторы, приемлемые с точки зрения синтаксиса C++. Также можно использовать
русский язык в кодировке Windows-1251, в этом случае идентификаторы будут сгенерированы с применением транслита.
[loc+]. Дополнительный файл локализации. Расширение .loc. Все аналогично [loc], но содержимое строк идет на другом
языке (перевод). Идентификаторы переводить нельзя. Дополнительные файлы локализации получаются из [loc] и
затем синхронизируются с ним.
[cpp]. cpp-файл локализации. Расширение .cpp. Этот файл содержит все локализованные строки - по отдельности
и списком. Его надо включить в проект. Для всех языков применяется один и тот же cpp-файл локализации.
[bin]. Бинарный файл локализации. Расширение .bin. В нем находится содержимое строк. Для каждого языка
- свой [bin]. Бинарый файл должен соответствовать [cpp], порядок строк и количество строк в нем те же самые.
Формат:
Локализованная строка - это просто указатель const char *, с которым работают как обычно. При запуске
программы локализованные строки загружаются из [bin], и каждый указатель настраиваются на нужную точку
в памяти. Конкретнее из [bin] в память грузится 'блок строк', а j-й указатель настраивается на адрес:
DATA + OFFSET[j]
Имена локализованных строк имеют вид: <префикс>__<идентификатор>
Например: LS__OK, LS__It_is_a__0.
Это статический массив указателей на указатели на локализованные строки. Он имеет тип
const char **[] и используется только один раз - при загрузке локализации на старте программы.
Его имя имеет вид: <префикс>_all.
Например: LS_all.
Определен в [cpp].
Все локализованные строки данной локализации определены в [cpp]. Этот файл надо включить в проект.
В том же файле определен и массив локализованных строк.
Было решено отказаться от какого-либо хэдера, где перечислены имена локализованных строк.
Такой хэдер пришлось бы включать во многие файлы и малейшие изменения в нем приводили бы
к перекомпилированию значительной части проекта. Вместо этого используются объявления 'extern'
в каждом файле, но их не надо добавлять вручную, это делает локализатор. Единственное (и однократное)
исключение - объявление массива локализованных строк перед вызовом Localizer::load (см. ниже).
Имеют вид: <префикс><литерал>
Например: LS"OK"
Таким методом надо отметить те строки, которые подлежат локализации (этот процесс отчасти автоматизирован).
Исходник с такими отметками нормально компилируется, поскольку LS - это #define, определенный как
пустышка. Таким образом, можно заранее отмечать места локализации, но саму локализацию отложить
до подходящего момента.
Если в проекте есть несколько частей, для каждой из которых свой модуль локализации, то надо сделать
разные префиксы для них.
Локализация собственной программы загружается через объект Localizer, функцию load.
Объект Localizer должен существовать, пока требуются локализованные строки, поэтому лучше его сделать статическим.
1-й параметр функции - имя бинарного файла локализации.
2-й параметр функции - массив локализованных строк (упоминается выше и см. пример ниже).
Возвращаемое значение - true при успехе, false при ошибке. Вызов load надо поместить в начало программы.
Пример:
В этом примере программа использует локализацию с префиксом LSI и загружает ее из файла interval_loc.bin.
Список шагов длинный, но это не страшно, поскольку большинство из них умещаются в один bat-файл, пример
которого есть в конце. Исполнение утилиты разделено на эти фрагменты, чтобы заложить в процесс локализации
элементы модульности и гибкость. В повседневной работе над проектом используется только bat-файл,
и задумываться о множестве мелких шагов не нужно.
Шаг 0. Добавить localizer.cpp и localizer.h в проект.
Шаг 1. Придумать уникальный префикс, который будет идентифицировать локализацию. Выберите короткий префикс,
т.к. его придется писать много раз. Но это должен быть униакльный идентификатор, который не используется в
программе.
Шаг 2. Определить префикс. Например:
Эту директиву надо вставить в какой-то хэдер, желательно 'precompiled'.
Шаг 3. Теперь надо составить список файлов, подлежащих локализации. В список должны войти все файлы проекта,
где встречается или может встретиться в будущем метка LS. Список можно составить вручную или автоматически.
Можно использовать стандартную команду dir, например: dir /B *.cpp >my.pr.loc
Но лучше использовать сам локализатор, указав ему путь к файлу проекта *.vcproj. Это лучше, поскольку тогда
в список не попадут случайные файлы - которые находятся в папке, но не относятся к проекту, либо в проекте,
но исключены из сборки. Их пришлось бы убирать вручную.
Пример вызова локализатора для составления списка:
localizer.exe -list greenwin.vcproj greenwin.lst
Формат:
localizer.exe -list [proj] [lst]
Шаг 4. Указать строки, подлежащие локализации. Надо выполнить поиск по всем файлам проекта литералов (по символу
кавычки) и отметить префиксом все литералы, которые надо локализовать. Конечно, можно отметить так вообще все строки,
но это излишне. Следует отмечать только те строки, которые могут выглядеть по-разному на разных языках.
Процесс можно ускорить, применив локализатор:
localizer.exe -mark LS greenwin.lst
Формат:
localizer.exe -mark префикс [lst]
При этом префиксы будут автоматически расставлены перед всеми строками, содержащими латинские буквы, и
останется только удалить лишнее.
Шаг 5. Создание главного файла локализации:
localizer.exe -get LS greenwin.lst greenwin.loc
Формат:
localizer.exe -get префикс [lst] [loc]
На этом этапе происходит анализ исходников [src] (из списка [lst]), и сбор строк, подлежащих локализации в главный
файл локализации [loc]. Весь процесс полностью автоматический.
Возможны следующие ситуации:
- Главный файл локализации [loc] еще не создан. Тогда он считается пустым и создается заново.
- Главный файл локализации [loc] уже есть. Тогда его содержимое загружается и сравнивается с результатами сканирования.
- При сканировании обнаружена уже локализованная строка, которой нет в файле локализации. Например, встречается
идентификатор LS__OK. Локализатор выдаст предупреждение об этом. Причиной может быть то, что была взята устаревшая
версия исходника, где такая строка еще использовалась. Желательно избавиться от таких идентификаторов.
- При сканировании обнаружена уже локализованная строка, которая есть в файле локализации. Это нормальная ситуация.
Значит, файл уже локализовывался раньше, и содержимое сторки хранится в [loc].
- При сканировании обнаружена строка, подлежащая локализации. Ее содержимое эквивалентно одной из строк в [loc].
Это нормальная ситуация. Для всех одинаковых строк используется одно хранилище, так что эта строка просто будет
ссылаться на существующую строку, и для нее будет использован тот же идентификатор.
- При сканировании обнаружена строка, подлежащая локализации. Ее содержимое непохоже ни на одну из строк в [loc].
Это нормальная ситуация. Для такой строки будет автоматически сгенерирован уникальный идентификатор (по буквам
и цифрам из начала строки) и ее содержимое будет сохранено в [loc].
- В [loc] есть строка, которая не упоминается в проекте, и ни одна из строк, подлежащих локализации, не содержит
такого текста. Причина может быть связана с тем, что эта строка была найдена раньше, но с тех пор файл, из которого
она была взята, был удален. Или был удален фрагмент текста, откуда она взята. Такие строки будут удалены из [loc],
чтобы не занимать лишнее место. Будет выдано предупреждение о том, что строка удалена.
Шаг 6. Корректировка главного файла локализации.
Теперь вы можете внести в [loc] ручные исправления, если они нужны. В основном это имеет смысл для автоматически
сгенерированных идентификаторов - если они недостаточно "красивы". Если данный идентификатор уже используется
в программе, его надо исправить и в [src]. При исправлении соблюдайте уникальность идентификаторов, иначе программа
выдаст. А вот сортировку можно не делать, она будет восстановлена при очередном вызове get.
Шаг 7. Локализация строк
localizer.exe -put LS greenwin.lst greenwin.loc
Формат:
localizer.exe -put префикс [lst] [loc]
На этом этапе происходит модификация исходников программы [src]. Все строки, подлежащие локализации, вида
LS"..." заменяются на указатели вида LS__... Кроме того, в каждый файл, где есть такие строки, добавляется
их объявление вида:
Эти объявления генерируются автоматически и автоматически поддерживаются в правильном состоянии при каждом
вызове localizer.exe -put... Место для таких деклараций также подбирается автоматически - перед первой функцией
или объявлением, но после первоначальных комментариев, пустых строк и директив (вроде #include). Если вас
не удовлетворяет автоматически выбраннео место, этот кусок кода можно переместить ниже или выше, и последующие
вызовы localizer.exe -put... станут писать декларации именно туда.
Программа отслеживает, какие файлы действительно надо изменить, и не трогает остальные. Это снижает объем
перекомпиляции.
Вместо -put можно использовать команду -puttest, тогда исходные файлы не будут изменены, а вместо них
будут сгенерированы файлы с расширением .rez. Это может пригодиться, если вы не хотите пока локализовать
проект, но хотите посмотреть, что получится в результате генерации.
Шаг 8. Компиляция программы
Теперь необходимо избавиться от ошибок компиляции, которые могли возникнуть. Основная причина может быть в том,
что литералы были заменены на указатели. При том, что это почти эквивалентные вещи, есть некоторые тонкости,
которые могут сыграть роль. Исправьте программу в нужных местах, добейтесь того, чтобы она компилировалась
(линковаться она пока не будет).
Учтите, что локализация загружается в момент исполнения из функции main/WinMain/DllMain. Не следует использовать
локализованные строки в статических объектах, которые создаются до вызова main/WinMain/DllMain, поскольку
в момент создания статических объектов все локализованные строки указывают на NULL. Однако можно инициализировать
статические объекты указателями на указатели локализованных строк, которые не изменятся.
Например, пусть дана локализованная строка с идентификатором OK и содержимым "&OK".
Это значит, что:
- В [cpp] определен указатель
Сначала указатель LS__OK NULL, но после закрузки локализации будет указывать на строку "&OK". В статическом объекте
можно сохранить &LS__OK, а потом сделать разыменование указателя, когда локализация уже загружена.
Наиболее обычные случаи:
Для исправления эта строка просто убирается, и везде вместо mystring пишется LS__mystring.
Другой случай:
Это исправляется так:
Шаг 9. Генерация cpp-модуля
Для успешной линковки не хватает [cpp]. Сгенерируйте его:
localizer.exe -cpp LS greenwin.loc greenwin_loc.cpp
Формат:
localizer.exe -cpp префикс [loc] [cpp]
Добавьте этот файл в проект. Теперь линковка должна пройти нормально.
Шаг 10. Генерация бинарного файла
localizer.exe -bin LS greenwin.loc greenwin_loc.bin
Формат:
localizer.exe -bin префикс [loc] [bin]
Этот файл надо добавить в дистрибутив. В установленной программе он должен находиться рядом с исполняемым
файлом (в том же каталоге).
Шаг 11. Загрузка локализации
Теперь нужно добавить в программу код загрузки локализованных строк. Пример уже приводился выше:
В общем, надо где-то определить объект Localizer и вызвать у него функцию load. На вход дается
имя [bin] и массив вида <префикс>_all, который определен в [cpp].
Шаг 12. Отладка
После этого локализованная программа готова к запуску. убедитесь, что все работает.
Шаг 13. Обновление локализации.
Если программа изменилась - например, появились новые строки, подлежащие локализации, они были изменены
или убраны, надо выполнить следующие шаги:
- Шаг 3 - составить новый список файлов.
Это шаг можно пропустить, если проект [vcproj] не изменялся.
- Шаг 5 - обновить главный файл локализации.
- Шаг 7 - провести локализацию строк.
Программа проведет все необходимые правки - если они не нужны, то ни один файл не будет затронут и перекомпиляция
не потребуется.
- Шаг 9 - сгенерировать cpp-модуль.
В данном случае файл изменится, но перекомпиляция одного файла не потребует много времени.
- Шаг 10 - сгенерировать bin-модуль.
Для удобства все эти шаги можно собрать в один пакетный ('bat'-файл), например:
Шаг 14. Перевод на другие языки.
После того, как программа отлажена, можно выполнить перевод на другие языки. Для этого надо создать варианты
файла [loc]. В них должны быть те же идентификаторы, но содержимое строк - другое.
Шаг 15. Изменение языка программы.
С полученным переведенным файлом надо выполнить только один шаг (10) - сгенерировать bin-модуль. Потом
надо либо заменить этим bin-модулем прежний, либо модифицировать загрузку локализации так, чтобы загружался
один из bin-модулей в зависимости от нужного языка.
Шаг 16. Синхронизация обновлений
Если программа изменилась и, как следствие, изменился главный файл локализации [loc], тогда и переводы тоже
должны измениться соответственно. После выполнения шага 13 надо синхронизировать локализации:
localizer.exe -merge greenwin.loc greenwin.rus.loc
Формат:
localizer.exe -merge [loc] [loc+]
Локализатор сравнит два файла локализаций между собой.
Все идентификаторы, которые есть в главном файле, но которых нет в переведенном, будут добавлены в переведенный,
и их надо будет перевести. Для облегчения поиска таких мест, перед идентификатором будет добавлен комментарий
/!TRANSLATE!/
Все идентификаторы, которых нет в главном файле, но которые есть в переведенном, будут удалены из переведенного.
После этого надо перегенерировать bin-модуль для этого языка (шаг 10).
Если не удалось загрузить файл локализации, программа, скорее всего будет неработоспособной, поскольку вместо
многих строк будут пустые строки. В таком случае надо из программы выйти. При этом сообщение об ошибке (если
оно предусмотрено) должно быть нелокализованной строкой.
Но может возникнуть желание игнорировать отсутствие файла локализации и работать нормально. В этом случае надо
добавить опцию "-default" перед командой "-auto" или "-cpp". В результате в файл cpp будут записаны все
строки из [loc]. Всем переменным эти строки будут присвоены "по умолчанию". Тогда ошибку загрузки файла
локализации можно просто игнорировать и продолжать исполнение программы с этими строками.
Дополнительные опции можно указывать перед командой в любом порядке:
Это раздел написан "для себя", "на память", он касается того проекта, над которым я работаю. Это читать не нужно.
Локализация библиотеки GreenWin (на которой сделан локализатор) использует префикс LS. Ее бинарный
файл локализации - greenwin_loc.bin. Файл должен находиться в том же каталоге, что и exe-файл программы.
Локализация библиотеки GreenWin загружается автоматически ею самой, когда выполняется инициализация
библиотеки. Сама инициализация происходит в функции GSystem::start, либо в функции GConsoleSystem::start().
Для оконных приложений вы должны вызвать первую, а для консольных - вторую. В том же каталоге, что и
приложение, должен находиться бинарный файл локализации greenwin_loc.bin
Если это оконное приложение GreenWin, тогда есть смысл поместить вызов 'load' в функцию инициализации первого
модуля (GUnit::create). В случае ошибки лучше выбросить GInitException(), чтобы отменить запуск программы:
Если это консольное приложение GreenWin, есть смысл поместить вызов 'load' в функцию main, сразу после
вызова GConsoleSystem::start(). В случае ошибки лучше выйти из программы (exit(-1) или return -1).
Реализация GLocalizer отличается от Localizer тем, что принимает путь относительно исполняемого файла и
сама генерирует сообщение об ошибке. Для консольного приложения - в stderr, а для оконного - в message box.
Файлы
Внутри файл exe. Никакой установки не требуется. Программа распространяется свободно.
Эти два файла надо включить в свой проект. Опции компиляции большого значения не имеют, так как файлы
очень простые. Никаких дополнительных библиотек (помимо стандартных) не требуется, код распространяется
свободно.
Часть исходников проекта Localizer. Проект является частично Open Source - в той мере, в какой здесь
выложены исходники. Эти фрагменты вы можете использовать на свое усмотрение. Для компиляции необходима
моя библиотека GreenWin, исходники которой на данный момент не распространяются.
Общие сведения
Быстрый старт
#define LS
- такой или другой, лишь бы уникальный. Пометьте этим префиксом все строки, подлежащие локализации
(например LS"this string").
localizer -auto LS файл1 файл2...
localizer -auto LS interval.vcproj interval.lst interval.loc interval.rus.loc interval_loc.cpp interval_loc.bin interval_loc.rus.bin
LS"this string"
появятся указатели типа (const char *) вида
LS__this_string
и объявления вида
extern const char *LS__this_string;
#include "localizer.h"
...
static Localizer localizer;
...
extern const char **LS_all[];
if (!localizer.load("interval_loc.bin", LS_all))
(обработка ошибки)
Префикс
#define LS
Файлы
/<комментарий>
<идентификатор>
<содержимое>
/END/
/<комментарий>
<идентификатор>
<содержимое>
/END/
...
/!текст!/
/<комментарий>
<идентификатор>=<содержимое>
/<комментарий>
<идентификатор>=<содержимое>
...
DWORD N - число строк
DWORD SZ - длина блока строк
DWORD OFFSET[N] - блок смещений - смещение каждой из N строк в блоке строк относительно начала блока
BYTE DATA[SZ] - блок строк
Локализованная строка
Массив локализованных строк
Определение локализованных строк
Объявление (декларация) локализованных строк
Строки, подлежащие локализации
Загрузка локализации приложения
#include "localizer.h"
...
static Localizer localizer;
...
extern const char **LSI_all[];
if (!localizer.load("interval_loc.bin", LSI_all))
{
printf("Error of localization.\n")
return -1;
}
Автоматизация процесса локализации по шагам
#define LSI
Составление списка файлов, подлежащих локализации
Создание главного файла локализации
Локализация строк
/*--LOCALIZER DECLARATIONS-- Localizer: LS --BEGIN-- */
extern const char
*LS__..., *LS__...;
/*--LOCALIZER DECLARATIONS-- Localizer: LS --END-- */
const char *LS__OK;
- В [loc] есть фрагмент:
OK
&OK
/END/
static char mystring[]= LS"mystring";
- превращается в:
static char mystring[]= LS__mystring;
- и это compile error.
struct Pupseg
{
char *text;
int id;
};
static Pupseg pups = {LS"blin", 12 };
- превращается в:
static Pupseg pups = {LS__blin, 12 };
- и это неприятно, поскольку указатель LS__blin инициализируется слишком поздно.
struct Pupseg
{
char **text;
int id;
};
static Pupseg pups = {&LS__blin, 12 };
Генерация cpp-модуля
Генерация bin-модуля
#include "localizer.h"
...
static Localizer localizer;
...
extern const char **LSI_all[];
if (!localizer.load("interval_loc.bin", LSI_all))
throw GInitException();
Обновления
localizer.exe -list greenwin.vcproj greenwin.lst
localizer.exe -get LS greenwin.lst greenwin.loc
localizer.exe -put LS greenwin.lst greenwin.loc
localizer.exe -cpp LS greenwin.loc greenwin_loc.cpp
localizer.exe -bin LS greenwin.loc greenwin_loc.bin
Вместо greenwin используйте название своего проекта и свой префикс вместо LS.
Перевод
Синхронизация обновлений
Программа без двоичного файла
Дополнительные опции
При запуске локализатор постарается оптимизировать идентификаторы и корректно
исправить их в файлах локализации и в исходниках. Поскольку эта опция может затронуть сразу множество файлов,
рекомендуется сделать страховочную копию проекта перед ее запуском. Настоятельно рекомендуется указывать в
командной строке все файлы локализации, чтобы не произошло их рассинхронизации.
Особенности локализации библиотеки GreenWin
#include "gf_localizer.h"
...
static GLocalizer localizer;
...
extern const char **LSI_all[];
if (!localizer.load("interval_loc.bin", LSI_all))
throw GInitException();