- Notifications
You must be signed in to change notification settings - Fork41
Create unique translations of a document based on its language, joined by a shared reference document
License
sanity-io/document-internationalization
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
All new rewrite exclusively for Sanity Studio v3
There are two popular methods of internationalization in Sanity Studio:
- Document-level translation
- A unique document version for every language
- Joined together by references in a
translation.metadatadocument - Best for documents that have unique, language-specific fields and no common content across languages
- Field-level translation
- A single document with many languages of content
- Achieved by mapping over languages on each field
- Best for documents that have a mix of language-specific and common fields
This plugin adds features to the Studio to improve handlingdocument-level translations.
- A Language Selector to create and browse language-specific versions of a Document
- Hooks and components to use throughout your custom components to handle translations
- Document Badges to highlight the language version of a document
Forfield-level translations you should use thesanity-plugin-internationalized-array.
An example ofdocument-level translation could be alesson schema, thetitle,slug andcontent fields would be unique in every language.
A good use offield-level translation could be aperson schema. It could have the samename andimage in every language, but only thebiography would need translating.
See the upgrade guide for instructions on how to upgrade from the previous version of Document Internationalization.
npm install --save @sanity/document-internationalizationAdd it as a plugin insanity.config.ts (or .js):
The only required configuration is:
- The
supportedLanguagesarray and - The
schemaTypesarray
// sanity.config.tsimport{defineConfig}from'sanity'import{documentInternationalization}from'@sanity/document-internationalization'exportconstdefineConfig({// ... all other configplugins:[// ... all other pluginsdocumentInternationalization({// Required configurationsupportedLanguages:[{id:'es',title:'Spanish'},{id:'en',title:'English'}],schemaTypes:['lesson'],})]})
The plugin also supports asynchronously retrieving languages from the dataset, modifying the language field, adding a bulk publishing feature and adding additional fields to the metadata document.
// sanity.config.tsimport{defineConfig}from'sanity'import{documentInternationalization}from'@sanity/document-internationalization'exportconstdefineConfig({// ... all other configplugins:[// ... all other pluginsdocumentInternationalization({// Required// Either: an array of supported languages...supportedLanguages:[{id:'nb',title:'Norwegian (Bokmål)'},{id:'nn',title:'Norwegian (Nynorsk)'},{id:'en',title:'English'}],// ...or a function that takes the client and returns a promise of an array of supported languages// MUST return an "id" and "title" as strings// Note: Async language configuration cannot create templates for new documents// supportedLanguages: (client) => client.fetch(`*[_type == "language"]{id, title}`),// Required// Translations UI will only appear on these schema typesschemaTypes:['lesson'],// Optional// Customizes the name of the language fieldlanguageField:`language`,// defauts to "language"// Optional// Keep translation.metadata references weakweakReferences:true,// defaults to false// Optional// Adds UI for publishing all translations at once. Requires access to the Scheduling API// https://www.sanity.io/docs/scheduling-apibulkPublish:true,// defaults to false// Optional// Adds additional fields to the metadata documentmetadataFields:[defineField({name:'slug',type:'slug'}),],// Optional// Define API Version for all queries// https://www.sanity.io/docs/api-versioningapiVersion:'2025-02-19',// Optional// Enable "manage translations" button without creating a translated version. Helpful if you have// pre-existing documents that you need to tie together through the metadata documentallowCreateMetaDoc:true,// defaults to false// Optional// Callback function that runs after a translation document has been created// Note: Defaults to nullcallback:({ sourceDocument,// The document in the original language newDocument,// The newly created translation of the source document sourceLanguageId,// The id of the original language destinationLanguageId,// The id of the destination language metaDocumentId,// The id of the meta document referencing the document translations client// Sanity client}){// Your function implementation}})]})
The schema types that use document internationalization must also have astring field type with the same name configured in thelanguageField setting. Unless you want content creators to be able to change the language of a document, you may hide or disable this field since the plugin will handle writing patches to it.
// ./schema/lesson.ts// ...all other settingsdefineField({// should match 'languageField' plugin configuration setting, if customizedname:'language',type:'string',readOnly:true,hidden:true,})
The default behaviour of this plugin when creating a new translation is to duplicate the originating document, which is useful for then translating the fields directly in the new document - perhaps withSanity AI Assist. However, sometimes you may want to exclude certain fields from being copied to the new document. You can do this by updating your schema to exclude certain types or fields withoptions.documentInternationalization.exclude:
defineField({name:'title',title:'Title',type:'string',options:{documentInternationalization:{exclude:true,},},}),
To query a single document and all its translations, we use thereferences() function in GROQ.
// All `lesson` documents of a single language*[_type=="lesson"&&language==$language]{title,slug,language,// Get the translations metadata// And resolve the `value` reference field in each array item"_translations":*[_type=="translation.metadata"&&references(^._id)].translations[].value->{ title, slug, language},}
Fortunately, the Sanity GraphQL API contains a similar filter for document references.
# In this example we retrieve a lesson by its `slug.current` field valuequeryGetLesson($language:String!,$slug:String!) {allLesson(limit:1where: {language: {eq:$language},slug: {current: {eq:$slug}}} ) {_idtitlelanguageslug {current } }}# And then can run this query to find translation metadata documents that use its IDqueryGetTranslations($id:ID!) {allTranslationMetadata(where: {_: {references:$id}}) {translations {_keyvalue {titleslug {current } } } }}
In previous versions of this plugin, translations were stored as an array of references on the actual documents. This required a base language, lead to messy transaction histories and made deleting documents difficult.
In this version of the plugin, translations of a document are stored as an array of references in a separate document of the typetranslation.metadata, and one is created for every document that has translations. A document with no translations will not have a metadata document.
This means if you have 100 documents and they are all translated into 3 languages, you will have 400 documents. Keep this in mind for extremely high-volume datasets.
For more advanced topics see the documentation. For installation seeUsage.
- Upgrade from v1
- Creating translations of singleton documents
- Importing and creating documents
- Deleting translated documents
- Importing plugin components
- Allowing the same slug on different language versions
- Remove default new document template
MIT © Sanity.io
This plugin uses@sanity/plugin-kitwith default configuration for build & watch scripts.
SeeTesting a plugin in Sanity Studioon how to run this plugin with hot reload in the studio.
Run"CI & Release" workflow.Make sure to select the main branch and check "Release new version".
Semantic release will only release on configured branches, so it is safe to run the release on any branch.
About
Create unique translations of a document based on its language, joined by a shared reference document
Resources
License
Code of conduct
Security policy
Uh oh!
There was an error while loading.Please reload this page.
