# General Translation Integrations: Configuring Sanity URL: https://generaltranslation.com/en-GB/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 localisation, 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 in 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` takes precedence. ## Add a language field [#language-field] 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`. ```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 localisation [#field-level] By default, `gt-sanity` translates at the document level, creating one document per locale. Field-level localisation stores each locale's value in the same document as an internationalised 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', }); ``` The plugin generates `internationalizedArray*` schema types from `sourceLocale` and `locales`, together with an inline per-locale editing UI. Use the generated types in your schemas. ```ts defineField({ name: 'title', type: 'internationalizedArrayString', }); ``` By default, the plugin generates `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' }], }, ``` 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 localised 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. ## 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 Translations 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