diff --git a/providers/deepl/README.md b/providers/deepl/README.md
index 31d9ee59..5e201c20 100644
--- a/providers/deepl/README.md
+++ b/providers/deepl/README.md
@@ -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: [
{
diff --git a/providers/deepl/lib/__tests__/deepl.test.js b/providers/deepl/lib/__tests__/deepl.test.js
index 6a48ca41..a85cda38 100644
--- a/providers/deepl/lib/__tests__/deepl.test.js
+++ b/providers/deepl/lib/__tests__/deepl.test.js
@@ -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,
diff --git a/providers/deepl/lib/index.js b/providers/deepl/lib/index.js
index 7c9012ba..5f669196 100644
--- a/providers/deepl/lib/index.js
+++ b/providers/deepl/lib/index.js
@@ -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
@@ -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 {{
@@ -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 ``
+ })
+ )
+ }
+
+ if (omitPlaceholders) {
+ textArray = textArray.map((text) =>
+ text.replace(/{{(.*?)}}/g, (match) => {
+ placeholderTexts.push(match)
+ return ``
+ })
+ )
+ }
+
const { chunks, reduceFunction } = chunksService.split(textArray, {
maxLength: DEEPL_API_MAX_TEXTS,
maxByteSize: DEEPL_API_ROUGH_MAX_REQUEST_SIZE,
@@ -89,7 +128,9 @@ 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
@@ -97,7 +138,13 @@ module.exports = {
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(
@@ -116,7 +163,19 @@ module.exports = {
})
)
)
-
+
+ if (omitPlaceholders) {
+ result = result.map((text) =>
+ text.replace(//g, (_, id) => placeholderTexts[id])
+ )
+ }
+
+ if (omitTags) {
+ result = result.map((text) =>
+ text.replace(//g, (_, id) => placeholderTags[id])
+ )
+ }
+
if (format === 'jsonb') {
return formatService.htmlToBlock(result)
}
diff --git a/providers/deepl/package.json b/providers/deepl/package.json
index 01791f9a..023c3025 100644
--- a/providers/deepl/package.json
+++ b/providers/deepl/package.json
@@ -1,6 +1,6 @@
{
- "name": "strapi-provider-translate-deepl",
- "version": "1.2.8",
+ "name": "strapi-provider-translate-deepl-lokki",
+ "version": "1.2.9",
"description": "DeepL provider for translate plugin in Strapi 4",
"keywords": [
"strapi",