02 January, 2011

Интернационализируем приложение на Qt

Введение
Данная статья не претендует на уникальность, но простым языком объясняет как начать использовать возможности встроенной интернационализации в Qt приложениях.


Как сделать текстовую константу переводимой?
Текстовые константы у нас могут быть использованы в C++ коде, в QtScript - скриптах (и их расширениях), в QML  и в UI файлах. Для UI делать ничего не нужно, только не забываем, что поумолчанию мы все тексты пишем на английском.
Для C++ кода, чтобы сделать текстовую константу переводимой достаточно обернуть ее в вызов статического метода QObject::tr(const *char text). Если вы и так имеете текстовую константу внутри класса-наследника QObject, то, естественно указывать им класса перед методом нет необходиомости.
Для скриптов и QML существуют две функции qsTr(string) и qsTranslate(string, string)
Первая может применятся в файлах со скриптами, для экстеншенов надо применять вторую функцию, это связано с тем, что все файлы экстеншенов называются одинаково (__init__.js). Первый параметр функции qsTranslate принимает текстовый идентификатор уникальный для каждого файла (за этим надо следить самому, иначе некорректно создасться перевод), я использую и предлагаю всем последовать моему примеруу следующий способ формирования идентификатора, из названия модуля, т.е. например если мы имеем модуль views.somemodel.modelclass, то идетификатор будет views_somemodel_modelclass.
Важно отметить, что оборачивать в данные функции можно только константный текст, т. е. если используется конкатенация, то оборачивать необходимо каждый кусок отдельно, пример:
error = qsTr("File ") + filename + qsTr (" not found!") // Правильно
error = qsTr("File " + filename + " not found!") // Неправильно!
Пример для QML:
Text { text: qsTr("Pictures") }


Как обновить файл перевода
Если вы хотите создать новый перевод или обновить старый вам необходимо в консоли перейти в конфигурации в директорию в которой вы хотите сохранять переводы и там запустить следующую команду для каждого языка:
lupdate -recursive -extensions qs,js,ui,cpp,h,qml <path to app> -ts translation.ru_RU.ts
вместо ru_RU подставляя необходимый код языка.


Как перевести
После того как ts файл создан, можно занятся переводом, для этого нужно открыть приложение QtLinguist, оно есть в стандартной поставке Qt SDK, если вы затрудняетесь его найти проверьте путь /bin, если вы собирали qt из исходников и в этой папке нет файла linguist(.exe), то это значит что при сборке вы выключили сборку утилит, ключ “-nomake tools” и вам придется пересобрать Qt без этого ключа, либо найти это приложение где-нибудь еще.
Само приложение очень просто, достаточно открыть в нем файл и сразу становиться понятно, где и что переводить.
Не забудьте сохранить файл после того как вы его перевели.


Как добавить новый перевод в приложение
Для добавления перевода его сначала необходимо скомпилировать, это делается с помощью выполнения команды:
lrelease -compress translation.ru_RU.ts -qm translation.ru_RU.qm
для каждого языка, заменяя ru_RU на необходимую локаль.
В дальнейшем я планирую автоматизировать этот процесс, с помощью .pro-файла, но пока в этом нет необходимость, а процесс этот мне кажется достатчно трудоемким.
После как .qm созданы/обновлены, я добавляю их в специально для этого заведенный файл ресурсов translations.qrc, который потом подгружаю в приложение.
Это завершающий этап, теперь новая локаль будет доступна для выбора


P.S. Как использовать переводы  в приложении.
Для того чтобы использовать файлы интернационализации в вашем приложении, вам необходимо добавить следующую конструкцию (например в main.cpp):
QApplication app(argc, argv);

QTranslator translator;
translator.load("translation.ru_RU.qm");
app.installTranslator(&translator);
Естественно, если вы озаботитесь сохранением доступа к вашему экземпляру транслятора, с помощью метода load() вы сможете менять локаль на лету.

9 comments:

  1. Мне бы эту статью года 3 назад : )

    От меня несколько минорных замечаний/предложений:

    > error = qsTr("File ") + filename + qsTr (" not found!") // Правильно

    Думаю, правильнее было бы

    > error = sprintf(qsTr("File %s found!"), filename);

    иначе переводчику будет сложнее уловить контекст

    Как альтернативу QtLinguist могу предложить неплохой ресурс http://crowdin.net

    ReplyDelete
  2. Согласен, правда Qt Linguist неплохо подсвечивает в исходниках строчку перевода, так что контекст улавливается вполне.

    ReplyDelete
  3. теоретически, у переводчиков нет исходников (разве что при опенсорс).

    Кстати, в Qt есть возможность задавать контекст перевода в исходниках ? (такая возможность есть в GetText, коментариями)

    ReplyDelete
  4. Да есть, qsTranslate например, то что я для простоты назвал идентификатором, первым параметром как раз принимает контекст, нсколько помню, он также может принимать третьим параметром комментарий.

    ReplyDelete
  5. Qt Linguist очень вредная разработка - создает иллюзию дружелюбности для переводчика, но препядствует формированию удобной централизованной базы переводов, вводя еще в добавок понятия 'контекст', не имеющее ничего общего со смыслом различия тех или иных фраз. Лучше видеть два разных ключа для перевода, чем Form1::tag и Form2::tag.

    ReplyDelete
  6. Файл с двумя колонками 'ключ' и 'перевод' + online ресурс для его просмотра + сценарий сборки, генерирующий .ts как промежуточный артефакт и вставляющий туда переводы.

    Схема работы: перевода нет -> разработчик создает тег и забывает -> увидев отсутствие перевода на прототипе, ответственные выполняют добавление перевода + find/replace тега по базе кода, если его необходимо заменить.

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

    ReplyDelete
  7. Назвали это в шутку Agile Translation:

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

    ReplyDelete
  8. А как в самой программе Qt Linguist включить русский язык? ведь, в SDK Qt есть папка с пакетами переводов к Linguist... - вот как их связать?

    ReplyDelete
  9. @Alexey Khudyakov AFAIK Qt Linguist будет запускаться с переводом под дефолтную системную локаль.

    ReplyDelete