by Jasmin Blanchette
QT’s tr() механизм для интернационализации очень легок в понимании, легок в использовании, и легок в программировании. Эта статья даст вам несколько советов, как правильно использовать tr(). Эта статья также рассказывает о QRegExp, XML, и Шведском языке.
Распространенные грубые ошибки |
[BLUNDER:] Из Старо-Английского BLUNDEREN, "слепнуть", возможно из Древнего Шведского BLUNDRA, "одноглазый", из Древнего Норвежского BLUNDA. -- Американский словарь происхождений |
Функция tr() двойная, по своей природе. Она является и lupdate маркером, и C++ функцией. Забудете о двойственности – получите ошибки. Некоторые ошибки будут результатом ошибок компилятора, остальные – lupdate, и, наконец, несколько ошибок пройдут незамеченными, и станут результатом сообщения в qt-bugs@trolltech.com. Давайте рассмотрим проблемы, которые могут возникнуть, и попробуем дать пути их решения.
QPushButton *ok = new QPushButton( tr("OK"), this );
QCString str = "OK"; QPushButton *ok = new QPushButton( tr(str), this );
tr( QString("Cannot open %1").arg(fileName) ) tr( "Cannot open " + fileName )
tr( "Cannot open %1" ).arg( fileName )
const char *strings[2] = { QT_TR_NOOP("OK"), QT_TR_NOOP("Cancel") }; for ( int i = 0; i < 2; i++ ) { QButton *but = new QPushButton( strings[i], this ); }
|
Убеждение, что tr() является макросом, или виртуальным членом функции QObject – очень распространено. Истина заключается в том, что tr() является статической фукнцией, она не виртуальна, и уж тем более не является макросом. Каждый класс с Q_OBJECT имеет свой tr() и trUtf8(), которые объявляются и реализуются автоматически.
Вы можете подумать – почему tr() не реализована единожды и для всех объектов в QObject, использующих className() для обеспечения конекста. Причина такова: пусть А будет субклассом QObject, и пусть Б будет субклассом А. Код: |
Это очень важно, что все эти ошибки решаются ДО ТОГО, как .ts файлы посланы для перевода. Следующие секции продемонстрируют вам, как обнаружить и исправить tr() ошибки.
Использование Grep |
Первый инструмент для исправления ошибок #1, #2, and #3 - grep. Программисты под виндоуз, также могут попробовать использовать findstr, или закачать один из многих grep портов.
Следующая команда найдет строки, которые не окружены tr() (ошибка номер 1).
grep -n '"' *.cpp | grep -v 'tr('
Функция –n скажет grep`у печатать строку с информацией о номере. Функция –v инвертирует совпадения, так, что вышеследующее значит «найди все строки, которые совпадают», но не содержат вызовы tr(). Однако эта функция найдет, также, множество «невинных» строк, которые не требуют перевода.
Ошибка номер 2. Использование tr() в переменной – легко для обнаружения.
grep -n 'tr(' *.cpp | grep -v '"'
Это противоположность следующему : «найди все строки, содержащие tr(, но не содержащие “.”
Если приложение использует qApp-> translate(), используйте grep`ы, применяя translate вместо tr().
Ошибка 3. Неверное использование QT_TR_NOOP(), может быть легко предотвращено просмотром кода. Запустите grep, с параметром –l, для того, чтобы найти список файлов, которые используют QT_TR_NOOP() или QT_TRANSLATE_NOOP():
grep -l 'QT_TR' *.cpp
Для маленьких проектов использование grep будет более чем достаточным.
Псевдо-Шведский |
Белбо приказал Абу изменить все слова, меняя каждую "a" на "akka" и каждую "o" на "ulla," из-за чего текст стал напоминать финский.
-- Umberto Eco
Самый применяемый метод обнаружения пропущенных вызовов tr() -- т.е. Ошибка #1 -- это запустить приложение с переводом. Но что, если его еще нет?
Одно из возможных решений данной проблемы принимает форму персонажа повара-шведа из Muppet Show. Идея в использовании программы, переводящей оригинальное англоязычное предложение в язык, на котором общался данный чувак (сие наречие мы будем называть псевдо-шведским).[1] Затем приложение включает псевдо-шведский язык, и любое английское слово, выглядящее непереведенным, вызвано неверным использованием tr().
Вот некоторые из английских фраз из приложений на Qt и их псевдо-шведские эквиваленты:
Английский | Псевдо-шведский |
&File | &Feele-a |
Big pink pig | Beeg peenk peeg |
Qt Quarterly | Qt Qooerterly |
Internationalization | Interneshuneleezeshun |
Do you want to save %1? | Du yuoo vunt tu sefe-a %1? Bork Bork Bork! |
Преимущество использования данного бреда в том, что результат выглядит очень странно, делая легким обнаружение обычных английских слов. Также приложение доступно для тестера, который разберет большинство фраз на псевдо-шведском.
Конечно, нет технических причин предпочтения шведского повара вместо немецкого хот-дожника или японского повара-сушиста. Любой язык, выглядящий по-импортному, может быть успешно использован для тестов.
Таблица показывает нужные замены символов:
an | -> | un | -f | -> | ff | th| | -> | t | ||
au | -> | oo | -ir | -> | ur | -tion | -> | shun | ||
a- | -> | e | -i | -> | ee or i | -u | -> | oo | ||
en| | -> | ee | -ow | -> | oo | v | -> | f | ||
-ew | -> | oo | |o | -> | oo | w | -> | v |
Знак "|" используется для указания границ между словами, а "-" внутри слов. Таким образом, согласно правилу"-f", "f" заменяется на "ff" везде, кроме слов типа "fool" ("дурак" - прим. редактора), где "f" находится на границе слов. Программа lex, которая выполняет псевдо-шведизирование текста, доступна на www.almac.co.uk/chef/chef/ftp_chef.html.
Некоторые слова идентичны в английском и псевдо-шведском. Алгоритм может быть улучшен для искажения каждой "непереводимой" фразы, например, добавлением в конец тупизма типа "bork" (наверно, че-то типа нашего "бля" - прим.редактора) или другой ботвы.
Английский: | File Edit Preferences Help |
Псевдо-шведский: | Feele-a Ideet Prefferences Help bork |
Перевертыш: | Elif Tide Secnereferp Pleh |
Caps Lock: | fILE eDIT pREFERENCES hELP |
Смешанный шрифт: | fIlE eDiT pReFeReNcEs hElP |
Без гласных: | Fl Dt Prfrncs Hlp |
AltaVista (немецкий): | Datei Bearbeiten Sie Prдferenzen Hilfe |
Написание конвертора .ts |
Как записать это в стандартном ASCII файле, остается загадкой.
-- Paul M. Roberts
Сейчас мы напишем программу, которая принимает непереведенный .ts файл и сгенерирует файл .ts на шведском языке. Эти файлы являются XML файлами, так что желательно привлечение Qt`s SAX, или DOM парсеров для их чтения. Вместо этого мы бы посоветовали использование QRegExp, класса, который не содержит секретов для ранних подписчиков Qt Quarterly. Задание относительно легкое – нам необходимо заменить следующие строки кода :
<source>&File</source> <translation type="unfinished"></translation>
на
<source>&File</source> <translation>&Feele-a</translation>
А сама работа – чтение .ts файла, перевода, записи результата в другой .ts файл – очень проста. Всего 12 строк. Спасибо QFile и QRegExp:
QRegExp rx( "(<source>(.*)</source>.*<translation)[^>]*>" "</translation>" ); rx.setMinimal( TRUE ); QFile in( fileName ); if ( in.open(IO_ReadOnly) ) { QFile out( fileName + ".chef" ); if ( out.open(IO_WriteOnly) ) { QString str = in.readAll(); int pos = 0; while ( (pos = str.find(rx, pos)) != -1 ) { QString} newText = rx.cap(1) + ">" + mock( rx.cap(2) ) + "</translation>"; str.replace( pos, rx.matchedLength(), newText ); pos += newText.length(); } out.writeBlock( str.utf8(), str.utf8().length() ); out.close(); } in.close(); }
Наследуем QTranslator |
Я еще должен посмотреть на интересный кусок кода, пришедший от этих чуваков.
-- Alexander Stepanov
Совершенно иной подход, который не рекомендует использование QRegExp, или XML, является подклассом QTranslator. QTranslator::findMessage() функция является виртуальной, и может быть ПЕРЕреализована, как следующая :
QTranslatorMessage MockTranslator::findMessage( const char *context, const char *sourceText, const char *comment ) const { return QTranslatorMessage( context, sourceText, comment, mock(sourceText) ); }
При запуске приложения с инсталлированным MockTranslator, ошибки 1 и 3 – являются результатом НЕПЕРЕВЕДЕННОГО английского текста, в куче шведского.
Итак, мы не вовлекли в решение проблемы lupdate. Это справедливо, поскольку мы не знаем, возможно ли обмануть lupdate использованием tr() в переменной (ошибка 2). Это может быть протестировано следующим образом. Запустим lupdate для генерации .ts файла, и затем используем функцию «найти и заменить» текстового редактора, для замены каждого случая :
<translation type="unfinished"></translation>
<translation>Mock me!</translation>
QTranslatorMessage MockTranslator::findMessage( const char *context, const char *sourceText, const char *comment ) const { QTranslatorMessage msg = QTranslator::findMessage( context, sourceText, comment ); if ( msg.translation() == "Mock me!" ) msg.setTranslation( mock(sourceText) ); else qWarning( "Blunder #2 with %s, %s, %s", context, sourceText, comment ); return msg; }
Когда tr() не достаточно |
Инструмент lupdate поддерживает исходные файлы С++ и файлы .ui для QT Designer. Некоторые приложения хранят видимые пользователю строки в других местах: базах, файлах конфигурации, скриптах, и тд. Вот несколько примеров, как эти файлы могут быть использованы для интеграции в круг lupdate, Qt Linguist, lrelease.
Наиболее простым путем является написание С++ программы, которая разжимает строки, откуда они пришли, и генерирует .ts файл. В свою очередь .ts файл может быть переведен, используя QT Lingiust, и конвертирован в .qm файл. QApplication поддерживает множество .qm файлов одновременно, так что не будет особой проблемы совмещать эту технику с другой. Недостаток этой техники заключается в том, что она проходит мимо совмещающего алгоритма lupdate.
Альтернатива – написать программу, которая будет генерировать ложный С++ код, который также может быть прочитан lupdate. Вот пример подобного кода :
#if 0 qApp->translate( "CustomerDB", "Agency" ); qApp->translate( "CustomerDB", "Company" ); qApp->translate( "CustomerDB", "Foreign" ); qApp->translate( "CustomerDB", "International" ); #endif
Copyright © 2002 Trolltech. | Trademarks |