@braze/sanity-plugin-ai-translation 0.2.6 → 0.2.21
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 +220 -385
- package/dist/index.d.ts +8 -500
- package/dist/index.js +2367 -2964
- package/dist/index.js.map +1 -1
- package/package.json +4 -7
package/README.md
CHANGED
|
@@ -1,499 +1,334 @@
|
|
|
1
1
|
# @braze/sanity-plugin-ai-translation
|
|
2
2
|
|
|
3
|
-
A Sanity Studio plugin that
|
|
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
|
-
---
|
|
3
|
+
> A Sanity Studio v3 plugin that provides AI-powered translation capabilities for internationalized content using Sanity Agent Actions.
|
|
60
4
|
|
|
61
|
-
##
|
|
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)
|
|
5
|
+
## Features
|
|
71
6
|
|
|
72
|
-
|
|
7
|
+
- 🤖 **AI-Powered Translation** - Uses Sanity Agent Actions to translate documents between languages
|
|
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
|
|
73
16
|
|
|
74
|
-
|
|
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`):
|
|
17
|
+
## Installation
|
|
82
18
|
|
|
83
19
|
```sh
|
|
84
20
|
pnpm add @braze/sanity-plugin-ai-translation
|
|
85
21
|
```
|
|
86
22
|
|
|
87
|
-
|
|
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.
|
|
101
|
-
|
|
102
|
-
You’ll typically define your supported languages as:
|
|
23
|
+
## Prerequisites
|
|
103
24
|
|
|
104
|
-
|
|
105
|
-
const SUPPORTED_LANGUAGES = [
|
|
106
|
-
{ id: "en-us", title: "English" },
|
|
107
|
-
{ id: "es", title: "Spanish" },
|
|
108
|
-
{ id: "fr", title: "French" },
|
|
109
|
-
];
|
|
110
|
-
```
|
|
25
|
+
This plugin requires:
|
|
111
26
|
|
|
112
|
-
|
|
27
|
+
1. **Sanity Studio v4** - Version 4.9.0 or later
|
|
28
|
+
2. **@sanity/client** - Version 7.1.0 or later (for Agent Actions)
|
|
29
|
+
3. **@sanity/document-internationalization** - Plugin must be installed and configured
|
|
30
|
+
4. **sanity-plugin-internationalized-array** - Version 3.1.5 or later
|
|
113
31
|
|
|
114
|
-
|
|
115
|
-
documentInternationalization({
|
|
116
|
-
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
117
|
-
schemaTypes: ["post", "page"],
|
|
118
|
-
languageField: "language", // your language field name (example)
|
|
119
|
-
});
|
|
120
|
-
```
|
|
32
|
+
## Configuration
|
|
121
33
|
|
|
122
|
-
###
|
|
34
|
+
### Basic Setup
|
|
123
35
|
|
|
124
|
-
Add `
|
|
36
|
+
Add the plugin to your `sanity.config.ts` file:
|
|
125
37
|
|
|
126
38
|
```typescript
|
|
127
|
-
import { defineConfig } from
|
|
128
|
-
import { documentInternationalization } from
|
|
129
|
-
import { aiTranslationPlugin } from
|
|
39
|
+
import { defineConfig } from 'sanity'
|
|
40
|
+
import { documentInternationalization } from '@sanity/document-internationalization'
|
|
41
|
+
import { aiTranslationPlugin } from '@braze/sanity-plugin-ai-translation'
|
|
130
42
|
|
|
131
43
|
const SUPPORTED_LANGUAGES = [
|
|
132
|
-
{ id:
|
|
133
|
-
{ id:
|
|
134
|
-
{ id:
|
|
135
|
-
|
|
44
|
+
{ id: 'en', title: 'English' },
|
|
45
|
+
{ id: 'es', title: 'Spanish' },
|
|
46
|
+
{ id: 'fr', title: 'French' },
|
|
47
|
+
{ id: 'de', title: 'German' },
|
|
48
|
+
]
|
|
136
49
|
|
|
137
50
|
export default defineConfig({
|
|
138
|
-
// ...
|
|
51
|
+
// ... other config
|
|
139
52
|
plugins: [
|
|
53
|
+
// Document internationalization must be configured first
|
|
140
54
|
documentInternationalization({
|
|
141
55
|
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
142
|
-
schemaTypes: [
|
|
143
|
-
languageField: "language",
|
|
56
|
+
schemaTypes: ['post', 'page', 'product'],
|
|
144
57
|
}),
|
|
145
58
|
|
|
59
|
+
// Then add the AI translation plugin
|
|
146
60
|
aiTranslationPlugin({
|
|
61
|
+
// Required: Array of supported languages
|
|
147
62
|
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
148
|
-
translatableSchemaTypes: ["post", "page"],
|
|
149
|
-
languageFieldName: "language",
|
|
150
|
-
schemaId: "_.schemas.default",
|
|
151
|
-
maxConcurrentTranslationJobs: 5,
|
|
152
|
-
maxReferenceDepth: 3,
|
|
153
|
-
}),
|
|
154
|
-
],
|
|
155
|
-
});
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**Notes**
|
|
159
|
-
|
|
160
|
-
- `schemaId` is usually `'_.schemas.default'` for most projects.
|
|
161
|
-
- `languageFieldName` must match the field your documents use (often `language`).
|
|
162
|
-
|
|
163
|
-
### 3) Add translation history metadata
|
|
164
63
|
|
|
165
|
-
|
|
64
|
+
// Required: Document types that can be translated
|
|
65
|
+
translatableSchemaTypes: ['post', 'page', 'product'],
|
|
166
66
|
|
|
167
|
-
|
|
67
|
+
// Required: Field name containing the language identifier
|
|
68
|
+
languageFieldName: 'language',
|
|
168
69
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
documentInternationalization({
|
|
173
|
-
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
174
|
-
schemaTypes: ["post", "page"],
|
|
175
|
-
languageField: "language",
|
|
176
|
-
metadataFields: [translationHistorySchema],
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### 4) Deploy schema for Agent Actions
|
|
181
|
-
|
|
182
|
-
Agent Actions need access to your deployed schema.
|
|
70
|
+
// Required: Schema ID for Agent Actions (usually '_.schemas.default')
|
|
71
|
+
schemaId: '_.schemas.default',
|
|
183
72
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
73
|
+
// Optional: Maximum concurrent translation jobs (default: 5)
|
|
74
|
+
maxConcurrentTranslationJobs: 5,
|
|
75
|
+
}),
|
|
76
|
+
],
|
|
77
|
+
})
|
|
188
78
|
```
|
|
189
79
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
- schema deployment
|
|
193
|
-
- `schemaId`
|
|
194
|
-
- client `apiVersion` is set to `'vX'` (not date-based)
|
|
80
|
+
### Configuration Options
|
|
195
81
|
|
|
196
|
-
|
|
82
|
+
| Option | Type | Required | Description |
|
|
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) |
|
|
197
90
|
|
|
198
|
-
|
|
91
|
+
### OpenAI Integration (Optional)
|
|
199
92
|
|
|
200
|
-
|
|
93
|
+
The plugin supports optional OpenAI integration for enhanced translation capabilities, including review of Sanity translations and direct GPT-5 translation with no character limits.
|
|
201
94
|
|
|
202
|
-
|
|
95
|
+
#### Setup
|
|
203
96
|
|
|
204
97
|
```typescript
|
|
205
98
|
aiTranslationPlugin({
|
|
206
|
-
|
|
207
|
-
translatableSchemaTypes: ["post", "page"],
|
|
208
|
-
languageFieldName: "language",
|
|
209
|
-
schemaId: "_.schemas.default",
|
|
210
|
-
|
|
99
|
+
// ... other required config
|
|
211
100
|
openai: {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
defaultMode:
|
|
218
|
-
}
|
|
219
|
-
})
|
|
101
|
+
apiKey: process.env.OPENAI_API_KEY!, // Required
|
|
102
|
+
model: 'gpt-5', // Optional, defaults to 'gpt-5'
|
|
103
|
+
reasoningEffort: 'medium', // Optional: 'minimal' | 'low' | 'medium' | 'high'
|
|
104
|
+
verbosity: 'medium', // Optional: 'low' | 'medium' | 'high'
|
|
105
|
+
enabled: true, // Optional, defaults to false
|
|
106
|
+
defaultMode: 'sanity-with-review', // Optional, defaults to 'sanity-only'
|
|
107
|
+
}
|
|
108
|
+
})
|
|
220
109
|
```
|
|
221
110
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
- Do not hardcode API keys.
|
|
225
|
-
- Use environment variables (e.g. `.env` / `.env.local`) and your deployment platform’s secret manager.
|
|
226
|
-
|
|
227
|
-
**Why use OpenAI mode?**
|
|
228
|
-
|
|
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
|
|
232
|
-
|
|
233
|
-
**Costs**
|
|
234
|
-
|
|
235
|
-
- OpenAI calls incur API costs based on token usage.
|
|
236
|
-
|
|
237
|
-
---
|
|
111
|
+
**⚠️ Security Note:** Never commit API keys to version control. Always use environment variables:
|
|
238
112
|
|
|
239
|
-
|
|
113
|
+
```bash
|
|
114
|
+
# .env.local
|
|
115
|
+
OPENAI_API_KEY=sk-...your-key-here...
|
|
116
|
+
```
|
|
240
117
|
|
|
241
|
-
|
|
118
|
+
#### Translation Modes
|
|
242
119
|
|
|
243
|
-
|
|
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.
|
|
120
|
+
When OpenAI is enabled, users can select from three translation modes:
|
|
248
121
|
|
|
249
|
-
|
|
122
|
+
1. **Sanity Agent Only** (Default)
|
|
123
|
+
- Uses Sanity's built-in translation agent
|
|
124
|
+
- Fast and reliable
|
|
125
|
+
- No additional API costs
|
|
126
|
+
- 2000 character style guide limit
|
|
250
127
|
|
|
251
|
-
|
|
128
|
+
2. **GPT-5 Only** (Beta)
|
|
129
|
+
- Direct translation via GPT-5
|
|
130
|
+
- 1 million token context window
|
|
131
|
+
- Full style guide capacity
|
|
132
|
+
- Incurs OpenAI API costs
|
|
252
133
|
|
|
253
|
-
|
|
134
|
+
3. **Sanity + GPT-5 Review** (Highest Quality)
|
|
135
|
+
- Sanity translation reviewed and refined by GPT-5
|
|
136
|
+
- Best quality results
|
|
137
|
+
- Approximately 2x processing time
|
|
138
|
+
- Incurs OpenAI API costs
|
|
254
139
|
|
|
255
|
-
|
|
140
|
+
#### GPT-5 Features
|
|
256
141
|
|
|
257
|
-
-
|
|
258
|
-
-
|
|
142
|
+
GPT-5 (released August 2025) provides:
|
|
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
|
|
259
147
|
|
|
260
|
-
|
|
148
|
+
#### Cost Considerations
|
|
261
149
|
|
|
262
|
-
|
|
263
|
-
-
|
|
150
|
+
OpenAI modes incur API costs based on token usage:
|
|
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
|
|
264
154
|
|
|
265
|
-
|
|
155
|
+
#### OpenAI Configuration Options
|
|
266
156
|
|
|
267
|
-
|
|
157
|
+
| Option | Type | Default | Description |
|
|
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 |
|
|
268
165
|
|
|
269
|
-
|
|
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).
|
|
166
|
+
### Schema Setup
|
|
277
167
|
|
|
278
|
-
|
|
168
|
+
The plugin requires you to add the `translationHistory` schema to your document internationalization configuration:
|
|
279
169
|
|
|
280
|
-
|
|
170
|
+
```typescript
|
|
171
|
+
import { documentInternationalization } from '@sanity/document-internationalization'
|
|
172
|
+
import { translationHistorySchema } from '@braze/sanity-plugin-ai-translation'
|
|
281
173
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
174
|
+
// In your sanity.config.ts
|
|
175
|
+
documentInternationalization({
|
|
176
|
+
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
177
|
+
schemaTypes: ['post', 'page'],
|
|
178
|
+
// Add translation history to metadata
|
|
179
|
+
metadataFields: [translationHistorySchema],
|
|
180
|
+
})
|
|
181
|
+
```
|
|
285
182
|
|
|
286
|
-
|
|
183
|
+
### Deploy Schema
|
|
287
184
|
|
|
288
|
-
|
|
289
|
-
- pages referencing shared sections
|
|
290
|
-
- documents referencing related content
|
|
185
|
+
After configuration, deploy your schema so Agent Actions can access it:
|
|
291
186
|
|
|
292
|
-
|
|
187
|
+
```bash
|
|
188
|
+
cd your-sanity-project
|
|
189
|
+
npx sanity deploy
|
|
190
|
+
```
|
|
293
191
|
|
|
294
|
-
|
|
192
|
+
## Usage
|
|
295
193
|
|
|
296
|
-
|
|
194
|
+
### Translation Action
|
|
297
195
|
|
|
298
|
-
|
|
299
|
-
- publish the release when ready
|
|
196
|
+
Once configured, a "Translate" action will appear on all documents of types listed in `translatableSchemaTypes`.
|
|
300
197
|
|
|
301
|
-
|
|
198
|
+
1. Open a document in Sanity Studio
|
|
199
|
+
2. Click the "Translate" action in the document menu
|
|
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
|
|
302
207
|
|
|
303
|
-
|
|
208
|
+
### Translation States
|
|
304
209
|
|
|
305
|
-
The plugin
|
|
210
|
+
The plugin tracks three translation states:
|
|
306
211
|
|
|
307
|
-
|
|
212
|
+
- **Fresh** ✅ - Translation is up-to-date with source
|
|
213
|
+
- **Stale** ⚠️ - Source document revision changed (trivial changes)
|
|
214
|
+
- **Drifted** 🔴 - Source document content changed significantly
|
|
308
215
|
|
|
309
|
-
|
|
216
|
+
Based on state:
|
|
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
|
|
310
220
|
|
|
311
|
-
|
|
312
|
-
- **Locale prompts** (per-target-language instructions)
|
|
313
|
-
- **Protected phrases** (never translate these terms; e.g. brand names)
|
|
221
|
+
### Translation Settings
|
|
314
222
|
|
|
315
|
-
|
|
223
|
+
Create a `translationSettings` document to customize translation behavior:
|
|
316
224
|
|
|
317
225
|
```typescript
|
|
318
226
|
{
|
|
319
227
|
_type: 'translationSettings',
|
|
320
228
|
_id: 'translation-settings',
|
|
229
|
+
|
|
230
|
+
// Global prompt applied to all translations
|
|
321
231
|
globalPrompt: 'Maintain a professional tone. Preserve formatting and links.',
|
|
232
|
+
|
|
233
|
+
// Locale-specific prompts
|
|
322
234
|
localePrompts: [
|
|
323
|
-
{
|
|
324
|
-
|
|
235
|
+
{
|
|
236
|
+
locale: 'es',
|
|
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
|
+
}
|
|
325
243
|
],
|
|
244
|
+
|
|
245
|
+
// Terms that should never be translated
|
|
326
246
|
protectedPhrases: ['Braze', 'iOS', 'Android', 'SDK']
|
|
327
247
|
}
|
|
328
248
|
```
|
|
329
249
|
|
|
330
|
-
|
|
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).
|
|
250
|
+
### Translation Glossary
|
|
334
251
|
|
|
335
|
-
|
|
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:
|
|
252
|
+
Create `translationGlossaryTerm` documents for specific term mappings:
|
|
342
253
|
|
|
343
254
|
```typescript
|
|
344
255
|
{
|
|
345
256
|
_type: 'translationGlossaryTerm',
|
|
346
257
|
translations: [
|
|
347
|
-
{
|
|
348
|
-
{
|
|
349
|
-
{
|
|
258
|
+
{ locale: 'en', value: 'dashboard' },
|
|
259
|
+
{ locale: 'es', value: 'panel de control' },
|
|
260
|
+
{ locale: 'fr', value: 'tableau de bord' },
|
|
261
|
+
{ locale: 'de', value: 'Dashboard' }
|
|
350
262
|
]
|
|
351
263
|
}
|
|
352
264
|
```
|
|
353
265
|
|
|
354
|
-
|
|
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:
|
|
266
|
+
### Give Translation Feedback
|
|
380
267
|
|
|
381
|
-
|
|
268
|
+
The plugin includes an AI-powered feedback system that learns from corrections:
|
|
382
269
|
|
|
383
|
-
|
|
270
|
+
1. Open a translated document
|
|
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
|
|
384
279
|
|
|
385
|
-
|
|
280
|
+
The AI analyzes your feedback and automatically updates translation settings to improve future translations.
|
|
386
281
|
|
|
387
|
-
|
|
282
|
+
## Translation Workflow
|
|
388
283
|
|
|
389
|
-
|
|
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
|
|
284
|
+
### How It Works
|
|
395
285
|
|
|
396
|
-
|
|
286
|
+
1. **Fetch Metadata** - Retrieves translation history and existing translations
|
|
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
|
|
397
294
|
|
|
398
|
-
|
|
399
|
-
- `vttCaptionTransform` for VTT captions
|
|
400
|
-
- `wistiaCaptionTransform` for Wistia captions (if used by your schema)
|
|
295
|
+
### Reference Translation
|
|
401
296
|
|
|
402
|
-
|
|
297
|
+
When "Translate references" is enabled:
|
|
403
298
|
|
|
404
299
|
```typescript
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
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
|
-
});
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**Order matters:** first match wins.
|
|
429
|
-
|
|
430
|
-
### Concurrency and depth
|
|
431
|
-
|
|
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).
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
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:
|
|
300
|
+
// Example: Post with author reference
|
|
301
|
+
{
|
|
302
|
+
_type: 'post',
|
|
303
|
+
title: 'My Post',
|
|
304
|
+
author: { _ref: 'author-en' } // Original English author
|
|
305
|
+
}
|
|
448
306
|
|
|
449
|
-
|
|
450
|
-
|
|
307
|
+
// After translation with references enabled:
|
|
308
|
+
{
|
|
309
|
+
_type: 'post',
|
|
310
|
+
language: 'es',
|
|
311
|
+
title: 'Mi Publicación',
|
|
312
|
+
author: { _ref: 'author-es' } // Now points to Spanish author
|
|
313
|
+
}
|
|
451
314
|
```
|
|
452
315
|
|
|
453
|
-
|
|
316
|
+
The plugin:
|
|
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
|
|
454
321
|
|
|
455
|
-
|
|
322
|
+
## Publishing
|
|
456
323
|
|
|
457
|
-
|
|
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'`)
|
|
324
|
+
This package is configured for internal publishing to the Braze npm registry.
|
|
461
325
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
|
487
|
-
pnpm build
|
|
326
|
+
```bash
|
|
327
|
+
# Login to registry
|
|
328
|
+
npm login --scope=@braze --registry=https://registry.npmjs.org/
|
|
488
329
|
|
|
330
|
+
# Build and publish
|
|
489
331
|
cd package
|
|
490
|
-
pnpm
|
|
491
|
-
|
|
492
|
-
pnpm lint
|
|
332
|
+
pnpm build
|
|
333
|
+
npm publish
|
|
493
334
|
```
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
## License
|
|
498
|
-
|
|
499
|
-
MIT
|