QT多语言包机制是个好东西,但是往往代码的修改,伴随着已有的翻译失效问题很头疼,急需一个工具能够召回已翻译的但是失效的项,本工具应运而生。
核心技术QT XML操作类QDomDocument
直接上代码:
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QLabel> #include <QCheckBox> #include <QTreeWidget> #include <QHBoxLayout> #include <QVBoxLayout> #include <QDomDocument> //去除语言文件中的失效项 class MainWindow : public QWidget { Q_OBJECT public: MainWindow(QWidget *parent = 0); public: signals: public slots: void onSlotChooseLangFileFunc();//选择语言文件 void onSlotRecallRepeatTranslationFunc();//召回重复的翻译 void onSlotTrimLangFileContextFunc();//输出过滤文件 void onSlotSelectedRightItemFunc(QModelIndex);//选择右边对应翻译项 protected: void resizeEvent(QResizeEvent *); private: void recallItemTranslation(QTreeWidgetItem* item); void readAndSave(); private: void prepareRepeatXml();//左边重复项 void prepareOutputXml();//右边输出项 private: void initControl(); void bindEvent(); private: QVBoxLayout* main_layout; QHBoxLayout* tool_layout; QVBoxLayout* repeat_title_layout; QVBoxLayout* output_title_layout; QHBoxLayout* content_layout; QHBoxLayout* save_layout; QLabel* url_label;//语言文件 QLineEdit* url_linedit; QPushButton* url_button;//选择文件 QPushButton* recall_button;//回收翻译 QCheckBox* doall_chkBox;//全部替换 QPushButton* trim_button;//清理操作 QPushButton* cancel_button; QLabel* repeat_title_label; QTreeWidget* repeat_treewidget;//重复树 QLabel* output_title_label; QTreeWidget* output_treewidget;//输出树 private: QDomDocument output_doc; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include <QHeaderView> #include <QMessageBox> #include <QFileDialog> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { main_layout = new QVBoxLayout(this); tool_layout = new QHBoxLayout(this); repeat_title_layout = new QVBoxLayout(this); output_title_layout = new QVBoxLayout(this); content_layout = new QHBoxLayout(this); save_layout = new QHBoxLayout(this); url_label = new QLabel(this);//语言文件 url_linedit = new QLineEdit(this);//语言文件 url_button = new QPushButton(this);//选择文件 recall_button = new QPushButton(this);//回收翻译 doall_chkBox = new QCheckBox(this);//全部替换 trim_button = new QPushButton(this);//清理操作 cancel_button = new QPushButton(this); repeat_title_label = new QLabel(this); repeat_treewidget = new QTreeWidget(this);//重复树 output_title_label = new QLabel(this); output_treewidget = new QTreeWidget(this);//输出树 tool_layout->setContentsMargins(0,0); tool_layout->addWidget(url_label); tool_layout->addWidget(url_linedit); tool_layout->addWidget(url_button); repeat_title_layout->setContentsMargins(0,0); repeat_title_layout->addWidget(repeat_title_label); repeat_title_layout->addWidget(repeat_treewidget); output_title_layout->setContentsMargins(0,0); output_title_layout->addWidget(output_title_label); output_title_layout->addWidget(output_treewidget); content_layout->setContentsMargins(0,0); content_layout->addLayout(repeat_title_layout); content_layout->addWidget(recall_button); content_layout->addLayout(output_title_layout); save_layout->setContentsMargins(0,0); save_layout->addWidget(doall_chkBox); save_layout->addStretch(0); save_layout->addWidget(trim_button); save_layout->addWidget(cancel_button); main_layout->setContentsMargins(20,20,20); main_layout->addLayout(tool_layout); main_layout->addSpacing(10); main_layout->addLayout(content_layout); main_layout->addSpacing(10); main_layout->addLayout(save_layout); this->setLayout(main_layout); this->initControl(); this->bindEvent(); this->resize(640,480); } void MainWindow::initControl() { url_label->setText(tr("language file:"));//语言文件 url_button->setText(tr("choose..."));//选择文件 recall_button->setText(">>"); recall_button->setToolTip(tr("recall translation"));//回收翻译 doall_chkBox->setText(tr("recall all"));//全部替换 trim_button->setText(tr("save"));//清理重复 cancel_button->setText(tr("cancel")); repeat_title_label->setText(tr("repeat items"));//重复的数据 repeat_title_label->setAlignment(Qt::AlignCenter); output_title_label->setText(tr("output items"));//输出的数据 output_title_label->setAlignment(Qt::AlignCenter); QStringList headLabels; headLabels<<tr("source language")<<tr("target language"); repeat_treewidget->setColumnCount(2); repeat_treewidget->setHeaderLabels(headLabels); //repeat_treewidget->header()->setSectionResizeMode(QHeaderView::Interactive); repeat_treewidget->headerItem()->setTextAlignment(0,Qt::AlignCenter); repeat_treewidget->headerItem()->setTextAlignment(1,Qt::AlignCenter); repeat_treewidget->setSelectionBehavior(QAbstractItemView::SelectRows); repeat_treewidget->setSelectionMode(QAbstractItemView::SingleSelection); repeat_treewidget->setFocusPolicy(Qt::NoFocus); output_treewidget->setColumnCount(2); output_treewidget->setHeaderLabels(headLabels); //output_treewidget->header()->setSectionResizeMode(QHeaderView::Interactive); output_treewidget->headerItem()->setTextAlignment(0,Qt::AlignCenter); output_treewidget->headerItem()->setTextAlignment(0,Qt::AlignCenter); output_treewidget->setSelectionBehavior(QAbstractItemView::SelectRows); output_treewidget->setSelectionMode(QAbstractItemView::SingleSelection); output_treewidget->setFocusPolicy(Qt::NoFocus); QString style = "QTreeView{ background:#ffffff;color:#202020; }" "QTreeView::item{ height:30px; }" "QTreeView::item:disabled{ color:#cccccc; }" "QTreeView::item:hover{ background-color:rgb(0,255,30); }" "QTreeView::item:selected{ background-color:rgb(30,80,80); }"; repeat_treewidget->setStyleSheet(style); output_treewidget->setStyleSheet(style); } void MainWindow::bindEvent() { connect( url_button,SIGNAL(clicked()),this,SLOT(onSlotChooseLangFileFunc()) ); connect( recall_button,SLOT(onSlotRecallRepeatTranslationFunc()) ); connect( trim_button,SLOT(onSlotTrimLangFileContextFunc()) ); connect( cancel_button,SLOT(close()) ); connect( repeat_treewidget,SIGNAL(clicked(QModelIndex)),SLOT(onSlotSelectedRightItemFunc(QModelIndex)) ); } void MainWindow::onSlotChooseLangFileFunc() { QString langFile = QFileDialog::getOpenFileName(0,tr("choose language file"),tr("."),"qt ts language(*.ts)"); if( langFile.length()<=0 ) { return ; } url_linedit->setText(langFile); this->prepareRepeatXml(); this->prepareOutputXml(); } //召回重复的翻译选项 void MainWindow::onSlotRecallRepeatTranslationFunc() { QTreeWidgetItem* item = repeat_treewidget->currentItem(); if( !item )//没有选中 { if( doall_chkBox->isChecked() ) { if( QMessageBox::Yes==QMessageBox::question(0,tr("notice"),tr("will replace all the existing translation! continue?")) ) { QTreeWidgetItemIterator it(repeat_treewidget); while( *it )//召回全部项 { this->recallItemTranslation(*it); ++it; } } } else { QMessageBox::warning(0,tr("select an repeat translation!")); } return ; } //召回选中项 this->recallItemTranslation(item); } //保存召回的翻译XML void MainWindow::onSlotTrimLangFileContextFunc() { QString langUrl = url_linedit->text(); if( langUrl.length()<=0 ) { QMessageBox::warning(0,tr("language url is null!")); return ; } //保存修改后的 langUrl += ".mod.ts"; QFile ofile(langUrl); if( !ofile.open(QIODevice::WriteOnly | QIODevice::Truncate |QIODevice::Text) ) { return ; } QTextStream out(&ofile); out.setCodec("UTF-8"); output_doc.save(out,4,QDomNode::EncodingFromTextStream); ofile.close(); } void MainWindow::onSlotSelectedRightItemFunc(QModelIndex) { QTreeWidgetItem* item = repeat_treewidget->currentItem(); if( !item ) { return ; } output_treewidget->setCurrentItem(NULL);//取消选择 //选择节点的父节点 QTreeWidgetItem* itemparent = item->parent(); if( !itemparent ) { return ; } //qDebug()<<"当前节点——"<<item->text(0)<<"其父节点——"<<itemparent->text(0); //遍历右边树 QTreeWidgetItemIterator it(output_treewidget); while( *it ) { if( (*it)->text(0).compare(item->text(0),Qt::CaseSensitive)==0 && !(*it)->text(1).isEmpty() )//节点关键字 { QTreeWidgetItem* itemcmp = (*it)->parent();//父节点关键字 if( itemcmp && itemcmp->text(0).compare(itemparent->text(0),Qt::CaseSensitive)==0 ) { //qDebug()<<"右边当前节点——"<<(*it)->text(0)<<"其父节点——"<<itemcmp->text(0); output_treewidget->setCurrentItem(*it);//选中对应项 break; } } ++it; } } void MainWindow::resizeEvent(QResizeEvent *e) { QWidget::resizeEvent(e); repeat_treewidget->setColumnWidth(0,repeat_treewidget->width()*0.5); output_treewidget->setColumnWidth(0,output_treewidget->width()*0.5); } void MainWindow::recallItemTranslation(QTreeWidgetItem *item) { if( !item )//重复选项 { return ; } //选中对应的右边选项,替换其翻译 repeat_treewidget->setCurrentItem(item); repeat_treewidget->clicked(repeat_treewidget->currentIndex()); if( output_treewidget->currentItem() ) { output_treewidget->currentItem()->setText(1,item->text(1)); output_treewidget->currentItem()->setToolTip(0,item->text(1)); } //获取父节点名称 QTreeWidgetItem* itemparent = item->parent(); if( !itemparent ) { qDebug()<<"节点父节点未找到——"<<item->text(0); return ; } //修改去重后的翻译XML if( output_doc.isNull() ) { qDebug()<<"XML----"<<output_doc.isNull(); return ; } QDomElement root = output_doc.documentElement();//根节点 QDomNode node = root.firstChild(); while( !node.isNull() ) { QDomElement ele = node.toElement(); if( !ele.isNull() && ele.tagName().compare("context",Qt::CaseInsensitive)==0 ) { //qDebug()<<"遍历节点——"<<ele.tagName(); QDomElement e = ele.firstChildElement();//子节点 while( !e.isNull() ) { if( e.tagName().compare("name",Qt::CaseInsensitive)==0 ) { if( e.text().compare(itemparent->text(0),Qt::CaseSensitive)!=0 ) { break; } //qDebug()<<"XML中找到节点——"<<e.text()<<itemparent->text(0); } if( e.tagName().compare("message",Qt::CaseInsensitive)==0 ) { QDomElement eTran = e.firstChildElement(); while( !eTran.isNull() ) { //qDebug()<<"1111XML中找到翻译——"<<eTran.tagName()<<item->text(0); if( eTran.tagName().compare("source",Qt::CaseSensitive)==0 ) { if( eTran.text().compare(item->text(0),Qt::CaseSensitive)==0 ) { //qDebug()<<"2222XML中找到翻译——"<<eTran.text()<<item->text(0); eTran = eTran.nextSiblingElement("translation"); if( !eTran.isNull() ) { //eTran.setNodeValue(item->text(1));//设置节点数据,没作用 eTran.firstChild().setNodeValue(item->text(1));//修改节点数据,可以 qDebug()<<"3333XML中写入翻译——"<<eTran.tagName()<<eTran.text(); } break; } } eTran = eTran.nextSiblingElement(); } } e = e.nextSiblingElement(); }// } node = node.nextSibling(); } } void MainWindow::readAndSave() { QString langUrl = url_linedit->text(); if( langUrl.length()<=0 ) { QMessageBox::warning(0,tr("language url is null!")); return ; } QFile file(langUrl); if( !file.open(QFile::ReadOnly|QFile::Text) ) { QMessageBox::warning(0,tr("language file open Failed!")); return ; } QDomDocument doc; if( !doc.setContent(&file) ) { file.close(); return ; } file.close(); if( doc.isNull() ) { qDebug()<<"XML----"<<doc.isNull(); return ; } QDomElement root = doc.documentElement(); QString rootName = root.tagName(); if( rootName.compare("ts",Qt::CaseInsensitive)==0 ) { //qDebug()<<"version---"<<rootName<<root.hasAttribute("version"); } QDomNode node = root.firstChild(); while( !node.isNull() ) { QDomElement ele = node.toElement(); if( !ele.isNull() && ele.tagName().compare("context",Qt::CaseInsensitive)==0 ) { //qDebug()<<"to translate file----"<<e.tagName()<<e.text(); } if( e.tagName().compare("message",Qt::CaseInsensitive)==0 ) { QDomElement eLoc = e.firstChildElement("location"); if( eLoc.isNull() ) { //qDebug()<<"找不到location节点,就是应该删除的message项!"; QDomElement temp = e; e = e.nextSiblingElement(); ele.removeChild(temp); continue; } else { //qDebug()<<"find lang file location----"<<eLoc.attribute("filename") // <<eLoc.attribute("line"); } } e = e.nextSiblingElement(); }// } node = node.nextSibling(); } //保存修改后的 langUrl += ".mod.ts"; QFile ofile(langUrl); if( !ofile.open(QIODevice::WriteOnly | QIODevice::Truncate |QIODevice::Text) ) { return ; } QTextStream out(&ofile); out.setCodec("UTF-8"); doc.save(out,QDomNode::EncodingFromTextStream); ofile.close(); } void MainWindow::prepareRepeatXml() { QString langUrl = url_linedit->text(); if( langUrl.length()<=0 ) { QMessageBox::warning(0,tr("language url is null!")); return ; } repeat_treewidget->clear(); QFile file(langUrl); if( !file.open(QFile::ReadOnly|QFile::Text) ) { QMessageBox::warning(0,tr("language file open Failed!")); return ; } QDomDocument doc; if( !doc.setContent(&file) ) { file.close(); return ; } file.close(); if( doc.isNull() ) { qDebug()<<"XML----"<<doc.isNull(); return ; } QTreeWidgetItem* itemRoot = NULL;//树根节点 QDomElement root = doc.documentElement(); if( root.tagName().compare("ts",Qt::CaseInsensitive)==0 ) { itemRoot = new QTreeWidgetItem(0);//根节点 itemRoot->setText(0,root.tagName()); repeat_treewidget->addTopLevelItem(itemRoot); } QDomNode node = root.firstChild(); while( !node.isNull() ) { QDomElement ele = node.toElement(); if( !ele.isNull() && ele.tagName().compare("context",Qt::CaseInsensitive)==0 ) { //qDebug()<<"遍历节点——"<<ele.tagName(); QTreeWidgetItem* itemFile = NULL;//文件节点 QString nodeName;//文件节点名称 QDomElement e = ele.firstChildElement();//子节点 while( !e.isNull() ) { if( e.tagName().compare("name",Qt::CaseInsensitive)==0 ) { nodeName = e.text(); } if( e.tagName().compare("message",Qt::CaseInsensitive)==0 ) { QDomElement eLoc = e.firstChildElement("location");//文中位置 if( eLoc.isNull() ) { itemFile = new QTreeWidgetItem(0);//context name节点 itemFile->setText(0,nodeName); itemRoot->addChild(itemFile); QStringList stringLang;//语言翻译数据 eLoc = e.firstChildElement();//再次取值 while( !eLoc.isNull() ) { //qDebug()<<eLoc.tagName()<<eLoc.text(); if( eLoc.tagName().compare("source",Qt::CaseInsensitive)==0 || eLoc.tagName().compare("translation",Qt::CaseInsensitive)==0 ) { stringLang<<eLoc.text(); } eLoc = eLoc.nextSiblingElement(); } if( itemFile )//添加语言翻译 { QTreeWidgetItem* itemLang = new QTreeWidgetItem(0);//context message location节点 itemLang->setText(0,stringLang.first()); itemLang->setToolTip(0,stringLang.last()); itemLang->setText(1,stringLang.last()); itemFile->addChild(itemLang); } //移除失效的节点 //QDomElement temp = e; e = e.nextSiblingElement(); //ele.removeChild(temp); //continue; } } e = e.nextSiblingElement(); }// } node = node.nextSibling(); } //展开所有项 repeat_treewidget->expandAll(); } void MainWindow::prepareOutputXml() { QString langUrl = url_linedit->text(); if( langUrl.length()<=0 ) { QMessageBox::warning(0,tr("language url is null!")); return ; } output_treewidget->clear(); QFile file(langUrl); if( !file.open(QFile::ReadOnly|QFile::Text) ) { QMessageBox::warning(0,root.tagName()); output_treewidget->addTopLevelItem(itemRoot); } QDomNode node = root.firstChild(); while( !node.isNull() ) { QDomElement ele = node.toElement(); if( !ele.isNull() && ele.tagName().compare("context",Qt::CaseInsensitive)==0 )//文件名节点 { nodeName = e.text(); } if( e.tagName().compare("message",Qt::CaseInsensitive)==0 )//翻译节点 { QDomElement eLoc = e.firstChildElement("location");//文中位置 if( eLoc.isNull() ) { //移除失效的节点 QDomElement temp = e; e = e.nextSiblingElement(); ele.removeChild(temp); continue; } else { itemFile = new QTreeWidgetItem(0);//context name节点 itemFile->setText(0,nodeName); itemRoot->addChild(itemFile); QStringList stringLang;//语言翻译数据 while( !eLoc.isNull() ) { //qDebug()<<eLoc.tagName()<<eLoc.text(); if( eLoc.tagName().compare("source",Qt::CaseInsensitive)==0 ) { stringLang<<eLoc.text(); } eLoc = eLoc.nextSiblingElement(); } if( itemFile )//添加语言翻译 { QTreeWidgetItem* itemLang = new QTreeWidgetItem(0);//context message location节点 itemLang->setText(0,stringLang.last()); itemFile->addChild(itemLang); } } }//end message e = e.nextSiblingElement(); }// } node = node.nextSibling(); } //保存修改后的 output_doc = doc; //langUrl += ".mod.ts"; //QFile ofile(langUrl); //if( !ofile.open(QIODevice::WriteOnly | QIODevice::Truncate |QIODevice::Text) ) //{ // return ; //} //QTextStream out(&ofile); //out.setCodec("UTF-8"); //doc.save(out,QDomNode::EncodingFromTextStream); //file.close(); //展开所有项 output_treewidget->expandAll(); }
by houwenbin1986@gmail.com(http://blog.csdn.net/houwenbin1986) TrimTSFile是一个去除QT语言文件失效项的小工具,同时,可以针对失效项已有的翻译进行召回,可以单条选中召回,也可以全部匹配召回。最后保存为纯净的语言包TS文件。再导入Linguist编辑!