@braze/sanity-plugin-ai-translation 0.2.4 → 0.2.6
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 +385 -220
- package/dist/index.d.ts +52 -4
- package/dist/index.js +1670 -1543
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,334 +1,499 @@
|
|
|
1
1
|
# @braze/sanity-plugin-ai-translation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Sanity Studio plugin that adds an AI-powered **Translate** document action for internationalized content.
|
|
4
|
+
|
|
5
|
+
- **Default provider:** Sanity Agent Actions (`client.agent.action.translate()`)
|
|
6
|
+
- **Optional provider:** OpenAI GPT-5 (direct API) for large documents / long style guides
|
|
7
|
+
- **Output:** Translations are created as **versioned documents inside Content Releases** (staged publishing)
|
|
8
|
+
- **Extras:** translate referenced documents, glossary terms, protected phrases, debug logging, field exclusion, and field transforms (Portable Text + captions)
|
|
9
|
+
|
|
10
|
+
**For editors:** see the standalone user guide at `docs/user-guide.md`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of contents
|
|
15
|
+
|
|
16
|
+
- [What this plugin does](#what-this-plugin-does)
|
|
17
|
+
- [Prerequisites](#prerequisites)
|
|
18
|
+
- [Install](#install)
|
|
19
|
+
- [Configure (Studio)](#configure-studio)
|
|
20
|
+
- [1) Configure document internationalization](#1-configure-document-internationalization)
|
|
21
|
+
- [2) Add the AI translation plugin](#2-add-the-ai-translation-plugin)
|
|
22
|
+
- [3) Add translation history metadata](#3-add-translation-history-metadata)
|
|
23
|
+
- [4) Deploy schema for Agent Actions](#4-deploy-schema-for-agent-actions)
|
|
24
|
+
- [Optional: OpenAI mode](#optional-openai-mode)
|
|
25
|
+
- [User guide (for editors)](#user-guide-for-editors)
|
|
26
|
+
- [Translate a document](#translate-a-document)
|
|
27
|
+
- [Choose a Content Release](#choose-a-content-release)
|
|
28
|
+
- [Translation options](#translation-options)
|
|
29
|
+
- [Translate referenced documents](#translate-referenced-documents)
|
|
30
|
+
- [What gets created](#what-gets-created)
|
|
31
|
+
- [Content configuration (settings + glossary)](#content-configuration-settings--glossary)
|
|
32
|
+
- [`translationSettings`](#translationsettings)
|
|
33
|
+
- [`translationGlossaryTerm`](#translationglossaryterm)
|
|
34
|
+
- [Advanced configuration](#advanced-configuration)
|
|
35
|
+
- [Exclude fields from translation](#exclude-fields-from-translation)
|
|
36
|
+
- [Field transforms](#field-transforms)
|
|
37
|
+
- [Concurrency and depth](#concurrency-and-depth)
|
|
38
|
+
- [Debugging & troubleshooting](#debugging--troubleshooting)
|
|
39
|
+
- [Development (this repo)](#development-this-repo)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## What this plugin does
|
|
45
|
+
|
|
46
|
+
When you open a document in Sanity Studio (for a configured schema type), the plugin adds a **Translate** action. You can:
|
|
47
|
+
|
|
48
|
+
1. Select one or more **target languages**
|
|
49
|
+
2. Choose whether to:
|
|
50
|
+
- overwrite existing translations
|
|
51
|
+
- translate only diffs (when applicable)
|
|
52
|
+
- translate referenced documents (with depth control)
|
|
53
|
+
3. Run translation using:
|
|
54
|
+
- **Sanity Agent Actions** (default), or
|
|
55
|
+
- **OpenAI** (optional)
|
|
56
|
+
|
|
57
|
+
Translations are written as **version documents** in a **Content Release**, so you can review and publish them on your schedule.
|
|
58
|
+
|
|
59
|
+
---
|
|
4
60
|
|
|
5
|
-
##
|
|
61
|
+
## Prerequisites
|
|
62
|
+
|
|
63
|
+
Your Studio must already support internationalized documents.
|
|
64
|
+
|
|
65
|
+
Required:
|
|
66
|
+
|
|
67
|
+
- **Sanity Studio v4** (plugin is built for Sanity Studio v4)
|
|
68
|
+
- `@sanity/document-internationalization` configured
|
|
69
|
+
- `sanity-plugin-internationalized-array` installed and configured (used for glossary term translations)
|
|
70
|
+
- `@sanity/client` **7.1.0+** (required for Agent Actions)
|
|
6
71
|
|
|
7
|
-
|
|
8
|
-
- 🔄 **Smart Translation Strategies** - Automatically detects translation freshness (fresh/stale/drifted) and applies appropriate strategy
|
|
9
|
-
- 🔗 **Reference Translation** - Optionally translate referenced documents recursively with configurable depth
|
|
10
|
-
- 🎯 **Custom Translation Prompts** - Configure global and locale-specific translation instructions
|
|
11
|
-
- 📚 **Translation Glossary** - Define term-specific translations to ensure consistency
|
|
12
|
-
- 🛡️ **Protected Phrases** - Specify terms that should never be translated (e.g., brand names, product names)
|
|
13
|
-
- 💬 **Feedback System** - AI-powered feedback analysis that automatically improves translation settings
|
|
14
|
-
- ⚙️ **Flexible Configuration** - Control concurrent translation jobs, reference depth, and translation behavior
|
|
15
|
-
- 🔍 **Debug Mode** - Comprehensive logging for troubleshooting translation issues
|
|
72
|
+
Also required for Agent Actions:
|
|
16
73
|
|
|
17
|
-
|
|
74
|
+
- Your schema must be deployed to Sanity so Agent Actions can access it (see [Deploy schema](#4-deploy-schema-for-agent-actions))
|
|
75
|
+
- **Important API version requirement:** the client must use `apiVersion: 'vX'` (not date-based versions). Date-based versions can cause Agent Actions to fail.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
Use your Studio package manager (examples use `pnpm`):
|
|
18
82
|
|
|
19
83
|
```sh
|
|
20
84
|
pnpm add @braze/sanity-plugin-ai-translation
|
|
21
85
|
```
|
|
22
86
|
|
|
23
|
-
|
|
87
|
+
You’ll also need the prerequisites if you haven’t installed them yet:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
pnpm add @sanity/document-internationalization sanity-plugin-internationalized-array
|
|
91
|
+
pnpm add @sanity/client@^7.1.0
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Configure (Studio)
|
|
97
|
+
|
|
98
|
+
### 1) Configure document internationalization
|
|
99
|
+
|
|
100
|
+
This plugin is designed to work with `@sanity/document-internationalization`. Configure it first.
|
|
24
101
|
|
|
25
|
-
|
|
102
|
+
You’ll typically define your supported languages as:
|
|
26
103
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
104
|
+
```typescript
|
|
105
|
+
const SUPPORTED_LANGUAGES = [
|
|
106
|
+
{ id: "en-us", title: "English" },
|
|
107
|
+
{ id: "es", title: "Spanish" },
|
|
108
|
+
{ id: "fr", title: "French" },
|
|
109
|
+
];
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then enable internationalization for the schema types you want to translate:
|
|
31
113
|
|
|
32
|
-
|
|
114
|
+
```typescript
|
|
115
|
+
documentInternationalization({
|
|
116
|
+
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
117
|
+
schemaTypes: ["post", "page"],
|
|
118
|
+
languageField: "language", // your language field name (example)
|
|
119
|
+
});
|
|
120
|
+
```
|
|
33
121
|
|
|
34
|
-
###
|
|
122
|
+
### 2) Add the AI translation plugin
|
|
35
123
|
|
|
36
|
-
Add
|
|
124
|
+
Add `aiTranslationPlugin()` after `documentInternationalization()`:
|
|
37
125
|
|
|
38
126
|
```typescript
|
|
39
|
-
import { defineConfig } from
|
|
40
|
-
import { documentInternationalization } from
|
|
41
|
-
import { aiTranslationPlugin } from
|
|
127
|
+
import { defineConfig } from "sanity";
|
|
128
|
+
import { documentInternationalization } from "@sanity/document-internationalization";
|
|
129
|
+
import { aiTranslationPlugin } from "@braze/sanity-plugin-ai-translation";
|
|
42
130
|
|
|
43
131
|
const SUPPORTED_LANGUAGES = [
|
|
44
|
-
{ id:
|
|
45
|
-
{ id:
|
|
46
|
-
{ id:
|
|
47
|
-
|
|
48
|
-
]
|
|
132
|
+
{ id: "en", title: "English" },
|
|
133
|
+
{ id: "es", title: "Spanish" },
|
|
134
|
+
{ id: "fr", title: "French" },
|
|
135
|
+
];
|
|
49
136
|
|
|
50
137
|
export default defineConfig({
|
|
51
|
-
// ...
|
|
138
|
+
// ...your config...
|
|
52
139
|
plugins: [
|
|
53
|
-
// Document internationalization must be configured first
|
|
54
140
|
documentInternationalization({
|
|
55
141
|
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
56
|
-
schemaTypes: [
|
|
142
|
+
schemaTypes: ["post", "page"],
|
|
143
|
+
languageField: "language",
|
|
57
144
|
}),
|
|
58
145
|
|
|
59
|
-
// Then add the AI translation plugin
|
|
60
146
|
aiTranslationPlugin({
|
|
61
|
-
// Required: Array of supported languages
|
|
62
147
|
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
148
|
+
translatableSchemaTypes: ["post", "page"],
|
|
149
|
+
languageFieldName: "language",
|
|
150
|
+
schemaId: "_.schemas.default",
|
|
151
|
+
maxConcurrentTranslationJobs: 5,
|
|
152
|
+
maxReferenceDepth: 3,
|
|
153
|
+
}),
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
```
|
|
63
157
|
|
|
64
|
-
|
|
65
|
-
translatableSchemaTypes: ['post', 'page', 'product'],
|
|
158
|
+
**Notes**
|
|
66
159
|
|
|
67
|
-
|
|
68
|
-
|
|
160
|
+
- `schemaId` is usually `'_.schemas.default'` for most projects.
|
|
161
|
+
- `languageFieldName` must match the field your documents use (often `language`).
|
|
69
162
|
|
|
70
|
-
|
|
71
|
-
schemaId: '_.schemas.default',
|
|
163
|
+
### 3) Add translation history metadata
|
|
72
164
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
165
|
+
This plugin can store translation history via a metadata schema you attach to `documentInternationalization`.
|
|
166
|
+
|
|
167
|
+
Add `translationHistorySchema`:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { translationHistorySchema } from "@braze/sanity-plugin-ai-translation";
|
|
171
|
+
|
|
172
|
+
documentInternationalization({
|
|
173
|
+
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
174
|
+
schemaTypes: ["post", "page"],
|
|
175
|
+
languageField: "language",
|
|
176
|
+
metadataFields: [translationHistorySchema],
|
|
177
|
+
});
|
|
78
178
|
```
|
|
79
179
|
|
|
80
|
-
###
|
|
180
|
+
### 4) Deploy schema for Agent Actions
|
|
181
|
+
|
|
182
|
+
Agent Actions need access to your deployed schema.
|
|
183
|
+
|
|
184
|
+
From your Studio project directory:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
npx sanity deploy
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If Agent Actions fail with schema-related errors, re-check:
|
|
191
|
+
|
|
192
|
+
- schema deployment
|
|
193
|
+
- `schemaId`
|
|
194
|
+
- client `apiVersion` is set to `'vX'` (not date-based)
|
|
81
195
|
|
|
82
|
-
|
|
83
|
-
|--------|------|----------|-------------|
|
|
84
|
-
| `supportedLanguages` | `Array<{id: string, title: string}>` | **Yes** | List of supported languages for translation |
|
|
85
|
-
| `translatableSchemaTypes` | `string[]` | **Yes** | Document types that should have translation actions |
|
|
86
|
-
| `languageFieldName` | `string` | **Yes** | Field name containing the language identifier in documents |
|
|
87
|
-
| `schemaId` | `string` | **Yes** | Schema ID for Sanity Agent Actions (typically `'_.schemas.default'`) |
|
|
88
|
-
| `maxConcurrentTranslationJobs` | `number` | No | Maximum number of concurrent translations (default: 5) |
|
|
89
|
-
| `openai` | `OpenAIConfig` | No | Optional OpenAI integration configuration (see below) |
|
|
196
|
+
---
|
|
90
197
|
|
|
91
|
-
|
|
198
|
+
## Optional: OpenAI mode
|
|
92
199
|
|
|
93
|
-
|
|
200
|
+
OpenAI mode is optional. If enabled, users can choose **OpenAI** as the translation provider inside the translation dialog.
|
|
94
201
|
|
|
95
|
-
|
|
202
|
+
Example configuration:
|
|
96
203
|
|
|
97
204
|
```typescript
|
|
98
205
|
aiTranslationPlugin({
|
|
99
|
-
|
|
206
|
+
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
207
|
+
translatableSchemaTypes: ["post", "page"],
|
|
208
|
+
languageFieldName: "language",
|
|
209
|
+
schemaId: "_.schemas.default",
|
|
210
|
+
|
|
100
211
|
openai: {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
defaultMode: 'sanity-
|
|
107
|
-
}
|
|
108
|
-
})
|
|
212
|
+
enabled: true,
|
|
213
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
214
|
+
model: "gpt-5",
|
|
215
|
+
reasoningEffort: "medium", // 'minimal' | 'low' | 'medium' | 'high'
|
|
216
|
+
verbosity: "medium", // 'low' | 'medium' | 'high' (if supported by model)
|
|
217
|
+
defaultMode: "openai-only", // 'sanity-only' | 'openai-only'
|
|
218
|
+
},
|
|
219
|
+
});
|
|
109
220
|
```
|
|
110
221
|
|
|
111
|
-
|
|
222
|
+
**Security**
|
|
112
223
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
OPENAI_API_KEY=sk-...your-key-here...
|
|
116
|
-
```
|
|
224
|
+
- Do not hardcode API keys.
|
|
225
|
+
- Use environment variables (e.g. `.env` / `.env.local`) and your deployment platform’s secret manager.
|
|
117
226
|
|
|
118
|
-
|
|
227
|
+
**Why use OpenAI mode?**
|
|
119
228
|
|
|
120
|
-
|
|
229
|
+
- Larger effective context window for long documents and long style guides
|
|
230
|
+
- Deterministic serialization/deserialization to preserve structure
|
|
231
|
+
- Strict structure validation to prevent data corruption
|
|
121
232
|
|
|
122
|
-
|
|
123
|
-
- Uses Sanity's built-in translation agent
|
|
124
|
-
- Fast and reliable
|
|
125
|
-
- No additional API costs
|
|
126
|
-
- 2000 character style guide limit
|
|
233
|
+
**Costs**
|
|
127
234
|
|
|
128
|
-
|
|
129
|
-
- Direct translation via GPT-5
|
|
130
|
-
- 1 million token context window
|
|
131
|
-
- Full style guide capacity
|
|
132
|
-
- Incurs OpenAI API costs
|
|
235
|
+
- OpenAI calls incur API costs based on token usage.
|
|
133
236
|
|
|
134
|
-
|
|
135
|
-
- Sanity translation reviewed and refined by GPT-5
|
|
136
|
-
- Best quality results
|
|
137
|
-
- Approximately 2x processing time
|
|
138
|
-
- Incurs OpenAI API costs
|
|
237
|
+
---
|
|
139
238
|
|
|
140
|
-
|
|
239
|
+
## User guide (for editors)
|
|
141
240
|
|
|
142
|
-
|
|
143
|
-
- **1M token context window** - Handle very large documents and style guides
|
|
144
|
-
- **Advanced reasoning** - Configurable reasoning effort for quality vs. speed tradeoff
|
|
145
|
-
- **Verbosity control** - Adjust response length to optimize token usage
|
|
146
|
-
- **Structure preservation** - Deterministic serialization ensures all document metadata and structure is preserved
|
|
241
|
+
### Translate a document
|
|
147
242
|
|
|
148
|
-
|
|
243
|
+
1. Open a document in Studio whose type is included in `translatableSchemaTypes`.
|
|
244
|
+
2. Open the document actions menu and click **Translate**.
|
|
245
|
+
3. Select one or more target languages.
|
|
246
|
+
4. Configure translation options (see below).
|
|
247
|
+
5. Click **Translate** to start.
|
|
149
248
|
|
|
150
|
-
|
|
151
|
-
- **GPT-5**: ~$0.002 per 1k input tokens, ~$0.010 per 1k output tokens, ~$0.005 per 1k reasoning tokens
|
|
152
|
-
- **Typical article** (2000 words): ~$0.05-0.15 per translation
|
|
153
|
-
- The plugin shows real-time cost estimates and usage summaries
|
|
249
|
+
While translation runs, you’ll see job statuses for each target language (and for any referenced documents, if enabled).
|
|
154
250
|
|
|
155
|
-
|
|
251
|
+
### Choose a Content Release
|
|
156
252
|
|
|
157
|
-
|
|
158
|
-
|--------|------|---------|-------------|
|
|
159
|
-
| `apiKey` | `string` | - | OpenAI API key (required if OpenAI enabled) |
|
|
160
|
-
| `model` | `'gpt-5' \| 'gpt-4o' \| 'gpt-4-turbo' \| 'gpt-4'` | `'gpt-5'` | Model to use for translation/review |
|
|
161
|
-
| `reasoningEffort` | `'minimal' \| 'low' \| 'medium' \| 'high'` | `'medium'` | GPT-5 reasoning effort (affects speed and quality) |
|
|
162
|
-
| `verbosity` | `'low' \| 'medium' \| 'high'` | `'medium'` | GPT-5 response verbosity |
|
|
163
|
-
| `enabled` | `boolean` | `false` | Enable OpenAI features |
|
|
164
|
-
| `defaultMode` | `TranslationMode` | `'sanity-only'` | Default translation mode when dialog opens |
|
|
253
|
+
Translations are always created inside a **Content Release**.
|
|
165
254
|
|
|
166
|
-
|
|
255
|
+
In the dialog you can typically:
|
|
167
256
|
|
|
168
|
-
|
|
257
|
+
- create a new release, or
|
|
258
|
+
- add the translations to an existing active release
|
|
169
259
|
|
|
170
|
-
|
|
171
|
-
import { documentInternationalization } from '@sanity/document-internationalization'
|
|
172
|
-
import { translationHistorySchema } from '@braze/sanity-plugin-ai-translation'
|
|
260
|
+
Use releases when you want:
|
|
173
261
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
177
|
-
schemaTypes: ['post', 'page'],
|
|
178
|
-
// Add translation history to metadata
|
|
179
|
-
metadataFields: [translationHistorySchema],
|
|
180
|
-
})
|
|
181
|
-
```
|
|
262
|
+
- staged review
|
|
263
|
+
- coordinated publishing of multiple translations at once
|
|
182
264
|
|
|
183
|
-
###
|
|
265
|
+
### Translation options
|
|
184
266
|
|
|
185
|
-
|
|
267
|
+
The exact UI labels may vary, but the workflow supports:
|
|
186
268
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
269
|
+
- **Overwrite fresh translations**
|
|
270
|
+
- Forces re-translation even if the existing translation is considered up-to-date.
|
|
271
|
+
- **Translate only diffs**
|
|
272
|
+
- For stale translations, translate only the changed fields where supported.
|
|
273
|
+
- **Translate references**
|
|
274
|
+
- Also translate referenced documents and update references to point at the localized versions.
|
|
275
|
+
- **Max reference depth**
|
|
276
|
+
- How far to traverse reference graphs (depth `0` means no traversal).
|
|
277
|
+
|
|
278
|
+
### Translate referenced documents
|
|
279
|
+
|
|
280
|
+
If you enable reference translation:
|
|
281
|
+
|
|
282
|
+
- The plugin traverses references (up to the configured depth).
|
|
283
|
+
- It translates referenced documents that are eligible (type is included in `translatableSchemaTypes`).
|
|
284
|
+
- It updates references in the translated document to point to localized versions.
|
|
285
|
+
|
|
286
|
+
This is useful for content like:
|
|
287
|
+
|
|
288
|
+
- posts referencing authors
|
|
289
|
+
- pages referencing shared sections
|
|
290
|
+
- documents referencing related content
|
|
191
291
|
|
|
192
|
-
|
|
292
|
+
### What gets created
|
|
193
293
|
|
|
194
|
-
|
|
294
|
+
For each selected target language, the plugin creates a **versioned document** inside the selected Content Release.
|
|
195
295
|
|
|
196
|
-
|
|
296
|
+
You can then:
|
|
197
297
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
3. Select target languages
|
|
201
|
-
4. Configure translation options:
|
|
202
|
-
- **Overwrite fresh translations** - Re-translate even if translation is up-to-date
|
|
203
|
-
- **Translate only diffs** - For stale translations, only translate changed fields
|
|
204
|
-
- **Translate references** - Include referenced documents in translation
|
|
205
|
-
- **Max reference depth** - How deep to traverse references (if enabled)
|
|
206
|
-
5. Click "Translate" to start
|
|
298
|
+
- review and edit translated versions
|
|
299
|
+
- publish the release when ready
|
|
207
300
|
|
|
208
|
-
|
|
301
|
+
---
|
|
209
302
|
|
|
210
|
-
|
|
303
|
+
## Content configuration (settings + glossary)
|
|
211
304
|
|
|
212
|
-
|
|
213
|
-
- **Stale** ⚠️ - Source document revision changed (trivial changes)
|
|
214
|
-
- **Drifted** 🔴 - Source document content changed significantly
|
|
305
|
+
The plugin introduces two document types for configuration:
|
|
215
306
|
|
|
216
|
-
|
|
217
|
-
- **Fresh** translations are skipped (unless "Overwrite fresh" is enabled)
|
|
218
|
-
- **Stale** translations can use diff-based translation (if "Translate only diffs" is enabled)
|
|
219
|
-
- **Drifted** translations are fully re-translated
|
|
307
|
+
### `translationSettings`
|
|
220
308
|
|
|
221
|
-
|
|
309
|
+
Create a document with `_id == "translation-settings"` to configure:
|
|
222
310
|
|
|
223
|
-
|
|
311
|
+
- **Global prompt** (applies to all translations)
|
|
312
|
+
- **Locale prompts** (per-target-language instructions)
|
|
313
|
+
- **Protected phrases** (never translate these terms; e.g. brand names)
|
|
314
|
+
|
|
315
|
+
Example shape:
|
|
224
316
|
|
|
225
317
|
```typescript
|
|
226
318
|
{
|
|
227
319
|
_type: 'translationSettings',
|
|
228
320
|
_id: 'translation-settings',
|
|
229
|
-
|
|
230
|
-
// Global prompt applied to all translations
|
|
231
321
|
globalPrompt: 'Maintain a professional tone. Preserve formatting and links.',
|
|
232
|
-
|
|
233
|
-
// Locale-specific prompts
|
|
234
322
|
localePrompts: [
|
|
235
|
-
{
|
|
236
|
-
|
|
237
|
-
prompt: 'Use formal Spanish (usted). Avoid Latin American regionalisms.'
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
locale: 'fr',
|
|
241
|
-
prompt: 'Use formal French. Maintain gender agreement for all terms.'
|
|
242
|
-
}
|
|
323
|
+
{_key: 'es', value: 'Use formal Spanish (usted).'},
|
|
324
|
+
{_key: 'fr', value: 'Use formal French.'},
|
|
243
325
|
],
|
|
244
|
-
|
|
245
|
-
// Terms that should never be translated
|
|
246
326
|
protectedPhrases: ['Braze', 'iOS', 'Android', 'SDK']
|
|
247
327
|
}
|
|
248
328
|
```
|
|
249
329
|
|
|
250
|
-
|
|
330
|
+
Notes:
|
|
331
|
+
|
|
332
|
+
- `localePrompts` uses `_key` for the locale id (must match your `supportedLanguages[].id`).
|
|
333
|
+
- Protected phrases may be filtered to only those that appear in the source document (to keep prompts smaller).
|
|
251
334
|
|
|
252
|
-
|
|
335
|
+
### `translationGlossaryTerm`
|
|
336
|
+
|
|
337
|
+
Create glossary term documents to enforce consistent translations of key terms.
|
|
338
|
+
|
|
339
|
+
This schema uses `internationalizedArrayString` so you can provide one value per locale.
|
|
340
|
+
|
|
341
|
+
Conceptual example:
|
|
253
342
|
|
|
254
343
|
```typescript
|
|
255
344
|
{
|
|
256
345
|
_type: 'translationGlossaryTerm',
|
|
257
346
|
translations: [
|
|
258
|
-
{
|
|
259
|
-
{
|
|
260
|
-
{
|
|
261
|
-
{ locale: 'de', value: 'Dashboard' }
|
|
347
|
+
{_key: 'en', value: 'dashboard'},
|
|
348
|
+
{_key: 'es', value: 'panel de control'},
|
|
349
|
+
{_key: 'fr', value: 'tableau de bord'},
|
|
262
350
|
]
|
|
263
351
|
}
|
|
264
352
|
```
|
|
265
353
|
|
|
266
|
-
|
|
354
|
+
Glossary terms are used to build locale-specific translation instructions.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Advanced configuration
|
|
359
|
+
|
|
360
|
+
### Exclude fields from translation
|
|
361
|
+
|
|
362
|
+
Use `excludeFields` to prevent certain fields from being translated (slugs, enums, flags, etc.).
|
|
363
|
+
|
|
364
|
+
Supports:
|
|
365
|
+
|
|
366
|
+
- field name matches anywhere (e.g. `'status'`)
|
|
367
|
+
- exact paths (e.g. `'seo.title'`)
|
|
368
|
+
- array paths / wildcards (e.g. `'body[*].imageType'`)
|
|
369
|
+
|
|
370
|
+
Example:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
aiTranslationPlugin({
|
|
374
|
+
// ...
|
|
375
|
+
excludeFields: ["slug", "status", "seo.canonicalUrl", "body[*].variant"],
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Important behavior:
|
|
267
380
|
|
|
268
|
-
The plugin
|
|
381
|
+
- The plugin will **not** allow the `languageFieldName` to be excluded. If you include it, it will be removed to prevent translation errors.
|
|
269
382
|
|
|
270
|
-
|
|
271
|
-
2. Click "Give Translation Feedback" action
|
|
272
|
-
3. Provide feedback and/or enable "Include edits" to analyze changes
|
|
273
|
-
4. Review AI-generated suggestions:
|
|
274
|
-
- Global prompt updates
|
|
275
|
-
- Locale-specific prompt updates
|
|
276
|
-
- Protected phrases to add
|
|
277
|
-
- Glossary terms to add
|
|
278
|
-
5. Approve suggestions to apply
|
|
383
|
+
### Field transforms
|
|
279
384
|
|
|
280
|
-
|
|
385
|
+
Some fields (like Portable Text or caption formats) benefit from transformation before translation.
|
|
281
386
|
|
|
282
|
-
|
|
387
|
+
Transforms run in this sequence:
|
|
283
388
|
|
|
284
|
-
|
|
389
|
+
1. Filter excluded fields
|
|
390
|
+
2. Apply transforms (to produce a translatable representation)
|
|
391
|
+
3. Translate (Sanity Agent or OpenAI)
|
|
392
|
+
4. Restore transforms (back into original structure)
|
|
393
|
+
5. Restore excluded fields (except language)
|
|
394
|
+
6. Create version document in the release
|
|
285
395
|
|
|
286
|
-
|
|
287
|
-
2. **Determine Strategy** - Checks freshness state (fresh/stale/drifted)
|
|
288
|
-
3. **Prepare Translation**:
|
|
289
|
-
- For **drifted** or **fresh (overwrite)**: Full document translation
|
|
290
|
-
- For **stale (translate diffs)**: Diff-based translation of changed fields only
|
|
291
|
-
4. **Translate** - Uses Sanity Agent Actions with configured prompts
|
|
292
|
-
5. **Localize References** - Optionally translates referenced documents
|
|
293
|
-
6. **Update Metadata** - Stores translation history with snapshots
|
|
396
|
+
Built-in transforms include:
|
|
294
397
|
|
|
295
|
-
|
|
398
|
+
- `richTextTransform` for Portable Text
|
|
399
|
+
- `vttCaptionTransform` for VTT captions
|
|
400
|
+
- `wistiaCaptionTransform` for Wistia captions (if used by your schema)
|
|
296
401
|
|
|
297
|
-
|
|
402
|
+
Example setup:
|
|
298
403
|
|
|
299
404
|
```typescript
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
405
|
+
import {
|
|
406
|
+
aiTranslationPlugin,
|
|
407
|
+
richTextTransform,
|
|
408
|
+
vttCaptionTransform,
|
|
409
|
+
wistiaCaptionTransform,
|
|
410
|
+
} from "@braze/sanity-plugin-ai-translation";
|
|
306
411
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
412
|
+
aiTranslationPlugin({
|
|
413
|
+
// ...
|
|
414
|
+
fieldTransforms: [
|
|
415
|
+
{
|
|
416
|
+
...vttCaptionTransform,
|
|
417
|
+
matcher: { schemaType: "video", fieldName: "captions" },
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
...wistiaCaptionTransform,
|
|
421
|
+
matcher: { schemaType: "videoDoc", fieldName: "wistiaCaptions" },
|
|
422
|
+
},
|
|
423
|
+
richTextTransform,
|
|
424
|
+
],
|
|
425
|
+
});
|
|
314
426
|
```
|
|
315
427
|
|
|
316
|
-
|
|
317
|
-
- Recursively traverses references up to configured depth
|
|
318
|
-
- Translates referenced documents if they don't exist
|
|
319
|
-
- Updates references to point to localized versions
|
|
320
|
-
- Uses semaphore to control concurrent jobs
|
|
428
|
+
**Order matters:** first match wins.
|
|
321
429
|
|
|
322
|
-
|
|
430
|
+
### Concurrency and depth
|
|
323
431
|
|
|
324
|
-
|
|
432
|
+
- `maxConcurrentTranslationJobs` controls how many translations run at the same time (default behavior is conservative).
|
|
433
|
+
- `maxReferenceDepth` controls how deeply references are traversed (set to `0` to disable traversal).
|
|
325
434
|
|
|
326
|
-
|
|
327
|
-
# Login to registry
|
|
328
|
-
npm login --scope=@braze --registry=https://registry.npmjs.org/
|
|
435
|
+
---
|
|
329
436
|
|
|
330
|
-
|
|
331
|
-
|
|
437
|
+
## Debugging & troubleshooting
|
|
438
|
+
|
|
439
|
+
### Enable debug logging
|
|
440
|
+
|
|
441
|
+
This plugin uses a centralized debug logger. To enable debug output in your browser:
|
|
442
|
+
|
|
443
|
+
```js
|
|
444
|
+
localStorage.setItem("sanity-ai-translation:debug", "true");
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
To disable:
|
|
448
|
+
|
|
449
|
+
```js
|
|
450
|
+
localStorage.removeItem("sanity-ai-translation:debug");
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Common issues
|
|
454
|
+
|
|
455
|
+
**Agent Actions fail immediately**
|
|
456
|
+
|
|
457
|
+
- Confirm `@sanity/client` is **7.1.0+**
|
|
458
|
+
- Confirm your client API version is `apiVersion: 'vX'` (not date-based)
|
|
459
|
+
- Confirm schema is deployed: `npx sanity deploy`
|
|
460
|
+
- Confirm `schemaId` matches your deployed schema (commonly `'_.schemas.default'`)
|
|
461
|
+
|
|
462
|
+
**Translation completes but content didn’t change**
|
|
463
|
+
|
|
464
|
+
- Ensure the document type is included in `translatableSchemaTypes`
|
|
465
|
+
- Check whether excluded fields or transforms are preventing updates
|
|
466
|
+
- If using OpenAI mode, structure validation can reject translations that modify non-text fields
|
|
467
|
+
|
|
468
|
+
**Reference translation is slow**
|
|
469
|
+
|
|
470
|
+
- Reduce `maxReferenceDepth`
|
|
471
|
+
- Reduce `maxConcurrentTranslationJobs`
|
|
472
|
+
- Ensure you’re not translating large reference graphs unnecessarily
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Development (this repo)
|
|
477
|
+
|
|
478
|
+
This repository is a pnpm monorepo with:
|
|
479
|
+
|
|
480
|
+
- `package/` — the plugin package
|
|
481
|
+
- `demo-nextjs/` — demo Next.js app with embedded Studio
|
|
482
|
+
|
|
483
|
+
Common commands:
|
|
484
|
+
|
|
485
|
+
```sh
|
|
486
|
+
pnpm dev
|
|
332
487
|
pnpm build
|
|
333
|
-
|
|
488
|
+
|
|
489
|
+
cd package
|
|
490
|
+
pnpm test
|
|
491
|
+
pnpm typecheck
|
|
492
|
+
pnpm lint
|
|
334
493
|
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## License
|
|
498
|
+
|
|
499
|
+
MIT
|