摘要:執行完畢,目錄下會多出一個文件,這個文件就是最終需要放在應用程序中分發給用戶的。問題使用上面的流程就完成了一次基本的程序國際化的流程。
Qt 是一個很方便的 C++ 應用開發框架(或許現在要加上 Qt Quick 開發框架?),不僅僅是程序編寫方便面,它提供了很多方便的類庫,而且它也提供了很方便的國際化方案(也就是翻譯成各國語言的方案)。
基本流程 編寫代碼階段我們先來說說在 Qt 中實現多國語言翻譯需要使用的基本流程。首先我們需要在編寫代碼的時候就要使用 Qt 提供的翻譯相關的函數來"包裹"住所有的需要翻譯的字符串。
你說哪些才是需要翻譯的字符串呢?就是任何會在用戶界面上顯示的字符串,如果不會顯示自然就不需要翻譯了。
如果你使用的是 C++ 代碼,那么翻譯用的函數就是QObject::tr函數。大多數時候,我們看到用到這個函數的時候可能都只是一個tr,因為都是在一個繼承自 QObject 的類中,所以可以直接調用父類的成員函數了。如果是在自由函數中想使用的話就要把完整的函數名QObject::tr寫全了。
如果你寫的是 Qt Quick 的程序,這個函數就變成了 qsTr 或者是 qsTranslate 、qsTranslateNoOp ...
代碼寫完之后,需要在工程文件 (.pro 文件) 中添加需要翻譯的源代碼文件。
SOURCES += main.cpp mainwindow.cpp TRANSLATIONS = English.ts Japanese.ts使用 lupdate 檢索源代碼生成翻譯源文件
在工程目錄下運行以下命令
lupdate project.pro
執行完后,目錄下會出現 English.ts 和 Japanese.ts 兩個文件,這兩個文件就是由上文中工程文件中制定的文件名。
使用 linguist 進行翻譯然后就可以使用 Qt 提供的 lingust 工具打開上文生成的 .ts 文件,將對應的詞條翻譯成目標語言就好了。
使用 lrelease 生成最終程序使用的翻譯文件當 linguist 翻譯完所有的詞條之后,就需要生成最終給應用程序使用的二進制翻譯文件了。
使用如下命令即可生成。
lrelease English.ts
執行完畢,目錄下會多出一個 English.qm 文件,這個文件就是最終需要放在應用程序中分發給用戶的。
問題使用上面的流程就完成了一次基本的程序國際化的流程。一般來說程序的翻譯工作和編碼工作是由不同人來完成的,可能實際完成翻譯的人不熟悉或者不愿去熟悉使用 linguist 的使用方法,他們提供給程序員的詞條翻譯可能就是一個 Excel 文件,這個 Excel 文件通常一列是待翻譯的詞條,另一列是相應的譯文。
解決方法于是我們可能需要一個將 Qt 的翻譯文件與 Excel 文件互相轉換的小工具,這樣我們可以把 Qt 翻譯文件轉換成 Excel 文件交給翻譯人員,然后將翻譯人員翻譯好的 Excel 文件再轉換為 Qt 的翻譯文件。
要做文件格式轉換,那么我們首先得了解一下兩種文件格式有什么特點。
Qt TS 文件格式分析Qt 的 TS 翻譯文件其實就是一個 XML 文件。我們先來看一個翻譯文件的例子。
QPushButton
這是一個還未翻譯的文件,詞條原文是"Hello world!"。在 ts 文件中,每個需要翻譯的詞條都是一個 message,message 包含需要翻譯的原文和已翻譯的譯文。
翻譯完成后需要去除 translation 的 type="unfinished" 屬性。
Excel 文件格式分析Excel 的文件格式是 Microsoft ? 的私有格式,Excel 2007 之前的格式是二進制格式, Excel 2007 之后的是 OOXML 格式。
在這里我們不用太細的追究它的內部細節,因為它的內部細節非常復雜,不像 ts 文件就是一個簡單的文本文件。現在有許多的可以用來讀寫 Excel 文件的庫,我們只需調用這些庫的讀寫函數就能完成我們所需的功能了。
在本文中,我將使用 C# 語言以及 NPOI 庫來讀寫 Excel 文件。
private void convertQtFile2ExcelFile(string qtFileName, string excelFileName) { XmlDocument xmlDoc = new XmlDocument(); XmlReaderSettings settings = new XmlReaderSettings(); settings.IgnoreComments = true; settings.DtdProcessing = DtdProcessing.Ignore; IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("worksheet"); int rowCnt = 0; IRow headRow = sheet.CreateRow(rowCnt); rowCnt++; ICell chCell = headRow.CreateCell(0); chCell.SetCellValue("源語言"); ICell enCell = headRow.CreateCell(1); enCell.SetCellValue("目標語言"); XmlReader reader = XmlReader.Create(qtFileName, settings); xmlDoc.Load(reader); XmlNode rootNode = xmlDoc.SelectSingleNode("TS"); XmlNodeList xnl = rootNode.ChildNodes; foreach (XmlNode node in xnl) { XmlNodeList nodeList = node.ChildNodes; string src = ""; string dst = ""; foreach (XmlNode n in nodeList) { foreach (XmlNode cn in n.ChildNodes) { if (cn.Name == "source") { src = cn.InnerText; } if (cn.Name == "translation") { dst = cn.InnerText; IRow row = sheet.CreateRow(rowCnt); rowCnt++; ICell firstCell = row.CreateCell(0); firstCell.SetCellValue(src); ICell secondCell = row.CreateCell(1); secondCell.SetCellValue(dst); //Console.WriteLine ("Src = " + src + " dst = " + dst); } } } } reader.Close(); using (FileStream fs = File.Create(excelFileName)) { workbook.Write(fs); } }Excel 轉 TS 文件
private bool convertExcelFile2QtFile(string excelFileName, string qtFileName) { try { using (var fs = File.OpenRead(excelFileName)) { var workBook = new XSSFWorkbook(fs); var sheet = workBook.GetSheetAt(0); var translateMap = new Dictionary自動翻譯詞條(); for (int i = 1; i < sheet.LastRowNum; i++) { IRow row = sheet.GetRow(i); if (row == null) { continue; } var srcCell = row.GetCell(0); var dstCell = row.GetCell(1); if (srcCell == null) { continue; } if (dstCell == null) { continue; } string src = srcCell.ToString(); string translated = dstCell.ToString(); if (translateMap.ContainsKey(src) == false) { translateMap.Add(src, translated); } } var document = new XmlDocument(); document.Load(qtFileName); XmlNodeList xnl = document.SelectNodes("/TS/context"); if (xnl != null) { foreach (XmlNode ctxNode in xnl) { var msgList = ctxNode.SelectNodes("message"); foreach (XmlNode msg in msgList) { string src = ""; foreach (XmlNode cn in msg.ChildNodes) { if (cn.Name == "source") { src = cn.InnerText; } else if (cn.Name == "translation") { if (translateMap.ContainsKey(src)) { cn.InnerText = translateMap[src]; var attrs = cn.Attributes; attrs.Remove(attrs["type"]); } } } } } } document.Save(qtFileName); } return true; } catch (Exception e) { MessageBox.Show("Exception:" + e.Message); return false; } }
其實了解了 TS 文件的結構之后,我們還能做一件事,那就是直接調用網上的翻譯接口,自動將所有的詞條翻譯成對應的語言,下面就是一個簡單的例子。
import xml.etree.ElementTree as ET import os import httplib import md5 import urllib import random import json import threading import requests from PyQt4.QtCore import * from PyQt4.QtGui import * import sys reload(sys) sys.setdefaultencoding("utf-8") ## 下面填寫你自己的百度翻譯 API 的 appid 和 secretKey appid = "" secretKey = "" myurl = "/api/trans/vip/translate" def translate(string, targetLanguage): if string is None: return "" salt = random.randint(32768, 65536) sign = appid + string + str(salt) + secretKey m1 = md5.new() m1.update(sign) sign = m1.hexdigest() encoded_str = urllib.quote(str(string)) localurl = myurl + "?appid=" + appid + "&q=" + encoded_str + "&from=auto&to=" + targetLanguage + "&salt=" + str(salt) + "&sign=" + sign try: r = requests.get("http://api.fanyi.baidu.com/" + localurl) if r.status_code == 200: response = r.text objResponse = json.loads(response) return objResponse["trans_result"][0]["dst"] except Exception, e: print e return string class MainWidget(QWidget): def __init__(self, parent=None): super(QWidget, self).__init__(parent) self.setWindowTitle(u"UI Auto Translator") self.labelTargetLanguage = QLabel(u"目標語言:") self.comboTargetLanguage = QComboBox() self.comboTargetLanguage.addItem(u"中文", u"zh") self.comboTargetLanguage.addItem(u"英語", u"en") self.comboTargetLanguage.addItem(u"粵語", u"yue") self.comboTargetLanguage.addItem(u"文言文", u"wyw") self.comboTargetLanguage.addItem(u"日語", u"jp") self.comboTargetLanguage.addItem(u"韓語", u"kor") self.comboTargetLanguage.addItem(u"法語", u"fra") self.comboTargetLanguage.addItem(u"西班牙語", u"spa") self.comboTargetLanguage.addItem(u"泰語", u"th") self.comboTargetLanguage.addItem(u"阿拉伯語", u"ara") self.comboTargetLanguage.addItem(u"俄語", u"ru") self.comboTargetLanguage.addItem(u"葡萄牙語", u"pt") self.comboTargetLanguage.addItem(u"德語", u"de") self.comboTargetLanguage.addItem(u"意大利語", u"it") self.comboTargetLanguage.addItem(u"希臘語", u"el") self.comboTargetLanguage.addItem(u"荷蘭語", u"nl") self.comboTargetLanguage.addItem(u"波蘭語", u"pl") self.comboTargetLanguage.addItem(u"保加利亞語", u"bul") self.comboTargetLanguage.addItem(u"愛沙尼亞語", u"est") self.comboTargetLanguage.addItem(u"丹麥語", u"dan") self.comboTargetLanguage.addItem(u"芬蘭語", u"fin") self.comboTargetLanguage.addItem(u"捷克語", u"cs") self.comboTargetLanguage.addItem(u"羅馬尼亞語", u"rom") self.comboTargetLanguage.addItem(u"斯洛文尼亞語", u"slo") self.comboTargetLanguage.addItem(u"瑞典語", u"swe") self.comboTargetLanguage.addItem(u"匈牙利語", u"hu") self.comboTargetLanguage.addItem(u"繁體中文", u"cht") self.comboTargetLanguage.addItem(u"越南語", u"vie") self.labelSrcFile = QLabel(u"源文件") self.inputSrcFile = QLineEdit() self.inputSrcBtn = QPushButton(u"打開文件") self.connect(self.inputSrcBtn, SIGNAL( "clicked()"), self.onInputSrcBtnClicked) self.labelTargetFile = QLabel(u"保存文件") self.inputTargetFile = QLineEdit() self.inputTargetBtn = QPushButton(u"選擇文件名") self.connect(self.inputTargetBtn, SIGNAL( "clicked()"), self.onInputTargetBtnClicked) self.cvtBtn = QPushButton(u"開始翻譯") self.connect(self.cvtBtn, SIGNAL("clicked()"), self.onCvtBtnClicked) self.gridLayout = QGridLayout() self.gridLayout.addWidget(self.labelTargetLanguage, 0, 0) self.gridLayout.addWidget(self.comboTargetLanguage, 0, 1) self.gridLayout.addWidget(self.labelSrcFile, 1, 0) self.gridLayout.addWidget(self.inputSrcFile, 1, 1) self.gridLayout.addWidget(self.inputSrcBtn, 1, 2) self.gridLayout.addWidget(self.labelTargetFile, 2, 0) self.gridLayout.addWidget(self.inputTargetFile, 2, 1) self.gridLayout.addWidget(self.inputTargetBtn, 2, 2) self.vLayout = QVBoxLayout() self.vLayout.addLayout(self.gridLayout) self.vLayout.addWidget(self.cvtBtn) self.setLayout(self.vLayout) @pyqtSlot() def onInputSrcBtnClicked(self): fileName = QFileDialog.getOpenFileName( self, u"打開要翻譯的文件", u".", u"*.ts") if fileName.isEmpty(): QMessageBox.warning(self, u"警告", u"未選擇要翻譯的文件", QMessageBox.Ok) return self.inputSrcFile.setText(fileName) @pyqtSlot() def onInputTargetBtnClicked(self): fileName = QFileDialog.getSaveFileName( self, u"選擇另存為的文件名", u".", u"*.ts") if fileName.isEmpty(): QMessageBox.warning(self, u"警告", u"未選擇要保存為的文明名", QMessageBox.Ok) return self.inputTargetFile.setText(fileName) def process_file(self, src, dst, lang): self.cvtBtn.setText(u"正在翻譯中...") tree = ET.parse(src) root = tree.getroot() source = "" for ctx in root: for msg in ctx: if msg.tag == "message": for tag in msg: if tag.tag == "source": source = tag.text if tag.tag == "translation": tag.text = translate(source, lang) print("source = {0}, translate = {1}".format(source, tag.text)) tag.set("type", "") tree.write(dst) self.cvtBtn.setText(u"開始翻譯") @pyqtSlot() def onCvtBtnClicked(self): targetLang = self.comboTargetLanguage.itemData( self.comboTargetLanguage.currentIndex()).toString() srcFileName = self.inputSrcFile.text() if srcFileName.isEmpty(): QMessageBox.warning(self, u"警告", u"未選擇要翻譯的文件", QMessageBox.Ok) return targetFileName = self.inputTargetFile.text() if targetFileName.isEmpty(): QMessageBox.warning(self, u"警告", u"未選擇要保存為的文明名", QMessageBox.Ok) return t = threading.Thread(target=self.process_file, args=(srcFileName, targetFileName, str(targetLang))) t.setDaemon(True) t.start() if __name__ == "__main__": app = QApplication(sys.argv) widget = MainWidget() widget.show() app.exec_()
https://huzhenyu.me/qt/2018/0...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41581.html
摘要:友情提示先關注收藏,再查看,萬字保姆級語言從入門到精通教程。及大牛出天地開始有隨之乃有萬種語年英國劍橋大學推出了語言。 友情提示:先關注收藏,再查看,13 萬字保...
摘要:可暫停顯示收發數據。定時器自動發送。同時支持嵌入式樹莓派等。三效果圖四開源主頁以上作品完整源碼下載都在開源主頁,會持續不斷更新作品數量和質量,歡迎各位關注。本開源項目已經成功升級到版本,分門別類,圖文并茂,保你爽到爆。 ...
摘要:作者逆向驛站微信公眾號逆向驛站知乎逆向驛站一款開發的國外軟件,大概率是沒有做中文支持的,所以你漢化中,不論怎么設置編碼都一定是亂碼。 作者:逆向驛站微信公眾號:逆向驛站知乎:逆向驛站showImg(https://segmentfault.com/img/bVbnE98?w=1100&h=731); 一款QT開發的國外軟件,大概率是沒有做中文支持的,所以你漢化中,不論怎么設置編碼都一定...
閱讀 2866·2021-11-11 10:58
閱讀 1920·2021-10-11 10:59
閱讀 3489·2019-08-29 16:23
閱讀 2324·2019-08-29 11:11
閱讀 2785·2019-08-28 17:59
閱讀 3838·2019-08-27 10:56
閱讀 2049·2019-08-23 18:37
閱讀 3111·2019-08-23 16:53