mtengines
Version:
Machine Translation (MT) library written in TypeScript
353 lines • 14.8 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 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