UNPKG

strapi-provider-translate-libretranslate

Version:

Libretranslate provider for translate plugin in Strapi 4

126 lines (105 loc) 3.75 kB
'use strict' const Bottleneck = require('bottleneck/es5') const { LT_PRIORITY_DEFAULT } = require('./constants') const { Client } = require('./client') const { getService } = require('./get-service') /** * Module dependencies */ module.exports = { provider: 'libretranslate', name: 'LibreTranslate', init(providerOptions = {}) { const apiKey = process.env.LT_API_KEY || providerOptions.apiKey const apiUrl = process.env.LT_API_URL || providerOptions.apiUrl if (!apiUrl) throw new Error('You must provide a URL to the LibreTranslate API') const localeMap = typeof providerOptions.localeMap === 'object' ? providerOptions.localeMap : {} const client = new Client(apiUrl, apiKey) const maxCharacters = Number(process.env.LT_API_MAX_CHARS) || providerOptions.apiMaxChars || -1 const maxRequestsPerMinute = Number(process.env.LT_API_MAX_RPM) || providerOptions.apiMaxRPM || -1 let minTime = 0 if (process.env.NODE_ENV === 'test') minTime = 10 if (maxRequestsPerMinute !== -1) minTime = 1000 / (maxRequestsPerMinute / 60) const limiter = new Bottleneck({ minTime, maxConcurrent: 1, }) const rateLimitedTranslate = limiter.wrap(client.translateText.bind(client)) const maxTexts = Number(process.env.LT_API_MAX_TEXTS) || providerOptions.apiMaxTexts || -1 return { /** * @param {{ * text:string|string[]|any[], * sourceLocale: string, * targetLocale: string, * priority: number, * format?: 'plain'|'markdown'|'html' * }} options all translate options * @returns {string[]} the input text(s) translated */ async translate({ text, priority, sourceLocale, targetLocale, format }) { if (!text) { return [] } if (!sourceLocale || !targetLocale) { throw new Error('source and target locale must be defined') } const { source, target } = client.parseLocales( localeMap[sourceLocale] ?? sourceLocale, localeMap[targetLocale] ?? targetLocale ) const chunksService = getService('chunks') const formatService = getService('format') let input = text if (format === 'jsonb') { input = await formatService.blockToHtml(input) } else if (format === 'markdown') { input = formatService.markdownToHtml(input) } const textArray = Array.isArray(input) ? input : [input] const { chunks, reduceFunction } = chunksService.split(textArray, { maxLength: maxTexts === -1 ? Number.MAX_VALUE : maxTexts, maxByteSize: maxCharacters === -1 ? Number.MAX_VALUE : maxCharacters, }) const result = reduceFunction( await Promise.all( chunks.map(async (texts) => { const result = await rateLimitedTranslate.withOptions( { priority: typeof priority === 'number' ? priority : LT_PRIORITY_DEFAULT, }, texts, source, target, ['html', 'markdown'].includes(format) ? 'html' : 'text' ) return result }) ) ) if (format === 'jsonb') { return formatService.htmlToBlock(result) } if (format === 'markdown') { return formatService.htmlToMarkdown(result) } return result }, async usage() { // afaik LibreTranslate has no concept of usage, apart from rate limiting return undefined }, } }, }