• 11729阅读
  • 10回复

【提问】如何实现动态语言切换? [复制链接]

上一主题 下一主题
离线abdurahim
 
只看楼主 正序阅读 楼主  发表于: 2006-01-18
QT程序里如何实现动态语言切换?
[ 此贴被XChinux在2006-01-18 12:47重新编辑 ]
离线sinl

只看该作者 10楼 发表于: 2006-03-21
引用第9楼xuxinshao2006-03-03 21:30发表的“”:
to:sinl
  这篇文章是从那里找来的,能把地址告诉我吗?



没记错的话是《C++ GUI Programming with Qt 3》这里
Chapter 15. Internationalization
离线xuxinshao

只看该作者 9楼 发表于: 2006-03-03
to:sinl
  这篇文章是从那里找来的,能把地址告诉我吗?
离线sinl

只看该作者 8楼 发表于: 2006-02-16
我想这个是楼主想要的,不过是英文,哪位大虾做件功德无量的事情,把它翻译一下?

Dynamic Language Switching
For most applications, detecting the user's preferred language in main() and loading the
appropriate .qm files there is perfectly satisfactory. But there are some situations where users might need the ability to switch language dynamically. An application that is used continuously by different people in shifts may need to change language without having to be restarted. For example, applications used by call center operators, by simultaneous translators, and by computerized cash register operators often require this capability. Making an application able to switch language dynamically requires a little more work than loading a single translation at startup, but it is not difficult. Here's what must be done: .. Provide a means by which the user can switch language. .. For every widget or dialog, set all of its translatable strings in a separate function (often called retranslateStrings()) and call this function when the language changes. Let's review the relevant parts of a Call Center application's source code. The application provides a Language menu to allow the user to set the language at run-time. The default language is English.
Figure 15.1. The Call Center application's Language menu Since we don't know which language the user will want to use when the application is started, we no longer load translations in the main() function. Instead we will load them dynamically when they are needed, so all the code that we need to handle translations must go in the main window and dialog classes.
Let's have a look at the Call Center application's QMainWindow subclass:
MainWindow::MainWindow(QWidget *parent, const char *name)
: QMainWindow(parent, name)
{
journalView = new JournalView(this);
setCentralWidget(journalView);
qmPath = qApp->applicationDirPath() + "/translations";
appTranslator = new QTranslator(this);
qtTranslator = new QTranslator(this);
qApp->installTranslator(appTranslator);
qApp->installTranslator(qtTranslator);
createActions();
createMenus();
retranslateStrings();
}
In the constructor, we set the central widget to be a JournalView, a QListView subclass. Then we set up a few private member variables related to translation:
.. The qmPath variable is a QString that specifies the path of the directory that contains the application's translation files.
.. The appTranslator variable is a pointer to the QTranslator object used for storing the current application translation.
.. The qtTranslator variable is a pointer to the QTranslator object used for storing Qt's translation. At the end, we call the createActions() and createMenus() private functions to create the
menu system, and we call retranslateStrings(), also a private function, to set the uservisible strings for the first time.
void MainWindow::createActions()
{
newAct = new QAction(this);
connect(newAct, SIGNAL(activated()), this, SLOT(newFile()));
...
aboutQtAct = new QAction(this);
connect(aboutQtAct, SIGNAL(activated()), qApp, SLOT(aboutQt()));
}
The createActions() function creates the QAction objects as usual, but without setting any of the texts or accelerator keys. These will be done in retranslateStrings().
void MainWindow::createMenus()
{
fileMenu = new QPopupMenu(this);
newAct->addTo(fileMenu);
openAct->addTo(fileMenu);
saveAct->addTo(fileMenu);
exitAct->addTo(fileMenu);
...
createLanguageMenu();
}
The createMenus() function creates menus, but does not insert these menus into the menu bar. Again, this will be done in retranslateStrings(). At the end of the function, we call createLanguageMenu() to fill the Language menu with the list of supported languages. We will review its source code in a moment. First, let's look at
retranslateStrings():
void MainWindow::retranslateStrings()
{
setCaption(tr("Call Center"));
newAct->setMenuText(tr("&New"));
newAct->setAccel(tr("Ctrl+N"));
newAct->setStatusTip(tr("Create a new journal"));
...
aboutQtAct->setMenuText(tr("About &Qt"));
aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
menuBar()->clear();
menuBar()->insertItem(tr("&File"), fileMenu);
menuBar()->insertItem(tr("&Edit"), editMenu);
menuBar()->insertItem(tr("&Reports"), reportsMenu);
menuBar()->insertItem(tr("&Language"), languageMenu);
menuBar()->insertItem(tr("&Help"), helpMenu);
}
The retranslateStrings() function is where all the tr() calls for the MainWindow class occur. It is called at the end of the MainWindow constructor and also every time a user changes the application's language using the Language menu. We set each QAction's menu text, accelerator, and status tip. We also insert the menus into the menu bar, with their translated names. (The call to clear() is necessary when retranslateStrings() is called more than once.)
The createMenus() function referred to earlier called createLanguageMenu() to populate the
Language menu with a list of languages:
void MainWindow::createLanguageMenu()
{
QDir dir(qmPath);
QStringList fileNames = dir.entryList("callcenter_*.qm");
for (int i = 0; i < (int)fileNames.size(); ++i)
{
QTranslator translator;
translator.load(fileNames, qmPath);
QTranslatorMessage message =
translator.findMessage("MainWindow", "English");
QString language = message.translation();
int id = languageMenu->insertItem(tr("&%1 %2").arg(i + 1).arg(language), this, SLOT(switchToLanguage(int)));
languageMenu->setItemParameter(id, i);
if (language == "English")
languageMenu->setItemChecked(id, true);
QString locale = fileNames;
locale = locale.mid(locale.find('_') + 1);
locale.truncate(locale.find('.'));
locales.push_back(locale);
}
}
Instead of hard-coding the languages supported by the application, we create one menu entry for each .qm file located in the application's translations directory. For simplicity, we assume that English also has a .qm file. An alternative would have been to call clear() on the
QTranslator objects when the user chooses English. One particular difficulty is to present a nice name for the language provided by each .qm file. Just showing "en" for "English" or "de" for "Deutsch", based on the name of the .qm file, looks crude and will confuse some users. The solution used in createLanguageMenu() is to check the
translation of the string "English" in the "MainWindow" context. That string should be translated to "Deutsch" in a German translation, to "Fran.ais" in a French translation, and to " " in a Japanese translation. We create menu items using QPopupMenu::insertItem(). They are all connected to the main window's switchToLanguage(int) slot, which we will review next. The parameter to the switchToLanguage(int) slot is the value set using setItemParameter(). This is very similar to
what we did in Chapter 3 when we implemented the Spreadsheet application's recently opened files list (p. 54). At the end, we append the locale in a QStringList called locales, which we will use for
implementing switchToLanguage().
void MainWindow::switchToLanguage(int param)
{
appTranslator->load("callcenter_" + locales[param], qmPath);
qtTranslator->load("qt_" + locales[param], qmPath);
for (int i = 0; i < (int)languageMenu->count(); ++i)
languageMenu->setItemChecked(languageMenu->idAt(i),
i == param);
retranslateStrings();
}
The switchToLanguage() slot is called when the user chooses a language from the Language menu. We start by loading the translation files for the application and for Qt. Then we update the check marks next to the Language menu entries so that the language in use is ticked, and we call retranslateStrings() to retranslate all the strings for the main window. On Microsoft Windows, an alternative to providing a Language menu is to respond to LocaleChange events, a type of event emitted by Qt when it detects a change in the environment's locale. The event type exists on all platforms supported by Qt, but is only
actually generated on Windows, when the user changes the system's locale settings (in the Regional and Language Options from the Control Panel). To handle LocaleChange events, we can reimplement QObject::event() as follows:
bool MainWindow::event(QEvent *event)
{
if (event->type() == QEvent::LocaleChange) {
appTranslator->load(QString("callcenter_")     + QTextCodec::locale(), qmPath);
qtTranslator->load(QString("qt_") + QTextCodec::locale(),
qmPath);
retranslateStrings();
}
return QMainWindow::event(event);
}
If the user switches locale while the application is being run, we attempt to load the correct translation files for the new locale and call retranslateStrings() to update the user interface. In all cases, we pass the event on to the base class's event() function, since one of our base classes may also be interested in LocaleChange events.
We have now finished our review of the MainWindow code. We will now review the code for one of the application's widget classes, the JournalView class, to see what changes are needed to make it support dynamic translation.
JournalView::JournalView(QWidget *parent, const char *name)
: QListView(parent, name)
{
...
retranslateStrings();
}
The JournalView class is a QListView subclass. At the end of the constructor, we call the private function retranslateStrings() to set the widget's strings. This is similar to what we did for MainWindow.
bool JournalView::event(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
retranslateStrings();
return QListView::event(event);
}
We reimplement the event() function to call retranslateStrings() on LanguageChange events. Qt generates a LanguageChange event when the contents of a QTranslator currently installed on QApplication changes. In the Call Center application, this occurs when we call load() on     appTranslator     or     qtTranslator,     either     from    
MainWindow::switchToLanguage() or from MainWindow::event(). LanguageChange events are not the same as LocaleChange events. A LocaleChange event tells the application, "Maybe you should load a new translation." In contrast, a LanguageChange event tells the application's widgets, "Maybe you should retranslate all your strings." When we implemented MainWindow, we didn't need to respond to Language-Change. Instead, we simply called retranslateStrings() whenever we called load() on a QTranslator.
void JournalView::retranslateStrings()
{
for (int i = columns() - 1; i >= 0; --i)
removeColumn(i);
addColumn(tr("Time"));
addColumn(tr("Priority"));
addColumn(tr("Phone Number"));
addColumn(tr("Subject"));
}
The retranslateStrings() function recreates the QListView column headers with newly translated texts. We do this by removing all column headings and then adding new column headings. This operation only affects the QListView header, not the data stored in the QListView.
This completes the translation-related code of a hand-written widget. For widgets and dialogs developed with Qt Designer, the uic tool automatically generates a function similar to our retranslateStrings() function that is automatically called in response to LanguageChange
events. All we need to do is to load a translation file when the user switches language.


Translating Applications
Translating a Qt application that contains tr() calls is a three-step process:
1. Run lupdate to extract all the user-visible strings from the application's source code.
2. Translate the application using Qt Linguist.
3. Run lrelease to generate binary .qm files that the application can load using
QTranslator.
Steps 1 and 3 are performed by application developers. Step 2 is handled by translators. This cycle can be repeated as often as necessary during the application's development and lifetime. As an example, we will show how to translate the Spreadsheet application of Chapter 3. The application already contains tr() calls around every user-visible string. First, we must modify the application's .pro file slightly to specify which languages we want to support. For example, if we want to support German and French in addition to English, we would add the following TRANSLATIONS entry to spreadsheet.pro:
TRANSLATIONS = spreadsheet_de.ts \
spreadsheet_fr.ts
Here, we specify two translation files: one for German and one for French. These files will be created the first time we run lupdate, and are updated every time we subsequently run
lupdate.
These files normally have a .ts extension. They are in a straightforwardXML format and are not as compact as the binary .qm files understood by QTranslator. It is lrelease's job to convert
human-readable .ts files into machine-efficient .qm files. For the curious, .ts stands for "translation source" and .qm for "Qt message" file. Assuming that we are located in the directory that contains the Spreadsheet application's source code, we can run lupdate on spreadsheet.pro from the command line as follows:
lupdate -verbose spreadsheet.pro
The -verbose argument is optional. It tells lupdate to provide more feedback than usual.
Here's the expected output:
Updating 'spreadsheet_de.ts'...
0 known, 101 new and 0 obsoleted messages
Updating 'spreadsheet_fr.ts'...
0 known, 101 new and 0 obsoleted messages
Every string that appears within a tr() call in the application's source code is stored in the .ts files, along with an empty translation. Strings that appear in the application's .ui files are also included.
The lupdate tool assumes by default that the arguments to tr() are Latin-1 strings. If this isn't the case, we must add a CODEC entry to the .pro file. For example:
CODEC = EUC-JP
This must be done in addition to calling QTextCodec::setCodecForTr() from the application's main() function. Translations then need to be added to the spreadsheet_de.ts and spreadsheet_fr.ts files using Qt Linguist, a GUI tool for translating Qt applications. To launch Qt Linguist, click Qt 3.2.x|Qt Linguist in the Start menu on Windows, type linguist on the command line on Unix, or double-click linguist in the Mac OS X Finder. To start adding translations to a .ts file, click File|Open and choose the file. The left-hand side of Qt Linguist's main window shows the list of contexts for the application being translated. For the Spreadsheet application, the contexts are "FindDialog", "GoToCellDialog", "MainWindow", "SortDialog", and "Spreadsheet". The top-right area is the list of source texts for the current context. Each source text is shown along with with a translation
and a Done flag. The middle-right area is where we can enter a translation for the current source item. The bottom-right area is a list of suggestions automatically provided by Qt Linguist. Once we have a translated .ts file, we need to convert it to a binary .qm file for it to be understandable by QTranslator. To do this from within Qt Linguist, click File|Release. Typically, we would start by translating only a few strings and run the application with the .qm file to make sure that everything works.
Figure 15.2. Qt Linguist in action
If we want to regenerate the .qm files for all .ts files, we can use the lrelease command-line
tool as follows:
lrelease -verbose spreadsheet.pro
Assuming that we translated 19 strings to French and clicked the Done flag for 17 of them,
lrelease produces the following output:
Updating 'spreadsheet_de.qm'...
0 finished, 0 unfinished and 101 untranslated messages
Updating 'spreadsheet_fr.qm'...
17 finished, 2 unfinished and 82 untranslated messages
Untranslated strings are shown in the original languages when running the application. The
Done flag isn't used by lrelease; it can be used by translators to identify which translations
are finished and which ones must be revisited.
When we modify the source code of the application, the translation files may become out of
date. The solution is to run lupdate again, provide translations for the new strings, and
regenerate the .qm files. Some development teams find it useful to run lupdate frequently,
while others prefer to wait until just before a final product release.
The lupdate and Qt Linguist tools are quite smart. Translations that are no longer used are
kept in the .ts files in case they are needed in later releases. When updating .ts files, lupdate
uses an intelligent merging algorithm that can save translators considerable time with text that
is the same or similar in different contexts.
For more information about Qt Linguist, lupdate, and lrelease, refer to the Qt Linguist manual
at http://doc.trolltech.com/3.2/linguist-manual.html. The manual contains a full explanation of
Qt Linguist's user interface and a step-by-step tutorial for programmers.
离线abdurahim
只看该作者 7楼 发表于: 2006-01-18
thank you very much!!!
离线XChinux

只看该作者 6楼 发表于: 2006-01-18
Qt的帮助里有例子的。实际上就是根据选择加载不同的翻译.qm文件进行翻译。
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
离线abdurahim
只看该作者 5楼 发表于: 2006-01-18
对,就是这种意思.
离线XChinux

只看该作者 4楼 发表于: 2006-01-18
还是不明白,什么叫想要的“语言环境”?
难道你是指不同国家的语言??
就是说像skype一样,选择中文,然后界面语言就变成中文的,选择日文,界面就变 成了日文的??
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
离线abdurahim
只看该作者 3楼 发表于: 2006-01-18
就是说,通过选择菜单项,把程序界面切换成想要的语言环境.
Dynamic language switching.
离线fanyu
只看该作者 2楼 发表于: 2006-01-18
没试过,觉得应该可以。
离线XChinux

只看该作者 1楼 发表于: 2006-01-18
不明白你指的是什么意思
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
快速回复
限100 字节
 
上一个 下一个