Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions providers/deepl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ module.exports = {
// use uppercase here!
EN: 'EN-US',
},
// controls if placeholder text inside double curly brackets should be omitted from translation
omitPlaceholders: false,
// Optional: Pass glossaries on translation. The correct glossary for each translation is selected by the target_lang and source_lang properties
glossaries: [
{
Expand Down
21 changes: 21 additions & 0 deletions providers/deepl/lib/__tests__/deepl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,27 @@ describe('deepl provider', () => {
await expect(deeplProvider.usage()).resolves.toBeTruthy()
})

it('omits placeholders when option used', async () => {
server.use(http.post(`${DEEPL_PAID_API}/translate`, translateHandler))

const deeplProvider = provider.init({
apiKey: authKey,
omitPlaceholders: true,
})

// given
const params = {
sourceLocale: 'en',
targetLocale: 'de',
text: 'Some text with a placeholder {{placeholder}} in it and an {{other}} one',
}
// when
const result = await deeplProvider.translate(params)

// then
expect(result).toEqual([params.text])
})

describe('api options used', () => {
const registerHandlerEnforceParams = (
requiredParams,
Expand Down
65 changes: 62 additions & 3 deletions providers/deepl/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ module.exports = {
typeof providerOptions.apiOptions === 'object'
? providerOptions.apiOptions
: {}
const omitPlaceholders = providerOptions.omitPlaceholders || false
const omitTags = providerOptions.omitTags || false
const glossaries =
Array.isArray(providerOptions.glossaries)
? providerOptions.glossaries
Expand All @@ -48,6 +50,22 @@ module.exports = {

const rateLimitedTranslate = limiter.wrap(client.translateText.bind(client))

let availableGlossaries = [];
let lastGlossariesFetch = new Date();

const fetchGlossaries = async () => {
availableGlossaries = await client.listGlossaries();
lastGlossariesFetch = new Date();
}
const findGlossary = providerOptions.findGlossary || ((glossaries, sourceLocale, targetLocale) => {
return glossaries.find((glossary) =>
glossary.sourceLang === sourceLocale && glossary.targetLang === targetLocale
);
});

if (providerOptions.fetchGlossaries)
fetchGlossaries();

return {
/**
* @param {{
Expand Down Expand Up @@ -81,6 +99,27 @@ module.exports = {

let textArray = Array.isArray(input) ? input : [input]

let placeholderTexts = [];
let placeholderTags = [];

if (omitTags) {
textArray = textArray.map((text) =>
text.replace(/<\/?[^>]+(>|$)/g, (match) => {
placeholderTags.push(match)
return `<t id=${placeholderTags.length - 1} />`
})
)
}

if (omitPlaceholders) {
textArray = textArray.map((text) =>
text.replace(/{{(.*?)}}/g, (match) => {
placeholderTexts.push(match)
return `<m id=${placeholderTexts.length - 1} />`
})
)
}

const { chunks, reduceFunction } = chunksService.split(textArray, {
maxLength: DEEPL_API_MAX_TEXTS,
maxByteSize: DEEPL_API_ROUGH_MAX_REQUEST_SIZE,
Expand All @@ -89,15 +128,23 @@ module.exports = {
const parsedSourceLocale = parseLocale(sourceLocale, localeMap, 'source')
const parsedTargetLocale = parseLocale(targetLocale, localeMap, 'target')

const glossary = glossaries.find(
let glossary = undefined;

glossary = glossaries.find(
(g) => g.target_lang === parsedTargetLocale && g.source_lang === parsedSourceLocale
)?.id

if (apiOptions.glossary) {
console.warn('Glossary provided in apiOptions will be ignored and overwritten by the actual glossary that should be used for this translation.')
}

const result = reduceFunction(
if (providerOptions.fetchGlossaries) {
if (new Date() - lastGlossariesFetch > (providerOptions.fetchGlossariesIntervalMs || 3600000))
await fetchGlossaries();
glossary = findGlossary(availableGlossaries, sourceLocale, targetLocale)?.glossaryId || glossary;
}

let result = reduceFunction(
await Promise.all(
chunks.map(async (texts) => {
const result = await rateLimitedTranslate.withOptions(
Expand All @@ -116,7 +163,19 @@ module.exports = {
})
)
)


if (omitPlaceholders) {
result = result.map((text) =>
text.replace(/<m id=(.*?) \/>/g, (_, id) => placeholderTexts[id])
)
}

if (omitTags) {
result = result.map((text) =>
text.replace(/<t id=(.*?) \/>/g, (_, id) => placeholderTags[id])
)
}

if (format === 'jsonb') {
return formatService.htmlToBlock(result)
}
Expand Down
4 changes: 2 additions & 2 deletions providers/deepl/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "strapi-provider-translate-deepl",
"version": "1.2.7",
"name": "strapi-provider-translate-deepl-lokki",
"version": "1.2.9",
"description": "DeepL provider for translate plugin in Strapi 4",
"keywords": [
"strapi",
Expand Down