mtengines
Version:
Machine Translation (MT) library written in TypeScript
311 lines • 12.4 kB
JavaScript
/*******************************************************************************
* Copyright (c) 2023-2026 Maxprograms.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse License 1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-v10.html
*
* Contributors:
* Maxprograms - initial API and implementation
*******************************************************************************/
import { DOMBuilder, SAXParser } from "typesxml";
import { MTMatch } from "./MTMatch.js";
import { MTUtils } from "./MTUtils.js";
export class QwenTranslator {
apiKey;
srcLang = '';
tgtLang = '';
model;
currentRegion = '';
models = {
'Singapore': [
'qwen3.5-plus',
'qwen-mt-plus',
'qwen-mt-flash',
'qwen-mt-lite',
'qwen-mt-turbo'
],
'Virginia': [
'qwen-mt-plus',
'qwen-mt-flash',
'qwen-mt-lite'
],
'Beijing': [
'qwen-mt-plus',
'qwen-mt-flash',
'qwen-mt-lite',
'qwen-mt-turbo'
]
};
regions = {
'Singapore': 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
'Virginia': 'https://dashscope-us.aliyuncs.com/compatible-mode/v1',
'Beijing': 'https://dashscope.aliyuncs.com/compatible-mode/v1'
};
constructor(apiKey, region, model) {
this.apiKey = apiKey;
this.currentRegion = region;
if (model) {
this.model = model;
}
}
getName() {
return 'Qwen Translator';
}
setModel(model) {
this.model = model;
}
getShortName() {
return 'Qwen';
}
getSourceLanguages() {
return MTUtils.getLanguages();
}
getTargetLanguages() {
return MTUtils.getLanguages();
}
setSourceLanguage(lang) {
this.srcLang = lang;
}
getSourceLanguage() {
return this.srcLang;
}
setTargetLanguage(lang) {
this.tgtLang = lang;
}
getTargetLanguage() {
return this.tgtLang;
}
translate(source) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
if (this.srcLang === '' || this.tgtLang === '') {
return Promise.reject(new Error('Source and Target languages must be set before translation.'));
}
let prompt = MTUtils.translatePropmt(source, this.srcLang, this.tgtLang);
return new Promise((resolve, reject) => {
fetch(this.regions[this.currentRegion] + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.apiKey,
},
body: JSON.stringify({
model: this.model,
messages: [
{ role: 'assistant', content: MTUtils.getRole(this.srcLang, this.tgtLang) },
{ role: 'user', content: prompt }
]
}),
}).then((response) => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
return response.json();
}).then((data) => {
let translation = data.choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (source.startsWith('"') && source.endsWith('"')) {
translation = '"' + translation + '"';
}
resolve(translation);
}).catch((error) => {
reject(error);
});
});
}
getMTMatch(source, terms) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.generatePrompt(source, this.srcLang, this.tgtLang, terms);
return new Promise((resolve, reject) => {
fetch(this.regions[this.currentRegion] + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.apiKey,
},
body: JSON.stringify({
model: this.model,
messages: [
{ role: 'assistant', content: MTUtils.getRole(this.srcLang, this.tgtLang) },
{ role: 'user', content: prompt }
]
}),
}).then((response) => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
return response.json();
}).then((data) => {
let translation = data.choices[0].message.content.trim();
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (translation.startsWith('```') && translation.endsWith('```')) {
translation = translation.substring(3, translation.length - 3).trim();
}
if (!translation.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) {
translation = '<target>' + translation + '</target>';
}
let target = MTUtils.toXMLElement(translation);
resolve(new MTMatch(source, target, this.getShortName()));
}).catch((error) => {
reject(error);
});
});
}
handlesTags() {
return true;
}
fixMatch(originalSource, matchSource, matchTarget) {
return new Promise((resolve, reject) => {
this.fixTranslation(originalSource, matchSource, matchTarget).then((translation) => {
let target = MTUtils.toXMLElement(translation);
resolve(new MTMatch(originalSource, target, this.getShortName()));
}).catch((error) => {
reject(error);
});
});
}
fixTranslation(originalSource, matchSource, matchTarget) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.fixMatchPrompt(originalSource, matchSource, matchTarget);
return new Promise((resolve, reject) => {
fetch(this.regions[this.currentRegion] + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.apiKey,
},
body: JSON.stringify({
model: this.model,
messages: [
{ role: 'assistant', content: MTUtils.getRole(this.srcLang, this.tgtLang) },
{ role: 'user', content: prompt }
]
}),
}).then((response) => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
return response.json();
}).then((data) => {
let translation = data.choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (!translation.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) {
translation = '<target>' + translation + '</target>';
}
resolve(translation);
}).catch((error) => {
reject(error);
});
});
}
fixesMatches() {
return true;
}
fixesTags() {
return true;
}
fixTags(source, target) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.fixTagsPrompt(source, target, this.srcLang, this.tgtLang);
return new Promise((resolve, reject) => {
fetch(this.regions[this.currentRegion] + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.apiKey,
},
body: JSON.stringify({
model: this.model,
messages: [
{ role: 'assistant', content: MTUtils.getRole(this.srcLang, this.tgtLang) },
{ role: 'user', content: prompt }
]
}),
}).then((response) => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
return response.json();
}).then((data) => {
let translation = data.choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (!translation.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) {
translation = '<target>' + translation + '</target>';
}
let contentHandler = new DOMBuilder();
let xmlParser = new SAXParser();
xmlParser.setContentHandler(contentHandler);
xmlParser.parseString(translation);
let newDoc = contentHandler.getDocument();
if (newDoc) {
const targetElement = newDoc.getRoot();
if (targetElement) {
resolve(targetElement);
}
else {
reject(new Error('No root element found in fixTags response'));
}
}
else {
reject(new Error('Error parsing XML from fixTags response'));
}
}).catch((error) => {
reject(error);
});
});
}
async getAvailableModels() {
return new Promise((resolve, reject) => {
let modelsForRegion = this.models[this.currentRegion];
if (modelsForRegion) {
let pairs = [];
for (let model of modelsForRegion) {
pairs.push([model, model]);
}
resolve(pairs);
}
else {
reject(new Error('No models available for region: ' + this.currentRegion));
}
});
}
}
//# sourceMappingURL=QwenTranslator.js.map