UNPKG

mtengines

Version:

Machine Translation (MT) library written in TypeScript

353 lines 14.8 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 GeminiTranslator { apiKey; model; srcLang = ''; tgtLang = ''; constructor(apiKey, model) { this.apiKey = apiKey; if (model) { this.model = model; } else { this.model = 'gemini-2.5-flash'; } } setModel(model) { this.model = model; } getName() { return 'Google Gemini'; } getShortName() { return 'Gemini'; } 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 systemInstruction = MTUtils.getRole(this.srcLang, this.tgtLang); let prompt = MTUtils.translatePropmt(source, this.srcLang, this.tgtLang); return new Promise((resolve, reject) => { fetch('https://generativelanguage.googleapis.com/v1beta/models/' + this.model + ':generateContent?key=' + this.apiKey, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ system_instruction: { parts: [{ text: systemInstruction }] }, contents: [ { parts: [{ text: prompt }] } ], generationConfig: { temperature: 0.3, maxOutputTokens: 2048, } }), }).then((response) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }).then((data) => { if (data.candidates && data.candidates.length > 0) { let translation = data.candidates[0].content.parts[0].text.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('"""') && translation.endsWith('"""')) { translation = translation.substring(3, translation.length - 3).trim(); } if (source.startsWith('"') && source.endsWith('"')) { translation = '"' + translation + '"'; } resolve(translation); } else { reject(new Error('No translation returned from Gemini API')); } }).catch((error) => { reject(error); }); }); } getMTMatch(source, terms) { if (!this.model) { return Promise.reject(new Error('Model is not set.')); } let systemInstruction = MTUtils.getRole(this.srcLang, this.tgtLang); let prompt = MTUtils.generatePrompt(source, this.srcLang, this.tgtLang, terms); return new Promise((resolve, reject) => { fetch('https://generativelanguage.googleapis.com/v1beta/models/' + this.model + ':generateContent?key=' + this.apiKey, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ system_instruction: { parts: [{ text: systemInstruction }] }, contents: [ { parts: [{ text: prompt }] } ], generationConfig: { temperature: 0.3, maxOutputTokens: 2048, } }), }).then((response) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }).then((data) => { if (data.candidates && data.candidates.length > 0) { let translation = data.candidates[0].content.parts[0].text.trim(); 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); let space = source.getAttribute('xml:space'); if (space) { target.setAttribute(space); } resolve(new MTMatch(source, target, this.getShortName())); } else { reject(new Error('No translation returned from Gemini API')); } }).catch((error) => { reject(error); }); }); } handlesTags() { return true; } fixesMatches() { 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.')); } if (this.srcLang === '' || this.tgtLang === '') { return Promise.reject(new Error('Source and Target languages must be set before translation.')); } let systemInstruction = MTUtils.getRole(this.srcLang, this.tgtLang); let prompt = MTUtils.fixMatchPrompt(originalSource, matchSource, matchTarget); return new Promise((resolve, reject) => { fetch('https://generativelanguage.googleapis.com/v1beta/models/' + this.model + ':generateContent?key=' + this.apiKey, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ system_instruction: { parts: [{ text: systemInstruction }] }, contents: [ { parts: [{ text: prompt }] } ], generationConfig: { temperature: 0.3, maxOutputTokens: 2048, } }), }).then((response) => { if (!response.ok) { throw new Error('HTTP error! status: ' + response.status); } return response.json(); }).then((data) => { if (data.candidates && data.candidates.length > 0) { let translation = data.candidates[0].content.parts[0].text.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.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) { translation = '<target>' + translation + '</target>'; } resolve(translation); } else { reject(new Error('No translation returned from Gemini API')); } }).catch((error) => { reject(error); }); }); } fixesTags() { return true; } fixTags(source, target) { 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 systemInstruction = MTUtils.getRole(this.srcLang, this.tgtLang); let prompt = MTUtils.fixTagsPrompt(source, target, this.srcLang, this.tgtLang); return new Promise((resolve, reject) => { fetch('https://generativelanguage.googleapis.com/v1beta/models/' + this.model + ':generateContent?key=' + this.apiKey, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ system_instruction: { parts: [{ text: systemInstruction }] }, contents: [ { parts: [{ text: prompt }] } ], generationConfig: { temperature: 0.3, maxOutputTokens: 2048, } }), }).then((response) => { if (!response.ok) { throw new Error('HTTP error! status: ' + response.status); } return response.json(); }).then((data) => { if (data.candidates && data.candidates.length > 0) { let translation = data.candidates[0].content.parts[0].text.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.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')); } } else { reject(new Error('No translation returned from Gemini API')); } }).catch((error) => { reject(error); }); }); } async getAvailableModels() { try { const response = await fetch('https://generativelanguage.googleapis.com/v1beta/models?key=' + this.apiKey, { method: 'GET', }); if (!response.ok) { throw new Error('HTTP error! status: ' + response.status); } const data = await response.json(); if (data.models) { // Filter for models that support generateContent return data.models .filter((model) => model.supportedGenerationMethods && model.supportedGenerationMethods.includes('generateContent')) .map((model) => [ model.name.replace('models/', ''), model.displayName || model.name.replace('models/', '') ]); } return []; } catch (error) { console.error('Error fetching available models:', error); throw error; } } } //# sourceMappingURL=GeminiTranslator.js.map