datocms-plugin-ai-translations 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -159,7 +159,11 @@ export default {
159
159
  };
160
160
  if (req.method === 'OPTIONS') return new Response(null, { headers: cors });
161
161
 
162
- const upstream = new URL('/v2/translate', env.DEEPL_BASE_URL || 'https://api.deepl.com');
162
+ const url = new URL(req.url);
163
+ const isFree = url.searchParams.get('endpoint') === 'free';
164
+ const baseUrl = isFree ? 'https://api-free.deepl.com' : 'https://api.deepl.com';
165
+ const upstream = new URL('/v2/translate', baseUrl);
166
+
163
167
  const body = await req.text(); // passthrough JSON body
164
168
 
165
169
  const resp = await fetch(upstream, {
@@ -195,7 +199,10 @@ export default async function handler(req, res) {
195
199
  res.setHeader('Access-Control-Allow-Headers', '*');
196
200
  if (req.method === 'OPTIONS') return res.status(204).end();
197
201
 
198
- const upstream = `${process.env.DEEPL_BASE_URL || 'https://api.deepl.com'}/v2/translate`;
202
+ const isFree = req.query.endpoint === 'free';
203
+ const baseUrl = isFree ? 'https://api-free.deepl.com' : 'https://api.deepl.com';
204
+ const upstream = `${baseUrl}/v2/translate`;
205
+
199
206
  const r = await fetch(upstream, {
200
207
  method: 'POST',
201
208
  headers: {
@@ -229,7 +236,11 @@ export const handler = async (event) => {
229
236
  if (event.httpMethod === 'OPTIONS') {
230
237
  return { statusCode: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*' } };
231
238
  }
232
- const upstream = `${process.env.DEEPL_BASE_URL || 'https://api.deepl.com'}/v2/translate`;
239
+
240
+ const isFree = event.queryStringParameters?.endpoint === 'free';
241
+ const baseUrl = isFree ? 'https://api-free.deepl.com' : 'https://api.deepl.com';
242
+ const upstream = `${baseUrl}/v2/translate`;
243
+
233
244
  const r = await fetch(upstream, {
234
245
  method: 'POST',
235
246
  headers: {
@@ -267,8 +278,95 @@ Endpoint selection (Free vs Pro)
267
278
 
268
279
  That’s it — once your proxy passes the test, DeepL translations (including large Structured Text fields) will work end‑to‑end.
269
280
 
270
- See also
271
- - CLI step‑by‑steps for deploying a proxy (Cloudflare, Vercel, Netlify): docs/DeepL-Proxy-CLI.md
281
+
282
+
283
+ ## DeepL Glossaries
284
+
285
+ The plugin supports DeepL glossaries to enforce preferred terminology. You can set a default glossary ID and/or map specific language pairs to specific glossary IDs. This works for all field types, including Structured Text.
286
+
287
+ ### Requirements
288
+
289
+ - A DeepL API key with access to Glossaries. Check your DeepL account/plan capabilities.
290
+ - The same proxy described above; translations with a glossary still call `POST <proxy>/v2/translate` with an extra `glossary_id` in the JSON body.
291
+
292
+ ### Configure in the Plugin
293
+
294
+ 1. Open Settings → vendor “DeepL”.
295
+ 2. Set “Proxy URL” and verify it via “Test proxy”.
296
+ 3. Expand “Advanced settings”.
297
+ 4. Optional: set “Default glossary ID” (e.g., `gls-abc123`).
298
+ 5. Optional: fill in “Glossaries by language pair” with one mapping per line.
299
+
300
+ You can use either DatoCMS locales (e.g., `en-US`, `pt-BR`) or DeepL codes (e.g., `EN`, `PT-BR`). The plugin normalizes both to DeepL codes internally.
301
+
302
+ ### Mapping Syntax
303
+
304
+ One entry per line. Supported forms:
305
+
306
+ ```
307
+ EN->DE=gls-abc123
308
+ en-US->pt-BR=gls-xyz789
309
+ fr→it gls-123 # alt arrow and delimiter
310
+ *->pt-BR=gls-777 # wildcard: any source to target
311
+ EN->*=gls-555 # wildcard: source to any target
312
+ pt-BR=gls-777 # shorthand for *->pt-BR
313
+ ```
314
+
315
+ Delimiters: `=`, `:`, or whitespace. Arrows: `->`, `→`, `⇒` (all treated the same). Case is ignored.
316
+
317
+ ### Resolution Order
318
+
319
+ When translating from `fromLocale` → `toLocale`, the plugin picks a glossary ID using this precedence:
320
+
321
+ 1. Exact pair match by DeepL codes (e.g., `EN:PT-BR`).
322
+ 2. Exact pair match by your raw locales (e.g., `en-US:pt-BR`).
323
+ 3. Wildcard any→target (e.g., `*:PT-BR` or `*:pt-BR`).
324
+ 4. Wildcard source→any (e.g., `EN:*` or `en-US:*`).
325
+ 5. Default glossary ID (if set).
326
+ 6. Otherwise, no glossary is used.
327
+
328
+ If DeepL returns a glossary mismatch (e.g., glossary languages don’t match the current pair) or a missing glossary, the plugin automatically retries the same request once without a glossary so your translation continues. A brief hint is surfaced in the UI logs.
329
+
330
+ ### Finding or Creating a Glossary ID
331
+
332
+ The plugin only needs the `glossary_id` string. You can create and list glossaries with the DeepL API from your own machine or server. Examples with cURL:
333
+
334
+ List glossaries
335
+ ```
336
+ curl -H "Authorization: DeepL-Auth-Key $DEEPL_AUTH_KEY" \
337
+ https://api.deepl.com/v2/glossaries
338
+ ```
339
+
340
+ Create a small glossary inline (tab-separated entries)
341
+ ```
342
+ curl -X POST https://api.deepl.com/v2/glossaries \
343
+ -H "Authorization: DeepL-Auth-Key $DEEPL_AUTH_KEY" \
344
+ -H "Content-Type: application/json" \
345
+ -d '{
346
+ "name": "Marketing-EN-DE",
347
+ "source_lang": "EN",
348
+ "target_lang": "DE",
349
+ "entries_format": "tsv",
350
+ "entries": "CTA\tCall-to-Action\nlead magnet\tLeadmagnet"
351
+ }'
352
+ ```
353
+
354
+ Note: If your account uses the Free endpoint, replace the host with `https://api-free.deepl.com`.
355
+
356
+ You do not need to expose `/v2/glossaries` through your proxy for the plugin to work — it only calls `/v2/translate`. Manage glossaries from your server/CLI, then paste the resulting IDs into the plugin settings.
357
+
358
+ ### Tips and Limitations
359
+
360
+ - Glossaries apply only to the DeepL vendor. OpenAI/Gemini/Anthropic do not use glossaries.
361
+ - The plugin preserves placeholders and HTML tags automatically (`notranslate`, `ph`, etc.). Glossaries will not alter those tokens.
362
+ - If you use DeepL “formality”, it is sent only for targets that support it; otherwise omitted.
363
+ - A wrong Pro/Free endpoint for your key will still raise the “Wrong endpoint” hint shown in settings and translation errors.
364
+
365
+ ### Quick Sanity Test
366
+
367
+ 1. Create a small EN→DE glossary with an obvious term (e.g., “CTA” → “Call‑to‑Action”).
368
+ 2. In Settings → DeepL, paste the glossary ID into either Default or the `EN->DE=...` mapping.
369
+ 3. Translate a field from EN to DE containing “CTA”. The resulting German text should include your glossary translation.
272
370
 
273
371
  ## Migration Notes
274
372