UNPKG

mtengines

Version:

Machine Translation (MT) library written in TypeScript

311 lines 12.4 kB
/******************************************************************************* * 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