@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 CHANGED
@@ -1,334 +1,499 @@
1
1
  # @braze/sanity-plugin-ai-translation
2
2
 
3
- > A Sanity Studio v3 plugin that provides AI-powered translation capabilities for internationalized content using Sanity Agent Actions.
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
- ## Features
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
- - 🤖 **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
72
+ Also required for Agent Actions:
16
73
 
17
- ## Installation
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
- ## Prerequisites
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
- This plugin requires:
102
+ You’ll typically define your supported languages as:
26
103
 
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
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
- ## Configuration
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
- ### Basic Setup
122
+ ### 2) Add the AI translation plugin
35
123
 
36
- Add the plugin to your `sanity.config.ts` file:
124
+ Add `aiTranslationPlugin()` after `documentInternationalization()`:
37
125
 
38
126
  ```typescript
39
- import { defineConfig } from 'sanity'
40
- import { documentInternationalization } from '@sanity/document-internationalization'
41
- import { aiTranslationPlugin } from '@braze/sanity-plugin-ai-translation'
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: 'en', title: 'English' },
45
- { id: 'es', title: 'Spanish' },
46
- { id: 'fr', title: 'French' },
47
- { id: 'de', title: 'German' },
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
- // ... other config
138
+ // ...your config...
52
139
  plugins: [
53
- // Document internationalization must be configured first
54
140
  documentInternationalization({
55
141
  supportedLanguages: SUPPORTED_LANGUAGES,
56
- schemaTypes: ['post', 'page', 'product'],
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
- // Required: Document types that can be translated
65
- translatableSchemaTypes: ['post', 'page', 'product'],
158
+ **Notes**
66
159
 
67
- // Required: Field name containing the language identifier
68
- languageFieldName: 'language',
160
+ - `schemaId` is usually `'_.schemas.default'` for most projects.
161
+ - `languageFieldName` must match the field your documents use (often `language`).
69
162
 
70
- // Required: Schema ID for Agent Actions (usually '_.schemas.default')
71
- schemaId: '_.schemas.default',
163
+ ### 3) Add translation history metadata
72
164
 
73
- // Optional: Maximum concurrent translation jobs (default: 5)
74
- maxConcurrentTranslationJobs: 5,
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
- ### Configuration Options
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
- | 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) |
196
+ ---
90
197
 
91
- ### OpenAI Integration (Optional)
198
+ ## Optional: OpenAI mode
92
199
 
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.
200
+ OpenAI mode is optional. If enabled, users can choose **OpenAI** as the translation provider inside the translation dialog.
94
201
 
95
- #### Setup
202
+ Example configuration:
96
203
 
97
204
  ```typescript
98
205
  aiTranslationPlugin({
99
- // ... other required config
206
+ supportedLanguages: SUPPORTED_LANGUAGES,
207
+ translatableSchemaTypes: ["post", "page"],
208
+ languageFieldName: "language",
209
+ schemaId: "_.schemas.default",
210
+
100
211
  openai: {
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
- })
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
- **⚠️ Security Note:** Never commit API keys to version control. Always use environment variables:
222
+ **Security**
112
223
 
113
- ```bash
114
- # .env.local
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
- #### Translation Modes
227
+ **Why use OpenAI mode?**
119
228
 
120
- When OpenAI is enabled, users can select from three translation modes:
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
- 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
233
+ **Costs**
127
234
 
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
235
+ - OpenAI calls incur API costs based on token usage.
133
236
 
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
237
+ ---
139
238
 
140
- #### GPT-5 Features
239
+ ## User guide (for editors)
141
240
 
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
241
+ ### Translate a document
147
242
 
148
- #### Cost Considerations
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
- 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
249
+ While translation runs, you’ll see job statuses for each target language (and for any referenced documents, if enabled).
154
250
 
155
- #### OpenAI Configuration Options
251
+ ### Choose a Content Release
156
252
 
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 |
253
+ Translations are always created inside a **Content Release**.
165
254
 
166
- ### Schema Setup
255
+ In the dialog you can typically:
167
256
 
168
- The plugin requires you to add the `translationHistory` schema to your document internationalization configuration:
257
+ - create a new release, or
258
+ - add the translations to an existing active release
169
259
 
170
- ```typescript
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
- // 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
- ```
262
+ - staged review
263
+ - coordinated publishing of multiple translations at once
182
264
 
183
- ### Deploy Schema
265
+ ### Translation options
184
266
 
185
- After configuration, deploy your schema so Agent Actions can access it:
267
+ The exact UI labels may vary, but the workflow supports:
186
268
 
187
- ```bash
188
- cd your-sanity-project
189
- npx sanity deploy
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
- ## Usage
292
+ ### What gets created
193
293
 
194
- ### Translation Action
294
+ For each selected target language, the plugin creates a **versioned document** inside the selected Content Release.
195
295
 
196
- Once configured, a "Translate" action will appear on all documents of types listed in `translatableSchemaTypes`.
296
+ You can then:
197
297
 
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
298
+ - review and edit translated versions
299
+ - publish the release when ready
207
300
 
208
- ### Translation States
301
+ ---
209
302
 
210
- The plugin tracks three translation states:
303
+ ## Content configuration (settings + glossary)
211
304
 
212
- - **Fresh** ✅ - Translation is up-to-date with source
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
- 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
307
+ ### `translationSettings`
220
308
 
221
- ### Translation Settings
309
+ Create a document with `_id == "translation-settings"` to configure:
222
310
 
223
- Create a `translationSettings` document to customize translation behavior:
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
- 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
- }
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
- ### Translation Glossary
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
- Create `translationGlossaryTerm` documents for specific term mappings:
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
- { locale: 'en', value: 'dashboard' },
259
- { locale: 'es', value: 'panel de control' },
260
- { locale: 'fr', value: 'tableau de bord' },
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
- ### Give Translation Feedback
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 includes an AI-powered feedback system that learns from corrections:
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
- 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
383
+ ### Field transforms
279
384
 
280
- The AI analyzes your feedback and automatically updates translation settings to improve future translations.
385
+ Some fields (like Portable Text or caption formats) benefit from transformation before translation.
281
386
 
282
- ## Translation Workflow
387
+ Transforms run in this sequence:
283
388
 
284
- ### How It Works
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
- 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
396
+ Built-in transforms include:
294
397
 
295
- ### Reference Translation
398
+ - `richTextTransform` for Portable Text
399
+ - `vttCaptionTransform` for VTT captions
400
+ - `wistiaCaptionTransform` for Wistia captions (if used by your schema)
296
401
 
297
- When "Translate references" is enabled:
402
+ Example setup:
298
403
 
299
404
  ```typescript
300
- // Example: Post with author reference
301
- {
302
- _type: 'post',
303
- title: 'My Post',
304
- author: { _ref: 'author-en' } // Original English author
305
- }
405
+ import {
406
+ aiTranslationPlugin,
407
+ richTextTransform,
408
+ vttCaptionTransform,
409
+ wistiaCaptionTransform,
410
+ } from "@braze/sanity-plugin-ai-translation";
306
411
 
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
- }
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
- 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
428
+ **Order matters:** first match wins.
321
429
 
322
- ## Publishing
430
+ ### Concurrency and depth
323
431
 
324
- This package is configured for internal publishing to the Braze npm registry.
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
- ```bash
327
- # Login to registry
328
- npm login --scope=@braze --registry=https://registry.npmjs.org/
435
+ ---
329
436
 
330
- # Build and publish
331
- cd package
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
- npm publish
488
+
489
+ cd package
490
+ pnpm test
491
+ pnpm typecheck
492
+ pnpm lint
334
493
  ```
494
+
495
+ ---
496
+
497
+ ## License
498
+
499
+ MIT