# General Translation Integrations: Configuring Sanity URL: https://generaltranslation.com/en-US/docs/integrations/sanity/guides/configuring-sanity.mdx --- title: Configuring Sanity description: "How to configure the General Translation gt-sanity plugin: this guide covers locales, document filters, field-level localization, field exclusion, credentials, language fields, and singletons." related: links: - /docs/integrations/sanity/guides/translating-content - /docs/integrations/sanity/guides/managing-translations - /docs/integrations/sanity/guides/querying-translations --- Configure how General Translation works inside Sanity Studio with the `gtPlugin` function. This guide covers the most common options. For the complete list, see the [plugin configuration reference](/docs/integrations/sanity/reference/plugin-configuration). ## Add the plugin [#add-plugin] Add `gtPlugin` to your Studio config. This step is also covered in the [Quickstart](/docs/integrations/sanity/quickstart). ```ts title="sanity.config.ts" import { defineConfig } from 'sanity'; import { gtPlugin } from 'gt-sanity'; export default defineConfig({ plugins: [ gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], translateDocuments: [{ type: 'article' }, { type: 'page' }], }), ], }); ``` ## Set source and target locales [#set-locales] Use `sourceLocale` for the source language and `locales` for the target languages. ```ts gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], }); ``` If you already have a `gt.config.json`, you can spread it into the plugin config. `defaultLocale` is accepted as an alias for `sourceLocale`. ```ts title="sanity.config.ts" import gtConfig from './gt.config.json'; gtPlugin({ ...gtConfig, }); ``` The source locale resolves in this order: `sourceLocale`, then `defaultLocale`, then the library default. If both `sourceLocale` and `defaultLocale` are set, `sourceLocale` wins. ## Add a language field [#language-field] Document-level translations are stored as separate documents. The plugin uses a language field to record each document's locale. By default the field is named `language`. Field-level localization does not use this field. ```ts title="schema/article.ts" import { defineField, defineType } from 'sanity'; export const articleType = defineType({ name: 'article', title: 'Article', type: 'document', fields: [ defineField({ name: 'language', type: 'string', readOnly: true, hidden: true, }), ], }); ``` To use a different field name, set `languageField` and use the same name in your schema. ```ts gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], languageField: 'locale', }); ``` ## Choose which documents to translate [#choose-documents] Use `translateDocuments` to filter which documents can be translated. Accepts document type filters, document ID filters, or shorthand type strings. ```ts // By document type gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], translateDocuments: [{ type: 'page' }, { type: 'post' }], }); ``` ```ts // By specific document ID gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], translateDocuments: [{ documentId: 'homepage' }, { documentId: 'about-page' }], }); ``` ```ts // Shorthand type strings gtPlugin({ sourceLocale: 'en', locales: ['es'], translateDocuments: ['article', 'page'], }); ``` String entries are treated as `{ type: '' }`. `showDocumentInternationalization` uses the `type` entries to decide which schema types get language badges and templates, so document-type filters are required to enable those features. ## Configure field-level localization [#field-level] By default, `gt-sanity` translates at the document level, creating one document per locale. Field-level localization stores every locale's value inside the same document as an internationalized array (`[{ _key, _type, language, value }]` — the same shape as [`sanity-plugin-internationalized-array`](https://github.com/sanity-io/sanity-plugin-internationalized-array), so existing data needs no migration). Enable schema generation with `internationalizedArray` (or its alias, `fieldLevelLocalization`), then set `translationLevel` to `'internationalizedArray'`. ```ts gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], translateDocuments: [{ type: 'post' }], internationalizedArray: { enabled: true }, translationLevel: 'internationalizedArray', }); ``` Field-level localization is powered by [`sanity-plugin-internationalized-array`](https://github.com/sanity-io/sanity-plugin-internationalized-array), the reference Sanity plugin. `gtPlugin` configures it from `sourceLocale` and `locales`, and the native plugin registers the `internationalizedArray*` schema types and the Studio editing UI — per-language add buttons, language labels, and field actions all behave exactly as they do with the standalone plugin. Use the registered types in your schemas. ```ts defineField({ name: 'title', type: 'internationalizedArrayString', }); ``` By default, the plugin registers `string` and `text` types. Use `fieldTypes` to add `block` (Portable Text) or custom object definitions. ```ts internationalizedArray: { enabled: true, fieldTypes: ['string', 'text', 'block', { name: 'seo', type: 'seoFields' }], }, ``` If you already register `sanity-plugin-internationalized-array` in your Studio yourself, keep your setup and leave `internationalizedArray` unset so the schema types are only registered once. Translation reads and writes the stored `{ _key, _type, language, value }` data regardless of which plugin instance registered the types, so setting `translationLevel` (and `translateDocuments`) is all GT needs. ```ts import { internationalizedArray } from 'gt-sanity'; plugins: [ internationalizedArray({ languages: [ { id: 'en', title: 'English' }, { id: 'es', title: 'Spanish' }, ], fieldTypes: ['string'], }), gtPlugin({ sourceLocale: 'en', locales: ['es'], translateDocuments: [{ type: 'post' }], translationLevel: 'internationalizedArray', }), ], ``` To mix both strategies, set `translationLevel` to `'mixed'` and list the field-level document types in `fieldLevelDocuments`. ```ts gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], translateDocuments: [{ type: 'post' }, { type: 'siteSettings' }], internationalizedArray: { enabled: true }, translationLevel: 'mixed', fieldLevelDocuments: [{ type: 'siteSettings' }], }); ``` Document types localized in place are excluded automatically from `@sanity/document-internationalization`, so they do not receive language badges or per-locale document templates. During import, the plugin updates only the target locale in the source document and preserves every other language. See the [plugin configuration reference](/docs/integrations/sanity/reference/plugin-configuration#field-level) for every field-level option. ## Exclude fields from translation [#exclude-fields] Mark a field in your schema to keep its content out of translation. Set `options.gt.exclude` on the field definition. ```ts title="schema/article.ts" defineField({ name: 'internalNotes', type: 'string', options: { gt: { exclude: true } }, }); ``` Excluded fields are never sent for translation, so translated documents keep the source value unchanged. Exclusion applies at any nesting depth. The plugin also honors the exclusion options of the standard Sanity localization plugins, so if your schema already uses them you do not need to add a second mark: - `options.documentInternationalization.exclude` from `@sanity/document-internationalization` - `options.aiAssist.exclude` from `@sanity/assist` - the legacy `localize: false` field property To exclude every occurrence of a custom type, set the option on the type definition itself. ```ts title="schema/objects/legalDisclaimer.ts" export const legalDisclaimer = defineType({ name: 'legalDisclaimer', type: 'object', // Excludes this type everywhere it is used options: { gt: { exclude: true } }, fields: [ defineField({ name: 'jurisdiction', type: 'string' }), defineField({ name: 'text', type: 'text' }), ], }); ``` For rules that target documents by ID or transform values per locale — such as slug deduplication — use the plugin-level [`ignoreFields`, `skipFields`, and `dedupeFields`](/docs/integrations/sanity/reference/plugin-configuration#field-matchers) options instead. ## Configure singleton documents [#singletons] Use `singletons` for documents that exist once per site, such as site settings or navigation. `singletonMapping` controls how the translated singleton's document ID is derived from the source ID and locale. ```ts gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], singletons: ['siteSettings', 'navigation', 'footer'], singletonMapping: (sourceDocumentId, locale) => `${sourceDocumentId}-${locale}`, }); ``` If you omit `singletonMapping`, the default maps `sourceDocumentId` and `locale` to `` `${sourceDocumentId}-${locale}` `` (for example, `siteSettings-es`). ## Store credentials [#store-credentials] The plugin reads your API key and project ID from a private Sanity document whose `_id` matches `secretsNamespace` (default `generaltranslation.secrets`). Create it with a one-off script. ```js title="populateSecrets.js" import { getCliClient } from 'sanity/cli'; const client = getCliClient({ apiVersion: '2026-04-06' }); client.createOrReplace({ _id: 'generaltranslation.secrets', _type: 'generaltranslation.secrets', secret: process.env.GT_API_KEY, project: process.env.GT_PROJECT_ID, }); ``` ```bash GT_API_KEY=your-api-key GT_PROJECT_ID=your-project-id npx sanity exec populateSecrets.js --with-user-token ``` The `secret` field is used as the API key and the `project` field as the project ID. To read credentials from a different document, set `secretsNamespace` to that document's `_id`. You can also pass `apiKey` and `projectId` directly to `gtPlugin`, but the secrets document is recommended so credentials stay out of source control. When both are present, the secrets document takes precedence at runtime. ## Add the optional Translations tab [#translations-tab] The **Translate** document action is added automatically. To also show a translation tab inside the document editor, add `TranslationsTab` with `structureTool`. ```ts title="sanity.config.ts" import { defineConfig } from 'sanity'; import { structureTool } from 'sanity/structure'; import { gtPlugin, TranslationsTab } from 'gt-sanity'; export default defineConfig({ plugins: [ structureTool({ defaultDocumentNode: (S) => S.document().views([ S.view.form(), S.view.component(TranslationsTab).title('General Translation'), ]), }), gtPlugin({ sourceLocale: 'en', locales: ['es', 'zh', 'ja'], }), ], }); ``` ## Next steps - /docs/integrations/sanity/guides/translating-content - /docs/integrations/sanity/guides/managing-translations - /docs/integrations/sanity/guides/querying-translations