admin管理员组

文章数量:1574090

QT-自动在线翻译工具,适用于国际化多语言应用

  • 前言
  • 一、演示效果
  • 二、关键程序
  • 三、程序下载链接


前言

有时候我们在多语言版本的时候,如果是手动翻译的话需要将我们的中文文本拷贝到百度翻译或者谷歌翻译去翻译译文,然后再填入对应的翻译内容,这种操作如果 是量少的话还能接受,当时但是如果是大量的翻译文本,那就真的要哭死了,基于这个麻烦事儿,我们是自己手动写个翻译工具比较合适,整个的基本思路如下:
1、申请百度翻译免费账户,一天5万字使用,自己够够的。
2、根据http的api接口,测试上传源文和接收的译文。
3、然后根据格式自己去拆分出来。

一、演示效果

二、关键程序

代码如下:

#include "widget.h"
#include "ui_widget.h"

#include <QFileDialog>

//#define m_strApId 20190930000338348
//#define m_strApIdKey "haynzrywFQvJHuFYXRaJ"
//#define m_strApId 20210805000907975
//#define m_strApIdKey "4nYsiVhc4lHQCAfmPCuv"

#pragma execution_character_set("utf-8")

const QString tagMessage("message");
const QString tagTranslation("translation");
const QString tagSource("source");

// 不阻塞定时器
struct sMyTimeout
{
	QTime time;
	uint32_t interval;

	void start(uint32_t t)
	{
		interval = t;
		time.start();
	};

	bool isTimeOut()
	{
		return time.elapsed() > interval;
	};
};

Widget::Widget(QWidget *parent) :
	QWidget(parent),
	ui(new Ui::Widget)
	, m_strFrom("zh")
	, m_strTo("en")
	, m_strOpenFile("")
	, m_bTranslateAll(false)
	, m_bTranslateStart(false)
	, m_nCurrentHashIndex(0)
	, m_nModelStep(0)
	, m_nOneStep(0)
	, m_nAllStep(0)
{
    ui->setupUi(this);

	this->setWindowIcon(QIcon(":/Resource/main.png"));
    this->setWindowTitle(tr("自动翻译工具(v2.0.0)"));

	// 动态检测网络状态线程
    m_pNetworkDetectionThread = new CNetworkDetectionThread();

	// 启动线程
    if(m_pNetworkDetectionThread)
         m_pNetworkDetectionThread->start();

	m_pManager = new QNetworkAccessManager(this);
	if (m_pManager)
		connect(m_pManager, &QNetworkAccessManager::finished, this, &Widget::slotReplyFinished);

	initForm();
	signalSlotDefine();

	m_timer.start(100);
}

Widget::~Widget()
{
	if (m_pNetworkDetectionThread)
	{
		delete m_pNetworkDetectionThread;
		m_pNetworkDetectionThread = NULL;
	}

	delete ui;
}

// 初始化窗体
void Widget::initForm()
{
	QTableWidget *table = ui->tableWidget;
	QStringList strHeaderList;
	strHeaderList << QObject::tr("源文") << QObject::tr("译文");
	table->setColumnCount(strHeaderList.size());
	table->setHorizontalHeaderLabels(strHeaderList);
	table->verticalHeader()->setMinimumWidth(30);
	table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
	table->horizontalHeader()->setStretchLastSection(true);
	table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
	table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
	table->setSelectionMode(QAbstractItemView::SingleSelection);

	ui->progressBar->setValue(0);

	ui->comboBoxSrc->addItem(tr("zh|中文"));
	ui->comboBoxSrc->addItem(tr("en|英文"));
	ui->comboBoxSrc->addItem(tr("spa|西班牙语"));
	ui->comboBoxSrc->addItem(tr("pt|葡萄牙语"));
	ui->comboBoxSrc->addItem(tr("jp|日语"));
	ui->comboBoxSrc->addItem(tr("kor|韩语"));
	ui->comboBoxSrc->addItem(tr("cht|繁体"));
	ui->comboBoxSrc->addItem(tr("de|德语"));
	ui->comboBoxSrc->addItem(tr("bur|缅甸语"));
	ui->comboBoxSrc->addItem(tr("th|泰语"));
	ui->comboBoxSrc->addItem(tr("fil|菲律宾语"));
	ui->comboBoxSrc->addItem(tr("fra|法语"));

	ui->comboBoxTag->addItem(tr("en|英文"));
	ui->comboBoxTag->addItem(tr("spa|西班牙语"));
	ui->comboBoxTag->addItem(tr("zh|中文"));
	ui->comboBoxTag->addItem(tr("pt|葡萄牙语"));
	ui->comboBoxTag->addItem(tr("jp|日语"));
	ui->comboBoxTag->addItem(tr("kor|韩语"));
	ui->comboBoxTag->addItem(tr("cht|繁体"));
	ui->comboBoxTag->addItem(tr("de|德语"));
	ui->comboBoxTag->addItem(tr("bur|缅甸语"));
	ui->comboBoxTag->addItem(tr("th|泰语"));
	ui->comboBoxTag->addItem(tr("fil|菲律宾语"));
	ui->comboBoxTag->addItem(tr("fra|法语"));
}

// 信号槽连接
void Widget::signalSlotDefine()
{
	connect(ui->btnOpen,&QPushButton::clicked,this,&Widget::slotOpenFile);
	connect(ui->btnSave, &QPushButton::clicked, this, &Widget::slotSaveFile);
	connect(ui->btnTranslateCurrent, &QPushButton::clicked, this, &Widget::slotTranslateCurrent);
	connect(ui->btnTranslateAll, &QPushButton::clicked, this, &Widget::slotTranslateAll);
	connect(&m_timer, &QTimer::timeout, this, &Widget::slotOnTime);

	connect(ui->comboBoxSrc, SIGNAL(currentTextChanged(const QString&)), this, SLOT(slotFromChange(const QString&)));
	connect(ui->comboBoxTag, SIGNAL(currentTextChanged(const QString&)), this, SLOT(slotToChange(const QString&)));
	connect(ui->tableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(slotTablewidgetCellChanged(int,int)));
}

// 源语种下拉框改变
void Widget::slotFromChange(const QString strText)
{
	if (strText.isEmpty())
		return;

	QStringList strList = strText.split("|");
	if (strList.size() == 2)
		m_strFrom = strList.at(0);

}

// 译语种下拉框改变
void Widget::slotToChange(const QString strText)
{
	if (strText.isEmpty())
		return;

	QStringList strList = strText.split("|");
	if (strList.size() == 2)
		m_strTo = strList.at(0);
}

// 单元项编辑事件
void Widget::slotTablewidgetCellChanged(int nRow, int nColumn)
{
	if (nRow < 0 || nColumn < 0)
		return;

	auto pItem = ui->tableWidget->item(nRow, nColumn);
	if (pItem == nullptr)
		return;
	
	auto keys = m_ScrTranHash.keys();
	m_ScrTranHash[keys[nRow]] = pItem->text();
}

// 上传译文
int Widget::slotUploadData(QString strSource)
{
	strSource = strSource.replace("#", "");
	strSource = strSource.replace("_", "");
	strSource = strSource.replace("%", "");
	strSource = strSource.replace("$", "");
	strSource = strSource.replace("&", "");
	strSource = strSource.replace("*", "");
	strSource = strSource.replace("~", "");

    QString strMD5;
    char chSalt[1024];

	// 获取随机数
    sprintf(chSalt,"%d",rand());

	// 连接加密文件
    QString strSign=QString("%1%2%3%4").arg(m_strApId).arg(strSource).arg(chSalt).arg(m_strApIdKey);
    
	// 生成md5加密文件
    QByteArray strTemp = QCryptographicHash::hash(strSign.toUtf8(),QCryptographicHash::Md5);
	strMD5.append(strTemp.toHex());

	// 连接上传文本
    QString myurl=QString("http://api.fanyi.baidu/api/trans/vip/translate?"
                   "q=%1&from=%2&to=%3&appid=%4"
                   "&salt=%5&sign=%6").arg(strSource).arg(m_strFrom).arg(m_strTo).arg(m_strApId).arg(chSalt).arg(strMD5);

	// 发送上传
    m_pManager->get(QNetworkRequest(QUrl(myurl)));

	return 1;
}

// 收到翻译结果
int Widget::slotReplyFinished(QNetworkReply *reply)
{
    QJsonParseError jsonError;
    QByteArray all=reply->readAll(); 

    QJsonDocument json = QJsonDocument::fromJson(all, &jsonError);
    QJsonObject object = json.object(); 
	qDebug() << QString::fromStdString(json.toJson().toStdString())<< "\n";
    QString strResult("");
	QString strSource("");
    if(object.contains("error_code"))
    {
        int nResult=object.value("error_code").toInt();;

        switch (nResult) 
		{
        case 52001:strResult = tr("52001 请求超时 重试"); break;
        case 52002:strResult = tr("52002 系统错误 重试");break;
        case 54000:strResult = tr("54000 必填参数为空"); break;
        case 54001:strResult = tr("54001 签名错误"); break;
        case 54003:strResult = tr("54003 速度过快访问频率受限"); break;
        case 54004:strResult = tr("54004 账户余额不足");break;
        case 54005:strResult = tr("54005 请求频繁"); break;
        case 58002:strResult = tr("58002 服务关闭");break;
        default:strResult = tr("其他错误");break;
        }

    }
    else 
	{
        QJsonArray value = object.value("trans_result").toArray();
        QJsonObject object1=value.at(0).toObject();
        strResult=object1.value("dst").toString();
		strSource = object1.value("src").toString();
    }

	if (m_nModelStep == Tran_All)
	{
		auto keys = m_ScrTranHash.keys();
		if (m_nCurrentHashIndex < keys.size())
		{
			m_ScrTranHash[keys.at(m_nCurrentHashIndex)] = strResult;

			auto table = ui->tableWidget;
			table->setItem(m_nCurrentHashIndex, 1, new QTableWidgetItem(strResult));
			table->item(m_nCurrentHashIndex, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
		}
	}
	else if (m_nModelStep == Tran_One)
	{
		auto keys = m_ScrTranHash.keys();
		int nRow = ui->tableWidget->currentRow();
		if (nRow < keys.size() && nRow >= 0)
		{
			m_ScrTranHash[keys.at(nRow)] = strResult;
			auto table = ui->tableWidget;
			table->setItem(nRow, 1, new QTableWidgetItem(strResult));
			table->item(nRow, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
		}
	}


	m_bTranslateStart = false;
    reply->deleteLater();

    return 1;
}

// 打开文件
bool Widget::openXmlFile(QString strFile)
{
	QFile file(strFile);
	if (!file.open(QFile::ReadOnly | QFile::Text))
		return false;

	if (!m_doc.setContent(&file))
	{
		file.close();
		return false;
	}

	file.close();

	return true;
}

// 读取文件
bool Widget::readXmlFile(QString strFile)
{
	if (!openXmlFile(strFile))
		return false;

	//修改保存xml
	QDomElement root = m_doc.documentElement();
	QDomNode firstChild = root.firstChild();
	QDomElement roolElement = m_doc.documentElement();
	QDomNode childNode = roolElement.firstChild();
	QDomNodeList list = m_doc.elementsByTagName(tagMessage); 

	for (int i = 0; i < list.count(); i++)
	{
		QDomElement e = list.at(i).toElement();
		if (e.nodeName() == tagMessage)
		{
			QString strName("");
			QDomNodeList listAllNodes = e.childNodes();
			for (int j = 0; j < listAllNodes.size(); j++)
			{
				if (listAllNodes.at(j).nodeName() == tagSource)
				{
					strName = listAllNodes.at(j).firstChild().nodeValue();
					break;
				}
			}

			if (!strName.isEmpty())
				m_ScrTranHash.insert(strName, "");
		}

	}

	return true;
}

// 保存译文
bool Widget::saveTranslate(QString strFromFile, QString strToFile)
{
	if (!openXmlFile(strFromFile))
		return false;

	QDomElement root = m_doc.documentElement();
	QDomNode firstChild = root.firstChild();
	QDomElement roolElement = m_doc.documentElement();
	QDomNode childNode = roolElement.firstChild();
	QDomNodeList list = m_doc.elementsByTagName(tagMessage); 

	for (int i = 0; i < list.count(); i++)
	{
		QDomElement e = list.at(i).toElement();//map
		if (e.nodeName() == tagMessage)
		{
			QDomNodeList listAllNodes = e.childNodes();
			QString strName("");
			for (int j = 0; j < listAllNodes.size(); j++)
			{
				if (listAllNodes.at(j).nodeName() == tagTranslation)
					e.removeChild(listAllNodes.at(j));
				else if (listAllNodes.at(j).nodeName() == tagSource)
					strName = listAllNodes.at(j).firstChild().nodeValue();
			}

			//增加节点
			if (!strName.isEmpty() && m_ScrTranHash.contains(strName))
			{
				QDomElement child = m_doc.createElement("translation");
				QDomText domText = m_doc.createTextNode(tr("%1").arg(m_ScrTranHash[strName]));
				child.appendChild(domText);
				e.appendChild(child);
			}

		}

	}


	QFile filexml(strToFile);
	if (!filexml.open(QFile::WriteOnly | QFile::Truncate)) {
		qWarning("error::ParserXML->writeOperateXml->file.open\n");
		return false;
	}

	QTextStream ts(&filexml);
	ts.reset();
	ts.setCodec("utf-8");
	m_doc.save(ts, 4, QDomNode::EncodingFromTextStream);
	filexml.close();

	return true;
}

// 打开文件
void Widget::slotOpenFile()
{
	QFileDialog *pFileDialog = new QFileDialog(this);
	m_strOpenFile = pFileDialog->getOpenFileName(this, tr("打开文件"),
		".",
		tr("ts文件 (*.ts)"));

	if (m_strOpenFile.isEmpty())
		return;

	m_ScrTranHash.clear();
	while (ui->tableWidget->rowCount() > 0)
		ui->tableWidget->removeRow(0);

	openXmlFile(m_strOpenFile);
	readXmlFile(m_strOpenFile);

	auto table = ui->tableWidget;
	auto keys = m_ScrTranHash.keys();
	for (size_t i = 0; i < keys.size(); i++)
	{
		int iRow = table->rowCount();
		table->insertRow(iRow);
	
		table->setItem(iRow, 0, new QTableWidgetItem(keys[i]));
		table->item(iRow, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
		table->item(iRow, 0)->setFlags(Qt::ItemIsEditable);

	}
}

// 保存文件
void Widget::slotSaveFile()
{
	QString strFileName = QFileDialog::getSaveFileName(this,
		tr("文件另存"),
		"",
		tr("另存文件(*.ts)"));

	if (strFileName.isEmpty())
		return;

	bool bRet = saveTranslate(m_strOpenFile, strFileName);
	if (bRet)
	{
		QString strDisk = strFileName.left(strFileName.lastIndexOf(":"));
		QString strQm = strFileName;
		QProcess process;
		process.start(strDisk);
		process.waitForFinished();
		QString strCreatQm =QString("lrelease ") + strFileName +QString(" -qm ")+ strQm.replace(".ts",".qm");
		process.start(strCreatQm);
		process.waitForFinished();
	}
}

// 翻译当前
void Widget::slotTranslateCurrent()
{
	m_strApId = ui->lineEditAPID->text();
	m_strApIdKey = ui->lineEditAPID_KEY->text();
	m_bTranslateStart = true;
	m_bTranslateAll = false;
	m_nModelStep = Tran_One;
	m_nOneStep = 0;
	ui->labelTranslate->setText(tr("翻译暂停中"));
}

// 翻译全部
void Widget::slotTranslateAll()
{
	m_strApId = ui->lineEditAPID->text();
	m_strApIdKey = ui->lineEditAPID_KEY->text();
	m_bTranslateStart = true;
	m_bTranslateAll = true;
	m_nModelStep = Tran_All;
	m_nAllStep = 0;
}

// 定时器
void Widget::slotOnTime()
{
	switch (m_nModelStep)
	{

	case Tran_One:
	{
		stepTranslateOne();
	}break;;

	case Tran_All:
	{
		stepTranslateAll();
	}break;
	default:
		break;
	}

}

// 翻译当前步骤
void Widget::stepTranslateOne()
{
	switch (m_nOneStep)
	{
	case 0:
	{
		if (!m_bTranslateAll && m_bTranslateStart)
		{
			auto nRow = ui->tableWidget->currentRow();
			if (nRow >= 0)
			{
				auto pItem = ui->tableWidget->item(nRow, 0);
				if (pItem != nullptr)
				{
					slotUploadData(pItem->text());
					m_nOneStep = 1;
				}
			}
		}
	}break;

	case 1:
	{
		if (!m_bTranslateStart)
		{
			m_nOneStep = 0;
		}
	}break;

	default:
		break;
	}
}

// 翻译所有步骤
void Widget::stepTranslateAll()
{
	static sMyTimeout timeout;
	auto keys = m_ScrTranHash.keys();
	switch (m_nAllStep)
	{
	case 0:
	{
		if (m_bTranslateAll)
		{
			m_bTranslateAll = false;
			m_nAllStep = 1;
			ui->progressBar->setRange(0, m_ScrTranHash.keys().size());
			ui->labelTranslate->setText(tr("正在翻译中"));
		}
	}break;

	case 1:
	{

		if (m_bTranslateStart && m_nCurrentHashIndex < keys.size())
		{
			timeout.start(1500);
			slotUploadData(keys[m_nCurrentHashIndex]);
			ui->progressBar->setValue(m_nCurrentHashIndex);
			m_nAllStep = 2;
		}
		else if (m_nCurrentHashIndex >= keys.size())
		{
			m_nAllStep = 3;
		}

	}break;

	case 2:
	{
		if (!m_bTranslateStart
			&& (m_nCurrentHashIndex < keys.size())
			&& timeout.isTimeOut())
		{
			m_nCurrentHashIndex++;
			m_bTranslateStart = true;
			m_nAllStep = 1;
		}
		else if (!m_bTranslateStart && (m_nCurrentHashIndex >= keys.size()))
		{
			
			m_nAllStep = 3;
		}
	}break;

	case 3:
	{
		m_bTranslateStart = false;
		m_bTranslateAll = false;
		m_nCurrentHashIndex = 0;
		ui->progressBar->setValue(m_ScrTranHash.keys().size());
		ui->labelTranslate->setText(tr("翻译已完成"));
	}break;

	default:
		break;
	}
}






三、程序下载链接

https://download.csdn/download/u013083044/87320815

本文标签: 适用于多语言在线翻译工具QT