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 +189 -14
- package/dist/assets/index-BFAwSDKK.js +342 -0
- package/dist/assets/{index-B40o42zY.css → index-C_QC-zaQ.css} +1 -1
- package/dist/index.html +2 -2
- package/docs/DeepL-Proxy-CLI.md +195 -0
- package/package.json +23 -4
- package/dist/assets/index-CbLzkuRe.js +0 -130
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Translations
|
|
2
2
|
|
|
3
|
-
This plugin integrates with
|
|
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
|

|
|
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. **
|
|
18
|
-
2.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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.
|