datocms-plugin-ai-translations 1.9.2 → 2.0.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
@@ -1,6 +1,6 @@
1
1
  # AI Translations
2
2
 
3
- This plugin integrates with the OpenAI API and provides on-demand AI-powered translations for your fields. You can also translate entire records or perform bulk translations across multiple records and models.
3
+ This plugin integrates with AI providers and provides on-demand AI-powered translations for your fields. You can also translate entire records or perform bulk translations across multiple records and models.
4
4
 
5
5
  ![47659](https://github.com/user-attachments/assets/2aae06c5-d2fb-404d-ae76-08b5ebd55759)
6
6
 
@@ -14,18 +14,32 @@ See the [CHANGELOG.md](./CHANGELOG.md) file for details about all the latest fea
14
14
 
15
15
  On the plugin's Settings screen:
16
16
 
17
- 1. **OpenAI API Key**: Paste a valid OpenAI API key. The plugin uses this key for translation requests.
18
- 2. **GPT Model**: Select one of the available GPT-based models. Only GPT-4.1 and GPT-4o variants are permitted.
19
- - mini: Balanced cost (~$0.02/$0.04) & quality.
20
- - nano: Fastest & cheapest (~$0.01/$0.02), lower nuance.
21
- - normal: Highest fidelity (~$0.03/$0.06), higher latency & cost.
22
- 3. **Translatable Field Types**: Pick which field editor types (single_line, markdown, structured_text, etc.) can be translated.
23
- 4. **Translate Whole Record**: Decide if you want the sidebar feature that allows users to translate every localized field in the record at once.
24
- 5. **Translate Bulk Records**: Decide if you want the bulk translation feature that allows users to translate multiple records at once on the table view.
25
- 6. **AI Bulk Translations Page**: Translate whole models at once.
26
- 7. **Prompt Template**: Customize how translations are requested. The plugin uses placeholders like `{fieldValue}`, `{fromLocale}`, `{toLocale}`, and `{recordContext}`.
27
-
28
- _**Models**_: To ensure optimal translation quality, latency, and cost predictability, only GPT-4.1 and GPT-4o families are supported. These models balance fidelity, speed, and price for translation workflows.
17
+ 1. **AI Vendor**: Choose your provider OpenAI (ChatGPT), Google (Gemini), or Anthropic (Claude).
18
+ 2. If you chose OpenAI:
19
+ - **OpenAI API Key**: Paste a valid OpenAI key.
20
+ - **GPT Model**: After entering your key, the plugin lists relevant chat models and highlights a recommended default.
21
+ - Default: gpt‑4.1‑mini (fastest and broadly available)
22
+ - High‑stakes short copy: gpt‑4.1
23
+ - Large or budget batches: gpt‑4o‑mini
24
+ 3. If you chose Google (Gemini):
25
+ - **Google API Key**: Paste a valid key from a GCP project with the Generative Language API enabled.
26
+ - **Gemini Model**: Recommended `gemini-2.5-flash` (fast/cost‑effective default). For the highest fidelity, use `gemini-2.5-pro`. For very large or budget batches, consider `gemini-2.5-flash-lite`.
27
+ 4. **Translatable Field Types**: Pick which field editor types (single_line, markdown, structured_text, etc.) can be translated.
28
+ 5. **Translate Whole Record**: Enable the sidebar that translates every localized field in a record.
29
+ 6. **Translate Bulk Records**: Enable bulk translations from table view or via the dedicated page.
30
+ 7. **AI Bulk Translations Page**: Translate whole models at once.
31
+ 8. **Prompt Template**: Customize how translations are requested. Use `{fieldValue}`, `{fromLocale}`, `{toLocale}`, and `{recordContext}`.
32
+
33
+ ### Key Restrictions and Security
34
+ - Keys are stored in plugin settings and used client‑side. Do not share your workspace publicly.
35
+ - Prefer restricting keys:
36
+ - OpenAI: regular secret key; rotate periodically.
37
+ - Google: restrict by HTTP referrer and enable only the Generative Language API.
38
+ - The plugin redacts API keys from debug logs automatically.
39
+
40
+ _**Models**_
41
+ - OpenAI: the list is dynamic for your account; the plugin filters out embeddings, audio/whisper/tts, moderation, image, and realtime models, prioritizing general-purpose chat models used for translation.
42
+ - Google: provides a fixed list in settings (`gemini-1.5-flash` and `gemini-1.5-pro`).
29
43
 
30
44
  Save your changes. The plugin is now ready.
31
45
 
@@ -100,9 +114,170 @@ You can customize the translation prompt template in the plugin settings:
100
114
 
101
115
  ## Troubleshooting
102
116
 
103
- - **Invalid API Key**: Ensure your OpenAI API key is correct and has sufficient usage limits.
117
+ - **Invalid API Key**: Ensure your key matches the selected vendor and has access.
118
+ - **Rate Limit/Quota**: Reduce concurrency/batch size, switch to a lighter model, or increase your vendor quota.
119
+ - **Model Not Found**: Verify the exact model id exists for your account/region and is spelled correctly.
104
120
  - **Localization**: Make sure your project has at least two locales, otherwise translation actions won't appear.
105
121
 
122
+ ## DeepL Requires a Proxy (Why and How)
123
+
124
+ DeepL’s API does not support browser‑origin requests (no CORS). If you call DeepL directly from the plugin (which runs in the browser), the preflight request fails and you’ll see network/CORS errors. To use DeepL in this plugin, you must route requests through a small server you control (a “proxy”).
125
+
126
+ What the proxy must do
127
+ - Accept a POST with JSON body that matches DeepL’s `/v2/translate` input. The plugin sends bodies like:
128
+ - `{ "text": ["Hello"], "target_lang": "DE", ... }`
129
+ - Add CORS headers to the response (at minimum `Access-Control-Allow-Origin: *` for testing; you can restrict later).
130
+ - Forward the request to DeepL’s API with the Authorization header added server‑side:
131
+ - `Authorization: DeepL-Auth-Key <YOUR_KEY>`
132
+ - Choose the proper upstream host:
133
+ - Free keys (end with `:fx`) → `https://api-free.deepl.com`
134
+ - Pro keys → `https://api.deepl.com`
135
+
136
+ Plugin settings to use with a proxy
137
+ - In Settings → DeepL, set “Proxy URL” to the base of your function (examples below) — the plugin will call `POST <proxy>/v2/translate`.
138
+ - If your key ends with `:fx`, also enable “Use DeepL Free endpoint (api-free.deepl.com)” so validation and error messages match your plan.
139
+ - Use the “Test proxy” button: we send a tiny "Hello world" test, and show inline success/error feedback.
140
+
141
+ Security notes
142
+ - Never expose the DeepL key in the browser. Keep it in your serverless function/env.
143
+ - For production, restrict CORS to `https://admin.datocms.com` and your own preview domains instead of `*`.
144
+ - Do not log request bodies or headers; avoid leaving keys in logs.
145
+
146
+ ### Option A: Cloudflare Workers
147
+
148
+ Environment vars
149
+ - `DEEPL_AUTH_KEY` — your DeepL key
150
+ - `DEEPL_BASE_URL` — `https://api-free.deepl.com` (Free) or `https://api.deepl.com` (Pro)
151
+
152
+ Worker (wrangler.toml configured; minimal example)
153
+ ```
154
+ export default {
155
+ async fetch(req, env) {
156
+ const cors = {
157
+ 'Access-Control-Allow-Origin': '*',
158
+ 'Access-Control-Allow-Headers': '*',
159
+ };
160
+ if (req.method === 'OPTIONS') return new Response(null, { headers: cors });
161
+
162
+ const upstream = new URL('/v2/translate', env.DEEPL_BASE_URL || 'https://api.deepl.com');
163
+ const body = await req.text(); // passthrough JSON body
164
+
165
+ const resp = await fetch(upstream, {
166
+ method: 'POST',
167
+ headers: {
168
+ 'Content-Type': 'application/json',
169
+ 'Authorization': `DeepL-Auth-Key ${env.DEEPL_AUTH_KEY}`,
170
+ },
171
+ body,
172
+ });
173
+
174
+ const text = await resp.text();
175
+ return new Response(text, { status: resp.status, headers: { ...cors, 'Content-Type': 'application/json' } });
176
+ }
177
+ }
178
+ ```
179
+
180
+ Deploy
181
+ - `wrangler deploy`
182
+ - Set `DEEPL_AUTH_KEY` and `DEEPL_BASE_URL` in your Worker’s environment.
183
+ - Copy the Worker URL (e.g., `https://your-worker.yourname.workers.dev`) into the plugin’s “Proxy URL”.
184
+
185
+ ### Option B: Vercel Serverless Function (Next.js API Route)
186
+
187
+ Environment vars (Project Settings → Environment Variables):
188
+ - `DEEPL_AUTH_KEY` — your key
189
+ - `DEEPL_BASE_URL` — `https://api-free.deepl.com` or `https://api.deepl.com`
190
+
191
+ Create `pages/api/deepl.ts` (or `app/api/deepl/route.ts` for App Router):
192
+ ```
193
+ export default async function handler(req, res) {
194
+ res.setHeader('Access-Control-Allow-Origin', '*');
195
+ res.setHeader('Access-Control-Allow-Headers', '*');
196
+ if (req.method === 'OPTIONS') return res.status(204).end();
197
+
198
+ const upstream = `${process.env.DEEPL_BASE_URL || 'https://api.deepl.com'}/v2/translate`;
199
+ const r = await fetch(upstream, {
200
+ method: 'POST',
201
+ headers: {
202
+ 'Content-Type': 'application/json',
203
+ 'Authorization': `DeepL-Auth-Key ${process.env.DEEPL_AUTH_KEY}`,
204
+ },
205
+ body: JSON.stringify(req.body ?? {}),
206
+ });
207
+ const text = await r.text();
208
+ res.status(r.status).setHeader('Content-Type', 'application/json').send(text);
209
+ }
210
+ ```
211
+
212
+ Deploy
213
+ - `vercel deploy` (or push to GitHub with Vercel connected).
214
+ - Set env vars, redeploy, then use `https://your-app.vercel.app/api/deepl` as the “Proxy URL”.
215
+
216
+ Note on Vercel Deployment Protection
217
+ - If your Vercel organization enforces Deployment Protection, unauthenticated public requests return 401.
218
+ - Go to Vercel → Project → Settings → Deployment Protection and either disable protection for this project or add a public bypass so DatoCMS can call the endpoint.
219
+ - After changing this setting, the proxy should be reachable from the plugin’s “Test proxy” and during translations.
220
+
221
+ ### Option C: Netlify Functions
222
+
223
+ Environment vars (Netlify dashboard → Site settings → Environment):
224
+ - `DEEPL_AUTH_KEY`, `DEEPL_BASE_URL`
225
+
226
+ Create `netlify/functions/deepl-proxy.ts`:
227
+ ```
228
+ export const handler = async (event) => {
229
+ if (event.httpMethod === 'OPTIONS') {
230
+ return { statusCode: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*' } };
231
+ }
232
+ const upstream = `${process.env.DEEPL_BASE_URL || 'https://api.deepl.com'}/v2/translate`;
233
+ const r = await fetch(upstream, {
234
+ method: 'POST',
235
+ headers: {
236
+ 'Content-Type': 'application/json',
237
+ 'Authorization': `DeepL-Auth-Key ${process.env.DEEPL_AUTH_KEY}`,
238
+ },
239
+ body: event.body || '{}',
240
+ });
241
+ const text = await r.text();
242
+ return {
243
+ statusCode: r.status,
244
+ headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json' },
245
+ body: text,
246
+ };
247
+ };
248
+ ```
249
+
250
+ Deploy
251
+ - `netlify deploy` (or via the Netlify app/CLI). Use `/.netlify/functions/deepl-proxy` as your “Proxy URL”.
252
+
253
+ ### Testing and common errors
254
+
255
+ - Use the “Test proxy” button in plugin settings to verify connectivity.
256
+ - Wrong endpoint for key (403 / “Wrong endpoint”):
257
+ - Free key (`…:fx`) must target `api-free.deepl.com`. Pro keys must target `api.deepl.com`.
258
+ - Fix by setting `DEEPL_BASE_URL` accordingly (and/or toggle “Use DeepL Free endpoint” in the plugin).
259
+ - CORS errors: ensure your proxy responds to OPTIONS and includes `Access-Control-Allow-Origin`.
260
+ - 414/URI too long: means you’re not POSTing a body through your proxy. The examples above use POST and won’t hit this.
261
+ - 429/Rate limit: lower concurrency or try smaller batches; upgrade plan if needed.
262
+
263
+ Endpoint selection (Free vs Pro)
264
+ - The example proxies choose the upstream via an environment variable `DEEPL_BASE_URL`.
265
+ - Set `DEEPL_BASE_URL` to `https://api-free.deepl.com` if your key ends with `:fx` (DeepL Free), otherwise to `https://api.deepl.com` (Pro).
266
+ - In the plugin settings, the toggle “Use DeepL Free endpoint (api-free.deepl.com)” should match the proxy’s `DEEPL_BASE_URL` so errors and validations are consistent. A mismatch will surface as a “Wrong endpoint for your API key” error.
267
+
268
+ That’s it — once your proxy passes the test, DeepL translations (including large Structured Text fields) will work end‑to‑end.
269
+
270
+ See also
271
+ - CLI step‑by‑steps for deploying a proxy (Cloudflare, Vercel, Netlify): docs/DeepL-Proxy-CLI.md
272
+
273
+ ## Migration Notes
274
+
275
+ - Existing installations continue to work with OpenAI by default; your current `apiKey` and `gptModel` remain valid.
276
+ - To use Google (Gemini):
277
+ 1. In Google Cloud, enable the Generative Language API for your project.
278
+ 2. Create an API key and restrict it by HTTP referrer if possible.
279
+ 3. In the plugin settings, switch vendor to Google (Gemini), paste the key, and select a Gemini model.
280
+
106
281
  ## License
107
282
 
108
283
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.