npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sanity-plugin-ai-translation

v0.2.79

Published

A Sanity Studio plugin that adds an AI-powered **Translate** document action for internationalized content.

Readme

@braze/sanity-plugin-ai-translation

A Sanity Studio plugin that adds an AI-powered Translate document action for internationalized content.

  • Default provider: Sanity Agent Actions (client.agent.action.translate())
  • Optional provider: OpenAI GPT-5 (direct API) for large documents / long style guides
  • Output: Translations are created as versioned documents inside Content Releases (staged publishing)
  • Extras: translate referenced documents, glossary terms, protected phrases, debug logging, field exclusion, and field transforms (Portable Text + captions)

For editors: see the standalone user guide at docs/user-guide.md.


Table of contents


What this plugin does

When you open a document in Sanity Studio (for a configured schema type), the plugin adds a Translate action. You can:

  1. Select one or more target languages
  2. Choose whether to:
    • overwrite existing translations
    • translate only diffs (when applicable)
    • translate referenced documents (with depth control)
  3. Run translation using:
    • Sanity Agent Actions (default), or
    • OpenAI (optional)

Translations are written as version documents in a Content Release, so you can review and publish them on your schedule.


Prerequisites

Your Studio must already support internationalized documents.

Required:

  • Sanity Studio v4 (plugin is built for Sanity Studio v4)
  • @sanity/document-internationalization configured
  • sanity-plugin-internationalized-array installed and configured (used for glossary term translations)
  • @sanity/client 7.1.0+ (required for Agent Actions)

Also required for Agent Actions:

  • Your schema must be deployed to Sanity so Agent Actions can access it (see Deploy schema)
  • Important API version requirement: the client must use apiVersion: 'vX' (not date-based versions). Date-based versions can cause Agent Actions to fail.

Install

Use your Studio package manager (examples use pnpm):

pnpm add @braze/sanity-plugin-ai-translation

You’ll also need the prerequisites if you haven’t installed them yet:

pnpm add @sanity/document-internationalization sanity-plugin-internationalized-array
pnpm add @sanity/client@^7.1.0

Configure (Studio)

1) Configure document internationalization

This plugin is designed to work with @sanity/document-internationalization. Configure it first.

You’ll typically define your supported languages as:

const SUPPORTED_LANGUAGES = [
  { id: "en-us", title: "English" },
  { id: "es", title: "Spanish" },
  { id: "fr", title: "French" },
];

Then enable internationalization for the schema types you want to translate:

documentInternationalization({
  supportedLanguages: SUPPORTED_LANGUAGES,
  schemaTypes: ["post", "page"],
  languageField: "language", // your language field name (example)
});

2) Add the AI translation plugin

Add aiTranslationPlugin() after documentInternationalization():

import { defineConfig } from "sanity";
import { documentInternationalization } from "@sanity/document-internationalization";
import { aiTranslationPlugin } from "@braze/sanity-plugin-ai-translation";

const SUPPORTED_LANGUAGES = [
  { id: "en", title: "English" },
  { id: "es", title: "Spanish" },
  { id: "fr", title: "French" },
];

export default defineConfig({
  // ...your config...
  plugins: [
    documentInternationalization({
      supportedLanguages: SUPPORTED_LANGUAGES,
      schemaTypes: ["post", "page"],
      languageField: "language",
    }),

    aiTranslationPlugin({
      supportedLanguages: SUPPORTED_LANGUAGES,
      translatableSchemaTypes: ["post", "page"],
      languageFieldName: "language",
      schemaId: "_.schemas.default",
      maxConcurrentTranslationJobs: 5,
      maxReferenceDepth: 3,
    }),
  ],
});

Notes

  • schemaId is usually '_.schemas.default' for most projects.
  • languageFieldName must match the field your documents use (often language).

3) Add translation history metadata

This plugin can store translation history via a metadata schema you attach to documentInternationalization.

Add translationHistorySchema:

import { translationHistorySchema } from "@braze/sanity-plugin-ai-translation";

documentInternationalization({
  supportedLanguages: SUPPORTED_LANGUAGES,
  schemaTypes: ["post", "page"],
  languageField: "language",
  metadataFields: [translationHistorySchema],
});

4) Deploy schema for Agent Actions

Agent Actions need access to your deployed schema.

From your Studio project directory:

npx sanity deploy

If Agent Actions fail with schema-related errors, re-check:

  • schema deployment
  • schemaId
  • client apiVersion is set to 'vX' (not date-based)

Optional: OpenAI mode

OpenAI mode is optional. If enabled, users can choose OpenAI as the translation provider inside the translation dialog.

Example configuration:

aiTranslationPlugin({
  supportedLanguages: SUPPORTED_LANGUAGES,
  translatableSchemaTypes: ["post", "page"],
  languageFieldName: "language",
  schemaId: "_.schemas.default",

  openai: {
    enabled: true,
    apiKey: process.env.OPENAI_API_KEY!,
    model: "gpt-5",
    reasoningEffort: "medium", // 'minimal' | 'low' | 'medium' | 'high'
    verbosity: "medium", // 'low' | 'medium' | 'high' (if supported by model)
    defaultMode: "openai-only", // 'sanity-only' | 'openai-only'
  },
});

Security

  • Do not hardcode API keys.
  • Use environment variables (e.g. .env / .env.local) and your deployment platform’s secret manager.

Why use OpenAI mode?

  • Larger effective context window for long documents and long style guides
  • Deterministic serialization/deserialization to preserve structure
  • Strict structure validation to prevent data corruption

Costs

  • OpenAI calls incur API costs based on token usage.

User guide (for editors)

Translate a document

  1. Open a document in Studio whose type is included in translatableSchemaTypes.
  2. Open the document actions menu and click Translate.
  3. Select one or more target languages.
  4. Configure translation options (see below).
  5. Click Translate to start.

While translation runs, you’ll see job statuses for each target language (and for any referenced documents, if enabled).

Choose a Content Release

Translations are always created inside a Content Release.

In the dialog you can typically:

  • create a new release, or
  • add the translations to an existing active release

Use releases when you want:

  • staged review
  • coordinated publishing of multiple translations at once

Translation options

The exact UI labels may vary, but the workflow supports:

  • Overwrite fresh translations
    • Forces re-translation even if the existing translation is considered up-to-date.
  • Translate only diffs
    • For stale translations, translate only the changed fields where supported.
  • Translate references
    • Also translate referenced documents and update references to point at the localized versions.
  • Max reference depth
    • How far to traverse reference graphs (depth 0 means no traversal).

Translate referenced documents

If you enable reference translation:

  • The plugin traverses references (up to the configured depth).
  • It translates referenced documents that are eligible (type is included in translatableSchemaTypes).
  • It updates references in the translated document to point to localized versions.

This is useful for content like:

  • posts referencing authors
  • pages referencing shared sections
  • documents referencing related content

What gets created

For each selected target language, the plugin creates a versioned document inside the selected Content Release.

You can then:

  • review and edit translated versions
  • publish the release when ready

Content configuration (settings + glossary)

The plugin introduces two document types for configuration:

translationSettings

Create a document with _id == "translation-settings" to configure:

  • Global prompt (applies to all translations)
  • Locale prompts (per-target-language instructions)
  • Protected phrases (never translate these terms; e.g. brand names)

Example shape:

{
  _type: 'translationSettings',
  _id: 'translation-settings',
  globalPrompt: 'Maintain a professional tone. Preserve formatting and links.',
  localePrompts: [
    {_key: 'es', value: 'Use formal Spanish (usted).'},
    {_key: 'fr', value: 'Use formal French.'},
  ],
  protectedPhrases: ['Braze', 'iOS', 'Android', 'SDK']
}

Notes:

  • localePrompts uses _key for the locale id (must match your supportedLanguages[].id).
  • Protected phrases may be filtered to only those that appear in the source document (to keep prompts smaller).

translationGlossaryTerm

Create glossary term documents to enforce consistent translations of key terms.

This schema uses internationalizedArrayString so you can provide one value per locale.

Conceptual example:

{
  _type: 'translationGlossaryTerm',
  translations: [
    {_key: 'en', value: 'dashboard'},
    {_key: 'es', value: 'panel de control'},
    {_key: 'fr', value: 'tableau de bord'},
  ]
}

Glossary terms are used to build locale-specific translation instructions.


Advanced configuration

Exclude fields from translation

Use excludeFields to prevent certain fields from being translated (slugs, enums, flags, etc.).

Supports:

  • field name matches anywhere (e.g. 'status')
  • exact paths (e.g. 'seo.title')
  • array paths / wildcards (e.g. 'body[*].imageType')

Example:

aiTranslationPlugin({
  // ...
  excludeFields: ["slug", "status", "seo.canonicalUrl", "body[*].variant"],
});

Important behavior:

  • The plugin will not allow the languageFieldName to be excluded. If you include it, it will be removed to prevent translation errors.

Field transforms

Some fields (like Portable Text or caption formats) benefit from transformation before translation.

Transforms run in this sequence:

  1. Filter excluded fields
  2. Apply transforms (to produce a translatable representation)
  3. Translate (Sanity Agent or OpenAI)
  4. Restore transforms (back into original structure)
  5. Restore excluded fields (except language)
  6. Create version document in the release

Built-in transforms include:

  • richTextTransform for Portable Text
  • vttCaptionTransform for VTT captions
  • wistiaCaptionTransform for Wistia captions (if used by your schema)

Example setup:

import {
  aiTranslationPlugin,
  richTextTransform,
  vttCaptionTransform,
  wistiaCaptionTransform,
} from "@braze/sanity-plugin-ai-translation";

aiTranslationPlugin({
  // ...
  fieldTransforms: [
    {
      ...vttCaptionTransform,
      matcher: { schemaType: "video", fieldName: "captions" },
    },
    {
      ...wistiaCaptionTransform,
      matcher: { schemaType: "videoDoc", fieldName: "wistiaCaptions" },
    },
    richTextTransform,
  ],
});

Order matters: first match wins.

Concurrency and depth

  • maxConcurrentTranslationJobs controls how many translations run at the same time (default behavior is conservative).
  • maxReferenceDepth controls how deeply references are traversed (set to 0 to disable traversal).

Debugging & troubleshooting

Enable debug logging

This plugin uses a centralized debug logger. To enable debug output in your browser:

localStorage.setItem("sanity-ai-translation:debug", "true");

To disable:

localStorage.removeItem("sanity-ai-translation:debug");

Common issues

Agent Actions fail immediately

  • Confirm @sanity/client is 7.1.0+
  • Confirm your client API version is apiVersion: 'vX' (not date-based)
  • Confirm schema is deployed: npx sanity deploy
  • Confirm schemaId matches your deployed schema (commonly '_.schemas.default')

Translation completes but content didn’t change

  • Ensure the document type is included in translatableSchemaTypes
  • Check whether excluded fields or transforms are preventing updates
  • If using OpenAI mode, structure validation can reject translations that modify non-text fields

Reference translation is slow

  • Reduce maxReferenceDepth
  • Reduce maxConcurrentTranslationJobs
  • Ensure you’re not translating large reference graphs unnecessarily

Development (this repo)

This repository is a pnpm monorepo with:

  • package/ — the plugin package
  • demo-nextjs/ — demo Next.js app with embedded Studio

Common commands:

pnpm dev
pnpm build

cd package
pnpm test
pnpm typecheck
pnpm lint

License

MIT