在C++工程中使用Qt翻译

一、使用翻译目的:

由于产品会面对不同的客户,使用翻译可以使得产品更好卖,会存在更多机会。

二、开发环境:

Window 10、Cmake、 C++、Qt。使用cmake管理组织代码,在开发时使用cmake生成Visual studio 2019工程,然后在这个工程里进行开发。

三、使用翻译的具体步骤:

cmake的代码组织架构:

project/  
├── CMakeLists.txt				# 项目的最外层构建文件  
├── subdirectory/				# 代码目录 
├── subdirectory/		    	# 代码目录
├── subdirectory/				# 代码目录
├── ...
├── install/bin					# 软件安装目录
├── install/cmake				 
│          └── common.cmake 	# 公共的cmake文件
├── install/component			# 项目组件路径
├── install/include				# 存放头文件目录
├── install/lib					# 存放库文件目录
└── install/...

cmake中的相关设置:

1. 在最外层cmake文件中设置一个全局属性:
set_property(GLOBAL PROPERTY INTERNATIONALIZATION ON)
  1. 在需要翻译的工程中进行如下设置
# 获取全局属性
get_property(INTERNATIONALIZATION GLOBAL PROPERTY "INTERNATIONALIZATION")
...

# 若在最外层设置开启翻译则生成项目的翻译文件
if(INTERNATIONALIZATION)
    find_package(Qt5LinguistTools)
    set(TS_FILES ${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_zh_cn.ts)
    qt5_create_translation(QM_FILES ${SRC_FILES} ${SRC_UI_FILES} ${TS_FILES} OPTIONS -source-language en_us -no-obsolete)
    add_library(${PROJECT_NAME} SHARED ... ${QM_FILES})
else()
    add_library(${PROJECT_NAME} SHARED ...)
endif()

对多个翻译文件进行合并:

由于多个待翻译的项目会生成多个翻译文件,但要是对每个翻译文件进行一次编译,太麻烦。合并翻译文件代码:
// 将多个翻译文件合并为一个
bool mergeTsFileList(const QStringList tsFiles, const QString& outputFilePath)
{
    QStringList contentList;
    for (const auto& path : tsFiles)
    {
        QFile file(path);
        if (!file.open(QFile::ReadOnly | QFile::Text)) {
            qDebug() << "open fail, xmlPath is:" << path + "\n" + file.errorString();
            return false;
        }

        QTextStream in(&file);
        // 设置为utf8才能保证中英文的正常读取
        in.setCodec("UTF-8");

        // 读取所有内容并移除前三行和最后一行
        QStringList content;
        while (!in.atEnd()) {
            content.push_back(in.readLine());
        }

        content.pop_front();
        content.pop_front();
        content.pop_front();
        content.pop_back();

        contentList += content;
    }

    // 保存目标XML文件
    QFile targetFile(outputFilePath);
    if (!targetFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        qDebug() << "无法打开目标文件";
        return false;
    }
    QTextStream out(&targetFile);

    // 写入XML声明
    out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << endl;
    out << "<!DOCTYPE TS>" << endl;
    out << "<TS version=\"2.1\" language=\"zh_CN\" sourcelanguage=\"en_us\">" << endl;
    for (const auto& temp : contentList) {
        out << temp << endl;
    }
    out << "</TS>" << endl;

    return true;
}

int main()
{
    // 替换为自己的目录
    QString dir = "xxx";
    
    // 替换为合并后的绝对路径
    QString mergedFile = "xxx\\language_en_us.ts";
    
    QDir directory(dir);
    QStringList filters;
    filters << "*.ts";
    QStringList tsFileList = directory.entryList(filters);
    for (auto& temp : tsFileList) {
        temp = dir + "\\" + temp;
    }

    mergeTsFileList(tsFileList, mergedFile);
        
    return 0;
}

制作短语书,并在合并文件后进行翻译:

1. 制作单词书:根据产生的翻译文件,把所有用source标注的文本进行提取到std::set中,保证唯一性,之后对std::set的内容全部打印后复制到翻译软件获取译文。有了原句和翻译好的句子,按照单词书的格式填入就行。

翻译文件的大致格式为:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN" sourcelanguage="en_us">
<context>
    <name>QObject</name>
    <message>
        <location filename="../idl/src/global_setting.cpp" line="60"/>
        <source>相对标尺</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../idl/src/global_setting.cpp" line="61"/>
        <source>绝对标尺</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../idl/src/global_setting.cpp" line="62"/>
        <source>自定义</source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>

单词书的格式:

<!DOCTYPE QPH>
<QPH language="en-us">
<phrase>
    <source>控制</source>
    <target>Control</target>
</phrase>
</QPH>
  1. 读取单词书的内容,对合并后的翻译文件进行翻译:
// 传入词典路径,获取词典内容
QMap<QString, QString> getDictionContent(const QString& path)
{
    QMap<QString, QString> dictionContent;
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Error opening file:" << path;
        return dictionContent;
    }

    QXmlStreamReader xmlReader(&file);
    while (!xmlReader.atEnd() && !xmlReader.hasError()) {
        if (xmlReader.readNextStartElement() && xmlReader.name() == "source") {
            QString source = xmlReader.readElementText();
            xmlReader.readNextStartElement();
            QString translation = xmlReader.readElementText();
            dictionContent.insert(source, translation);
        }
    }

    return dictionContent;
}

// 传入字典内容和翻译文件路径,在翻译后更新翻译文件
bool translateFile(const QMap<QString, QString>& dictionContent, const QString& path)
{
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Error opening file:" << path;
        return false;
    }

    QDomDocument docXML;
    QString errStr;
    int row, column;
    if (!docXML.setContent(&file, false, &errStr, &row, &column)) {
        qDebug() << path + "\nsetContent failed at line: " + QString::number(row) + "\n" + errStr;
        file.close();
        return false;
    }
    file.close();

    QDomElement root = docXML.documentElement();
    auto ctxList = root.childNodes();
    for (int ctxIndex = 0, ctxSize = ctxList.size(); ctxIndex < ctxSize; ++ctxIndex)
    {
        QDomNodeList msgList = ctxList.at(ctxIndex).childNodes();
        for (int msgIndex = 0, msgSize = msgList.size(); msgIndex < msgSize; ++msgIndex)
        {
            QDomElement msgElement = msgList.at(msgIndex).toElement();
            if (msgElement.nodeName() != "message") {
                qDebug() << "current name is:" << msgElement.text();
            }
            else {
                QDomElement translation = msgElement.firstChildElement("translation");
                QDomElement source = msgElement.firstChildElement("source");
                if (!translation.isNull() && !source.isNull()) {
                    if (dictionContent.contains(source.text()) == false) {
                        continue;
                    }

                    if (translation.attribute("type") == "unfinished") {
                        translation.removeAttribute("type");
                    }

                    QDomText newText = docXML.createTextNode(dictionContent[source.text()]);
                    translation.appendChild(newText);
                }
            }
        }
    }

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qDebug() << "Cannot open file for writing:" << path;
        return false;
    }

    QTextStream out(&file);
    docXML.save(out, 4); // 第二个参数是缩进空格数,用于格式化输出
    file.close();

    return true;
}

使用Qt语言家生成翻译的二进制文件:

使用Qt语言家打开翻译文件后,点击文件->发布,即可在翻译文件目录生成翻译好的二进制文件。 在可执行文件中使用翻译文件:
QApplication a(argc, argv);

// 安装翻译器,若是找到文件则翻译,一定要在QApplication构造之后QWidget构造之前进行加载
// tips:一定要使用二进制文件才可以进行翻译,否则load会失败
QTranslator translator;
if (translator.load(oct::rcf::utility::getAppPath() + "\\language_en_us.qm") && a.installTranslator(&translator)) {
    LOG_INFO << "Load translator success";
}
else {
    LOG_WARNING << "Load translator fail!";
}

QWidget w;
w.show();

return a.exec();

到此翻译就完成了,Qt的翻译是真的很方便,在.ui文件里面无论啥内容都不用管,.cpp文件只要用tr()包起来,一点也不污染代码,给它点赞。

http://blog.csdn.net/yulinxx/article/details/43260203 ------------------------------------------ C++ 国际化!!!!! 创建一个空白的Qt工程 添加一个main.cpp #include #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv) QWidget* pWnd = new QWidget(); QPushButton* pBtn = new QPushButton(QPushButton::tr("Test Translate")) QVBoxLayout* pVlayout = new QVBoxLayout(); pVlayout->addWidget(pBtn) pWnd->setLayout(pVlayout); pWnd->show(); return a.exec(); } 即可运行起来。。。。 此工程中,只有按钮上有文字。。。运行如下图示: 在工程中加入: TRANSLATIONS += testTrans_en.ts (ts文字名可随意取) 然后执行: Tools -> External -> Linguist -> Update Translations(lupdate) 提示完成: /usr/local/Qt5.4.0/5.4/gcc_64/bin/lupdate" finished 在工程中,可以发现多了一个 testTrans_en.ts 文件,此文件可用文本编辑器打开进行查看,如: QPushButton Test Translate 此步骤是将工程中的文字(加tr等包含的文字)进行提取到ts文件中 接下来,在终端运行 x@x:~$ linguist 打开 Qt Linguist 翻译工具,对文字进行逐个翻译并保存 在工程中再执行: Tools -> External -> Linguist -> Release Translations(lrelease) 结果提示:/usr/local/Qt5.4.0/5.4/gcc_64/bin/lrelease" finished 在工程中,可以发现多了一个 testTrans_en.qm文件,此文件为二进制格式文件,即我们最终使用翻译文件 然后添加Qt资源,将testTrans_en.qm添加进来 修改main.cpp文件,将翻译文件添加进来,最终执行 #include #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QTranslator qtTranslator; qtTranslator.load(":/translate/testTrans_en.qm") a.installTranslator(&qtTranslator); QWidget* pWnd = new QWidget(); QPushButton* pBtn = new QPushButton(QPushButton::tr("Test Translate")) QVBoxLayout* pVlayout = new QVBoxLayout(); pVlayout->addWidget(pBtn) pWnd->setLayout(pVlayout); pWnd->show() return a.exec() } 最终执行结果:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值