關於qt中的tr()函數
在論壇中漂,經常遇到有人遇到tr相關的問題。用tr的有兩類人:
- (1)因為發現中文老出問題,然后搜索,發現很多人用tr,於是他也開始用tr
- (2)另一類人,確實是出於國際化的需要,將需要在界面上顯示的文件都用tr包起來,這有分兩種:
- (2a) 用tr包住英文(最最推薦的用法,源碼英文,然后提供英文到其他語言的翻譯包)
- (2b) 用tr包住中文(源碼用中文,然后提供中文到其他語言的翻譯包)
注意哦,如果你正在用tr包裹中文字符,卻不屬於(2b),那么,這是個信號:
- 你在誤用tr
- 你需要的是QString,而不是tr
如果你確實屬於(2b),請做好心理准備,你可能還會遇到很多困難,請考慮Qt國際化(源碼含中文時)的點滴分析
tr 是做什么的?下面二者的區別是什么?
QString text1 = QObject::tr("hello"); QString text2 = QString("hello");
tr是用來實現國際化,如果你為這個程序提供了中文翻譯包(其中hello被翻譯成中文"你好"),那么text1的內容將是中文"你好";如果你為程序提供且使用日文翻譯包,那么text1的內容將是日文。
tr是經過多級函數調用才實現了翻譯操作,是有代價的,所以不該用的時候最好不要用。
關注的對象
本文關注的是tr或translate中包含中文字符串的情況:
-
QObject::tr()
-
QCoreApplication::translate()
-
QTextCodec::setCodecForTr
這個問題本多少可說的。因為涉及到的編碼問題和QString 與中文問題中是完全一樣的,只不過一個是用的setCodecForCStrings一個用的是setCodecForTr。
簡單回顧QString的中文問題
- QString 采用的unicode,在中文支持上不存在任何問題
-
"我是中文" 這是傳統的 const char * 的窄字符串
- 當將窄字符串賦值到QString時,我們需要告訴它我們的窄串采用的何種編碼(gbk?、utf-8?)
- 究竟何種編碼主要取決於我們的源代碼文件的編碼(windows上一般是gbk,其他平台一般utf-8)
例子:
QString s1 = "我是中文"; QString s2("我是中文"); QString s3; s3 = "我是中文"
-
s1、s2 用的是QString的構造函數QString ( const char * str )
-
s3 用的是QString的賦值操作符 QString & operator= ( const char * str)
如果不指定編碼,s1,s2,s3將全部都是(國內大多數人所稱的)亂碼。因為QString將這些const char *按照latin1來解釋的,而不是用戶期待的gbk或utf8。
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"))
這兩條語句中的一條可以解決問題,至於如何選擇,取決於源代碼文件的編碼方式(記事本打開另存為可查看),如果是UTF-8編碼,則使用后者;如果是ANSI編碼,則使用前者。
QObject::tr
說實話,在tr中使用中文不是個好主意。不過既然總有人用(無論是(1)還是(2b)),而且總有人遇到問題,所以還是簡單整理一下吧。
相比QCoreApplication::translate,大家用tr應該用的很多了,盡管不少人不清楚tr究竟是做什么的^_^
tr("我是中文");
這調用的是下面這個函數(至少我們可這么認為是)。
QString QObject::tr ( const char * sourceText, const char * disambiguation = 0, int n = -1 )
與QString("我是中文")完全一樣,你必須告訴tr這個窄字符串是何種編碼?你不告訴它,它就用latin1。於是所謂的亂碼問題就出來了。
如何告訴tr你寫的這幾個漢字在磁盤中保存的是何種編碼呢?這正是
QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312")); QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
所做的。這兩個選擇的原則,由於和前文完全一樣,此處也不再重復。
如果你的編碼采用的utf8,可以直接使用trUtf8而不必設置setCodecForTr。
如果你只關心亂碼問題,到此為止就可以了(下面不再關注編碼)。如果想對tr進一步了解,不妨。。繼續。。
QCoreApplication::translate
我們知道tr是用於實現程序的國際化(或者說多語言翻譯),看Qt相關資料的話,我們知道實現該功能的還有下面這個函數:
QString QCoreApplication::translate ( const char * context, const char * sourceText, const char * disambiguation, Encoding encoding, int n )
其實,這個才是真正進行翻譯操作的函數,前面我們提到的tr最終是通過調用該函數來實現翻譯功能的(稍后我們會看tr是如何調用translate的)。
對tr和這個函數,manual中都有比較詳盡的解釋。我們這兒簡單看一下它的這幾個參數:
- context 上下文,一般就是需要翻譯的字符串所在的類的名字
- sourceText 需要翻譯的字符串。(我們關注的編碼其實就是它的編碼)
- disambiguation 消除歧義用的。(比如我們的類內出現兩處"close",一處含義是關閉,另一處含義是親密的。顯然需要讓翻譯人員知道這點區別)
- encoding 指定編碼。它有兩個值
-
CodecForTr 使用setCodecForTr()設置的編碼來解釋 sourceText
- UnicodeUTF8 使用utf8編碼來解釋 sourceText
- 其實這兩個分別對應tr和trUtf8
-
- n 處理單復數(對中文來說,不存在這個問題)
tr與translate
這兩個函數的說明,一個在QObject的manual,另一個在QCoreApplication的manual中。
介紹一下tr與translate的關系。前面提到了,tr調用的是translate。如果僅僅這樣一說,沒有證據,還真難以讓大家相信。好吧,繼續
tr 在何處定義
你可能說:這不廢話嗎,manual中寫得明白的,它是QObject的靜態成員函數。而且還有源碼為證:
//來自 src/corelib/kernel/qobject.h
#ifdef qdoc static QString tr(const char *sourceText, const char *comment = 0, int n = -1); static QString trUtf8(const char *sourceText, const char *comment = 0, int n = -1); #endif
嘿嘿,差點就被騙了,發現沒:它們被預處理語句包住了。
這說明了什么呢?說明了這段代碼僅僅是用來生成Qt那漂亮的文檔的(qdoc3從代碼中抽取信息,生成一系列的html格式的manual)。
啊,也就是說,這是假的。那么真正的定義呢??在一個大家都很熟悉的地方,猜猜看?
這就是
Q_OBJECT
該宏的定義在src/corelib/kernel/qobjectdefs.h中
#define Q_OBJECT \ public: \ Q_OBJECT_CHECK \ static const QMetaObject staticMetaObject; \ Q_OBJECT_GETSTATICMETAOBJECT \ virtual const QMetaObject *metaObject() const; \ virtual void *qt_metacast(const char *); \ QT_TR_FUNCTIONS \ virtual int qt_metacall(QMetaObject::Call, int, void **); \ private:
其中的宏QT_TR_FUNCTIONS
# define QT_TR_FUNCTIONS \ static inline QString tr(const char *s, const char *c = 0) \ { return staticMetaObject.tr(s, c); } \ static inline QString trUtf8(const char *s, const char *c = 0) \ { return staticMetaObject.trUtf8(s, c); } \ static inline QString tr(const char *s, const char *c, int n) \ { return staticMetaObject.tr(s, c, n); } \ static inline QString trUtf8(const char *s, const char *c, int n) \ { return staticMetaObject.trUtf8(s, c, n); }
現在看到:tr調用的是 staticMetaObject對象的tr函數,staticMetaObject 的定義在moc生成的 xxx.moc 或 moc_xxx.cpp 文件內(你隨時可以驗證的)。
staticMetaObject 是一個 QMetaObject 類的實例,我們繼續看一下該類的源碼:
/*!
\internal */ QString QMetaObject::tr(const char *s, const char *c) const { return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr); } /*! \internal */ QString QMetaObject::trUtf8(const char *s, const char *c) const { return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8);
}