diff --git a/package-lock.json b/package-lock.json index 115cfcc1..58eb3364 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32401,7 +32401,7 @@ }, "plugin": { "name": "strapi-plugin-translate", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "dependencies": { "@strapi/design-system": "^2.0.0-rc.11", @@ -32485,7 +32485,7 @@ }, "providers/deepl": { "name": "strapi-provider-translate-deepl", - "version": "1.2.0", + "version": "1.2.2", "license": "MIT", "dependencies": { "bottleneck": "^2.19.5", @@ -32506,7 +32506,7 @@ }, "peerDependencies": { "lodash": "*", - "strapi-plugin-translate": "*" + "strapi-plugin-translate": "1.3.1" } }, "providers/deepl/node_modules/msw": { @@ -32583,7 +32583,7 @@ }, "providers/libretranslate": { "name": "strapi-provider-translate-libretranslate", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "dependencies": { "axios": ">=0.21.2 <1.2.0 || >=1.2.1", @@ -32605,7 +32605,7 @@ }, "peerDependencies": { "lodash": "*", - "strapi-plugin-translate": "*" + "strapi-plugin-translate": "1.3.1" } }, "providers/libretranslate/node_modules/axios": { diff --git a/plugin/package.json b/plugin/package.json index 10381804..93c3f439 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -1,6 +1,6 @@ { "name": "strapi-plugin-translate", - "version": "2.0.0-next.1", + "version": "1.3.1", "description": "Strapi plugin for managing and automating translation of content", "keywords": [ "strapi", diff --git a/plugin/server/src/config/index.ts b/plugin/server/src/config/index.ts index b02c5dca..a16d1ff2 100644 --- a/plugin/server/src/config/index.ts +++ b/plugin/server/src/config/index.ts @@ -22,6 +22,7 @@ export default { translatedFieldTypes: [ { type: 'string', format: 'plain' }, { type: 'text', format: 'plain' }, + { type: 'blocks', format: 'jsonb' }, { type: 'richtext', format: 'markdown' }, 'component', 'dynamiczone', diff --git a/providers/deepl/README.md b/providers/deepl/README.md index d958809e..31d9ee59 100644 --- a/providers/deepl/README.md +++ b/providers/deepl/README.md @@ -21,9 +21,17 @@ module.exports = { // use uppercase here! EN: 'EN-US', }, + // Optional: Pass glossaries on translation. The correct glossary for each translation is selected by the target_lang and source_lang properties + glossaries: [ + { + id: "your-glossary-id", + target_lang: "DE", + source_lang: "EN", + } + ], apiOptions: { // see for supported options. - // note that tagHandling Mode cannot be set this way. + // note that tagHandling Mode and glossary cannot be set this way. // use with caution, as non-default values may break translation of markdown formality: 'default', // ... diff --git a/providers/deepl/package.json b/providers/deepl/package.json index 7a8fc859..2b2115c0 100644 --- a/providers/deepl/package.json +++ b/providers/deepl/package.json @@ -1,6 +1,6 @@ { "name": "strapi-provider-translate-deepl", - "version": "1.3.0-next.1", + "version": "1.2.2", "description": "DeepL provider for translate plugin in Strapi 4", "keywords": [ "strapi", @@ -66,7 +66,7 @@ }, "peerDependencies": { "lodash": "*", - "strapi-plugin-translate": "2.0.0-next.1" + "strapi-plugin-translate": "1.3.1" }, "engines": { "node": ">=18 <=22", diff --git a/providers/deepl/src/lib/__tests__/deepl.test.ts b/providers/deepl/src/lib/__tests__/deepl.test.ts index 650e9bb7..d6406e84 100644 --- a/providers/deepl/src/lib/__tests__/deepl.test.ts +++ b/providers/deepl/src/lib/__tests__/deepl.test.ts @@ -540,4 +540,112 @@ describe('deepl provider', () => { }) }) }) + + describe('glossaries', () => { + const glossaryHandler: HttpResponseResolver< + PathParams, + DefaultBodyType, + undefined + > = async ({ request }) => { + const body = await request.text() + const params = new URLSearchParams(body) + if (isAuthenticated(request)) { + let text = params.getAll('text') + if (text.length == 0) { + return new HttpResponse(null, { status: 400 }) + } + if (text.length > 50) { + return new HttpResponse(null, { status: 413 }) + } + let targetLang = params.get('target_lang') + if (!targetLang) { + return new HttpResponse(null, { status: 400 }) + } + let glossary = params.get('glossary_id') + if (glossary) { + return HttpResponse.json({ + translations: text.map((t) => ({ + detected_source_language: 'EN', + text: `${t} (glossary: ${glossary})`, + })), + }) + } + return HttpResponse.json({ + translations: text.map((t) => ({ + detected_source_language: 'EN', + text: t, + })), + }) + } + return new HttpResponse(null, { status: 403 }) + } + + beforeEach(() => { + server.use( + http.post(`${DEEPL_FREE_API}/translate`, glossaryHandler), + http.post(`${DEEPL_PAID_API}/translate`, glossaryHandler) + ) + }) + + it('uses the correct glossary for the given locale pair', async () => { + const deeplProvider = provider.init({ + apiKey: authKey, + glossaries: [ + { + id: 'glossary1', + source_lang: 'EN', + target_lang: 'DE', + }, + { + id: 'glossary2', + source_lang: 'EN', + target_lang: 'FR', + }, + ], + }) + + const params = { + sourceLocale: 'en', + targetLocale: 'de', + text: 'Some text', + } + + const result = await deeplProvider.translate(params) + + expect(result).toEqual(['Some text (glossary: glossary1)']) + }) + + it('ignores glossary in apiOptions and logs a warning', async () => { + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation() + + const deeplProvider = provider.init({ + apiKey: authKey, + glossaries: [ + { + id: 'glossary1', + source_lang: 'EN', + target_lang: 'DE', + }, + ], + apiOptions: { + glossary: 'someOtherGlossary', + }, + }) + + const params = { + sourceLocale: 'en', + targetLocale: 'de', + text: 'Some text', + } + + const result = await deeplProvider.translate(params) + + expect(result).toEqual(['Some text (glossary: glossary1)']) + expect(consoleWarnSpy).toHaveBeenCalledWith( + 'Glossary provided in apiOptions will be ignored and overwritten by the actual glossary that should be used for this translation.' + ) + + consoleWarnSpy.mockRestore() + }) + }) }) diff --git a/providers/deepl/src/lib/index.ts b/providers/deepl/src/lib/index.ts index 36faa74f..07435faa 100644 --- a/providers/deepl/src/lib/index.ts +++ b/providers/deepl/src/lib/index.ts @@ -14,6 +14,7 @@ import { getService } from './get-service' export type DeepLProviderOptions = { apiKey?: string apiUrl?: string + glossaries?: { id: string; source_lang: string; target_lang: string }[] localeMap?: Record apiOptions?: Record } @@ -33,6 +34,10 @@ export default { typeof providerOptions.apiOptions === 'object' ? providerOptions.apiOptions : {} + const glossaries = + Array.isArray(providerOptions.glossaries) + ? providerOptions.glossaries + : [] const client = new Translator(apiKey, { serverUrl: apiUrl, @@ -93,6 +98,17 @@ export default { maxByteSize: DEEPL_API_ROUGH_MAX_REQUEST_SIZE, }) + const parsedSourceLocale = parseLocale(sourceLocale, localeMap, 'source') + const parsedTargetLocale = parseLocale(targetLocale, localeMap, 'target') + + const 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( await Promise.all( chunks.map(async (texts) => { @@ -104,17 +120,9 @@ export default { : DEEPL_PRIORITY_DEFAULT, }, texts, - parseLocale( - sourceLocale, - localeMap, - 'source' - ) as SourceLanguageCode, - parseLocale( - targetLocale, - localeMap, - 'target' - ) as TargetLanguageCode, - { ...apiOptions, tagHandling } + parsedSourceLocale as SourceLanguageCode, + parsedTargetLocale as TargetLanguageCode, + { ...apiOptions, tagHandling, glossary } ) return Array.isArray(result) ? result.map((value) => value.text) diff --git a/providers/libretranslate/package.json b/providers/libretranslate/package.json index 8f58cb54..e50780be 100644 --- a/providers/libretranslate/package.json +++ b/providers/libretranslate/package.json @@ -1,6 +1,6 @@ { "name": "strapi-provider-translate-libretranslate", - "version": "1.2.0-next.1", + "version": "1.1.1", "description": "Libretranslate provider for translate plugin in Strapi 4", "keywords": [ "strapi", @@ -67,7 +67,7 @@ }, "peerDependencies": { "lodash": "*", - "strapi-plugin-translate": "2.0.0-next.1" + "strapi-plugin-translate": "1.3.1" }, "engines": { "node": ">=18 <=22",