поддержка русского языка в приложениях на qt
Qt — трудности перевода
Исходная программа
Пусть у нас есть простая программа:
Все что она делает — создает окно с надписью «Hello,World!». Сделаем для нее перевод на русский язык.
Шаг 1. Указание всех строк, для которых требуется сделать перевод
Все строки, которые увидит пользователь должны быть обработаны функциями QObject::tr() или QcoreApplication::translate().
У всех классов Qt, наследуемых от QObject, есть функция-член tr(). Так как мы работаем со строкой в глобальной функции которая не относится ни к какому классу, то используем функцию translate(), которая позволяет указать контекст перевода, то есть класс, к которому он относится (в данном случае — QLabel).
В фунцкиях tr() и translate() после переводимой строки можно указать комментарий, который будет показан переводчику во время перевода приложения на другой языка. Комментарии используются для устранения двусмысленности.
Если вам потребуется перевести текст, который находится вне функции, есть два макроса для помощи: QT_TR_NOOP() и QT_TRANSLATE_NOOP(), аналогичные tr() и translate(). Они незаметно помечают текст для извлечения утилитой lupdate, про которую мы поговорим ниже:
При отключении автоматического преобразования из const char * в QString путем компиляции программы с определенным макросом QT_NO_CAST_FROM_ASCII можно найти все строки, которые пропустили.
Когда в середину строки нам понадобится вставлять значения каких-либо переменных, лучше всего использовать функцию arg(). Например, при копировании файлов мы могли бы отображать ход процесса следующим образом:
Если потребуется изменить порядок аргументов при переводе, то при переводе надо будет поменять переменные с символом % местами, например, вот так:
«Копируем файл %3. %1 из %2 файлов скопировано»
Переписывать программу из-за этого не потребуется.
Шаг 2. Создание перевода
Обычно надо повторять эти шаги для каждого выпуска приложения. Утилита lupdate делает все возможное по повторному использованию переводов от предыдущих релизов.
Перед запуском lupdate, вам потребуется подготовить файл проектов. Вот как будет выглядеть наш файл проекта (файл helloworld.pro):
TEMPLATE = app
TARGET = release
DEPENDPATH +=.
INCLUDEPATH +=.
SOURCES += main.cpp
TRANSLATIONS += helloworld_ru.ts
Когда вы запускаете lupdate или lrelease, вы должны предоставить имя файла проекта в качестве аргумента командной строки.
Шаг 3. Загрузка файлов переводов в приложении.
В нашем приложении мы должны загрузить QTranslator::load() файлы используя QcoreApplication::installTranslator(). Окончательная версия программы примет вид:
Мы создаем объект QTranslator, загружаем в него файл перевода с помощью функции load(). В ней мы указываем начало имени файла нашего перевода. По умолчанию файлы переводов ищутся в папке с программой, но можно указать любую директорию, передав ее имя в качестве второго параметра функции. Расширение «.qm» будет добавлено автоматически. Функция Qlocale::system().name() возвращает имя текущей локали, в моем случае это было ru_RU.UTF-8. Порядок поиска файла переводов функцией load() будет следующим:
Правда когда я попробовал загрузить таким образом файл переводов в Windows XP, у меня ничего не вышло. Оказалось, что (по крайней мере у меня так) функция Qlocale::system().name() все время возвращала значение «С». Поэтому стоит предусмотреть дополнительный способ указания языка интерфейса приложения, например, через диалог настроек программы или параметры командной строки.
Вот и все, наше приложение переведено на русский язык, но есть еще парочка моментов, о которых стоит упомянуть в данной статье.
Дополнительные текстовые строки
Обратите внимание на использование QLibraryInfo::location() для обнаружения переводов Qt. Разработчик должен запросить путь к переводам обрабатывая QLibraryInfo::TranslationsPath для этой функции вместо использования переменной среды QTDIR в своих приложениях. Хотя в случаях, когда мы не уверены что у пользователя стоит полная версия Qt, имеет смысл поставлять этот файл переводов вместе с программой и загружать его из директории программы (или любой другой на выбор).
Динамический перевод
Некоторые приложения должны обеспечивать изменения настроек языка пользователя во время работы. Что бы предупредить виджеты об изменениях установленного QTranslators, можно переделать фунцкцию виджета changeEvent() для проверки не является ли событие событием LanguageChange, и обновите текст, отображаемый виджетами, используя функцию tr() обычным способом. Например:
Все остальные события изменения должны быть обработаны вызовом реализации по умолчанию данной функции.
Список установленных переводов может быть изменен в реакции на событие LocaleChange, или приложение может предоставлять интерфейс пользователю, который позволит ему изменить текущий язык приложения.
Обработчик событий по умолчанию для подклассов QWidget отвечает на событие QEvent::LanguageChange и вызовет эту функцию при необходимости; в других компонентах приложения можно так же заставить виджеты обновить себя отправив им событие LanguageChange.
UPD: Как подсказывает intellinside, классы графического интерфейса пользователя, сгенерированные Qt Designer’ом, имеют функцию retranslateUi(), которую можно вызвать для динамического изменения языка приложения.
Объект класса QTranslator используется для загрузки переводов из специального файла с расширением .qm, который является шестнадцатеричным файлом переводов. Данный файл компилируется из файла переводов в XML формате, который имеет расширение ts и прописывается в pro файле проекта. Данный файл содержит все строки приложения, которые были заключены в функцию tr(). Рекомендую задавать весь интерфейс приложения на английском языке, который будет языком по умолчанию, а переводы уже с нужным языком подгружать из файлов переводов. Если нужный файл перевода не будет найден, то автоматически будет загружен перевод на английском языке. Хотя Вы, конечно, можете применить и другой язык в качестве языка по умолчанию.
Пример загрузки перевода
Структура имени файла перевода играет важную роль в работе с QTranslator. Разберём минимальный пример.
После того, как файл перевода загружен, его необходимо установить в приложение. В данном случае использован глобальный указатель на приложение qApp->installTranslator(&qtLanguageTranslator).
Создание файла перевода
После того, как мы разобрались с минимальным способом установки перевода в приложение, давайте разберёмся с тем, а как вообще можно создать перевод. Для создания перевода необходимо пользоваться функциями tr(), trUtf8(), translate() в приложении и т.д. То есть весь текст, который будет требовать перевода необходимо обрамлять именно в эти функции, чтобы потом создать файл перевода. Выглядеть это может следующим образом:
После того, как в приложении помечены все необходимые для перевода строки, необходимо будет создать файл переводов, сделать непосредственно сам перевод и скомпилировать итоговый файл. Для этого используется следующие программы:
Первым шагом, который необходимо будет сделать, после того, как в приложении уже прописаны строки, требующие перевода, это добавить файл для перевода в pro файл проекта. Возможно, Вы также захотите указать информацию о кодировке, которая применяется для перевода.
lupdate сообщит о результате поиска строк для перевода.
Далее открываем Qt Linguist производим перевод всех строк, помечая, какие из них переведены (Это чисто служебная информация для самого переводчика, чтобы не вспоминать, что было переведено, а что нет.)
Для применения файла необходимо будет поместить его в каталог с исполняемым файлом приложения, а в коде прописать путь к этому файлу.
Динамический перевод приложения
Структура проекта
mainwindow.ui
Я предпочитаю использовать графический дизайнер для создания графического интерфейса приложения, поскольку это ускоряет разработку, если не требуется чего-то сложного во внешнем виде приложения, поэтому создадим окно со следующим внешним видом.
В данном окне имеется:
С переводом которых мы и будем работать. Удобство графического дизайнера заключается также и в том, что он автоматически формирует метод retranslateUi(), который используется для изменения перевода всех подписей, которые используются в приложении, хотя можно и самим прописать вручную подобный метод, но лично я не хочу терять время на то, что отлично может быть создано автоматически. Например в данном уроке он выглядит следующим образом:
QtLanguage.pro
Как говорилось выше, указываем имя файла перевода в профайле проекта.
main.cpp
А вот в этом файле ничего не будем менять.
mainwindow.h
mainwindow.cpp
Логика работы приложения будет следующей: при смене пункта в комбобоксе будет меняться перевод приложения.
В результате Вы получите приложение с поддержкой двух язык в своём графическом интерфейсе.
Видеоурок
Рекомендуем хостинг TIMEWEB
Рекомендуемые статьи по этой тематике
Неудобства при работе с переводами в Qt и способы борьбы с ними
В этой статье я хотел бы рассказать о некоторых неудобствах, с которыми столкнулся при работе с системой переводов в Qt, а также поделиться способами борьбы с этими неудобствами.
Для начала кратко напомню о том, как работает система переводов в Qt.
Прежде всего, разработчик при написании кода обёртывает строку, которая должна быть переведена на различные языки, в одну из специальных функций:
Далее в файле проекта указываются файлы, в которых переводчик будет осуществлять, собственно, сам перевод:
Всё, наши строки будут отображаться на нужном языке.
Неудобство 1: хранение переводов
Итак, хорошо, мы перевели строку, при запуске приложения загрузили файл перевода, в нашем текстовом поле (или где-то ещё) появился текст на нужном языке. Действительно, в таком тривиальном случае большего и не нужно. Однако, рассмотрим следующий пример. Допустим, у нас есть консольное приложение, где реализована обработка вводимых пользователем команд. Новые команды могут быть добавлены посредством установки функции-обработчика, например, вот так:
Всё отлично, но неплохо было бы при вводе, скажем «help command» выдавать справку по соответствующей команде command. Сделаем:
Чувствуете подвох? Да, сначала всё будет хорошо:
Если QTranslator был установлен заранее, то мы получим переведенную строку. Но что, если пользователь решит сменить язык (иными словами, будет загружен другой файл переводов)? Строка останется прежней.
У этой проблемы есть несколько решений. Я приведу несколько, в том числе то, которое видится мне наиболее естественным и удобным.
Решение 1: фабрика
Можно заменить строку на функцию-фабрику, которая будет возвращать строку:
Фабричная функция и её применение могут выглядеть следующим образом:
Решает ли это проблему? Да, перевод будет осуществляться каждый раз при вызове справки, таким образом, при смене языка справка будет показана переведенной на этот новый язык. Красивое ли это решение? Каждый считает по-своему, я же считаю, что нет.
Решение 2: QT_TRANSLATE_NOOP3
В заголовочном файле есть такой макрос — QT_TRANSLATE_NOOP3. Он помечает обёрнутую в него строку к переводу и возвращает анонимную структуру (struct), содержащую эту строку (в непереведённом виде), а также комментарий. В дальнейшем созданную структуру можно использовать в функциях tr и translate.
Надо ли говорить, что код получается громоздким и уродливым? Думаю, не надо. Кроме того, возникают сложности с передачей такой структуры в качестве параметра функции. Код:
О том, что для перевода без комментария используется другой макрос (и другая структура) — QT_TRANSLATE_NOOP — я уж и вовсе молчу. А ведь пришлось бы городить перегрузку installHelp и превращать одну структуру в другую. Отвратительно. Оставим это на совести разработчиков Qt.
Решение 3: самописный класс-обертка
Дальше дело остается за малым: реализуем свой статический метод translate так, чтобы он создавал экземпляр класса Translation, который по сути является более удобным аналогом анонимной структуры, которую возвращает QT_TRANSLATE_NOOP3. Добавляем также еще один метод translate, но уже не статический. Он просто вызывает внутри QCoreApplication::translate, передавая в качестве параметров контекст, исходную строку и комментарий, которые были указаны при вызове статического метода Translation::translate. Добавляем методы для копирования и (де)сериализации, и получаем удобный контейнер для хранения переводов. Не буду описывать остальные методы класса, так как они не имеют непосредственного отношения к решаемой задаче и тривиальны для разработчиков, знакомых с C++ и Qt, для которых и предназначена данная статья.
Вот как выглядел бы пример со справкой с использованием Translation:
Выглядит естественней, чем фабрика, и красивее, чем QT_TRANSLATE_NOOP3, не правда ли?
Неудобство 2: перевод без наследования
Второе неудобство, с которым я столкнулся в Qt — невозможность динамического перевода интерфейса без наследования хотя бы одного класса. Рассмотрим сразу пример:
Как видно из примера, мы загружаем файл перевода, создаём QWidget и устанавливаем его название. Но вдруг пользователь решил воспользоваться LanguageSettingsWidget и выбрал другой язык. Название QWidget должно поменяться, но для этого нам нужно предпринять какие-то дополнительные действия. Опять же, есть несколько вариантов.
Решение 1: наследование
Можно отнаследоваться от QWidget и переопределить один из виртуальных методов:
В таком случае при установке нового QTranslator будет вызван метод changeEvent, и, в нашем случае, setWindowTitle. Просто? Достаточно. Удобно? Я считаю, что не всегда (в частности, когда исключительно ради переводов приходится городить такой огород).
Решение 2: перевод извне
Также можно передать указатель на данный класс в другой класс, который уже унаследован от QWidget, и там вызывать соответствующий метод. Код приводить не буду — он очевиден и мало отличается от предыдущего примера. Скажу лишь, что это однозначно плохой способ — чем меньше классы знают друг о друге, тем лучше.
Решение 3: ещё один велосипед ещё одна обертка
Что же тут происходит? При создании экземпляра класса DynamicTranslator мы указываем целевой объект, перевод, а также имя слота (например, setWindowTitle) или имя свойства (windowTitle). Наш DynamicTranslator при каждой смене языка либо вызывает соответствующий слот при помощи tp://qt-project.org/doc/qt-4.8/qmetaobject.html»>QMetaObject, либо устанавливает нужное свойство при помощи setProperty. Вот как это выглядит на практике:
Благодаря тому, что виджет w является родителем нашего DynamicTranslator, нет необходимости беспокоиться о его удалении — DynamicTranslator будет удалён вместе с QWidget.
Вместо заключения
Безусловно, рассмотренные способы борьбы с неудобствами переводов не являются единственными, и уж тем более — единственно верными. Например, в достаточно больших приложениях вообще могут быть использованы сторонние средства перевода вместо тех, что предоставляет Qt (например, можно хранить все тексты в файлах, а в коде указывать только идентификаторы). Опять же, в больших приложениях пара десятков лишних строк (в случае с наследованием или написанием фабричной функции) не сделает погоды. Тем не менее, приведенные здесь решения могут сэкономить немного времени и строк кода, а также сделать этот код более естественным.
Критика и альтернативные решения всегда приветствуются — давайте писать более правильный, красивый и понятный код вместе. Благодарю за внимание.
unixforum.org
Форум для пользователей UNIX-подобных систем
Русский в QT-приложениях (Нужен простой способ русификации)
Русский в QT-приложениях
Сообщение MedVed » 14.04.2005 20:34
Вопрос: Как проще всего добавить поддержку русского в QT-приложения.
Сейчас использую такой код:
Re: Русский в QT-приложениях
Сообщение Valerius » 14.04.2005 21:37
Свобода есть тонкая полоска между диктатурой и анархией.
———————
Mandriva Linux PowerPack 2006 (Cooker-нутая) / KDE-3.5.1
Re: Русский в QT-приложениях
Сообщение sdk » 14.04.2005 22:19
Re: Русский в QT-приложениях
Вообщем совет использовать gettext это наиболее верный подход.
Re: Русский в QT-приложениях
Сообщение TIM » 15.04.2005 00:39
Re: Русский в QT-приложениях
Re: Русский в QT-приложениях
Сообщение MedVed » 15.04.2005 21:19
Кажется пробовал что-то подобное. Не работало. Попробую еще раз.
Re: Русский в QT-приложениях
Сообщение sdk » 16.04.2005 02:49
2MedVed:
Смотреть как обычно:
man gettext
info gettext
Re: Русский в QT-приложениях
Сообщение shark3D » 24.09.2005 13:00
Для ипользования русского в Qt-программах неободимо использовать кодеки следующим образом:
QTextCodec *russianCodec = QTextCodec::codecForName( «KOI8-R» );
QTextCodec::setCodecForTr( russianCodec );
и далее использовать конструкции
setCaption( tr(«Тест на русском!») );
QTextCodec *russianCodec = QTextCodec::codecForName( «KOI8-R» );
QTextCodec::setCodecForCStrings( russianCodec );
При разработке на QML есть пара моментов, которые нужно учесть, в отличие от стандартного приложения на QWidgets, а именно:
Структура проекта
Создаём проект, в который будут входит следующие файлы:
QmlLanguage.pro
Создание файла перевода будет аналогичным тому, как это делается при создании переводов для Qt/C++ приложения. То есть нужно подключить файл перевода, с которым будет работать Qt Linguist :
Самый важный момент для работы с переводами в QML то, что нужно подключить все файлы QML в качестве исходников, как обычные C++ файлы. Но с отметкой, что только для использования lupdate_only.
main.cpp
Поскольку подключение файлов переводов осуществляется в C++ слое, то потребуется зарегистрировать объект класса переводов в контексте QML. В данном случае мы напишем класс QmlTranslator, который будет обёрткой над QTranslator, поскольку тот класс не имеет методов, с которыми мы могли бы работать через QML.
qmltranslator.h
В классе присутствует метод установки перевода, в который передаётся префикс языка перевода. А из данного метода будет испускаться сигнал об изменении перевода, чтобы была возможность заново перевести весь интерфейс с новым переводом.
Метод установки перевода необходимо пометить макросом Q_INVOKABLE, чтобы была возможность использовать его в QML слое.
qmltranslator.cpp
main.qml
Интерфейс приложения будет выглядеть следующим образом:
Логика приложения следующая: при смене языка в комбобоксе будет изменяться язык приложения.
Поскольку мы зарегистрировали qmlTranslator в контексте QML слоя, то в обработчике изменения текста в комбобоксе будем вызывать метод изменения языка в объекте qmlTranslator. А чтобы отслеживать сигнал изменения языка, необходимо с помощью типа Connections подключить к этому сигналу и написать обработчик, в котором будет вызываться функция retranslateUi(). В функции retranslateUi() вынесены все текстовые свойства всех объектов, которые необходимо подвергнуть переводу. Сделано это для упрощения логики и уменьшения избыточного кода.
В целом динамический перевод приложения на Qt/QML мало чем отличается от перевода приложения с использованием только Qt/C++. Ключевым моментом является то, что нужно правильно подключить сигналы и слоты и правильно вызывать методы для инициализации перевода.