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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@equinor/document-internationalization

v3.0.0

Published

Create unique translations of a document based on its language, joined by a shared reference document.

Downloads

264

Readme

@equinor/document-internationalization

All new rewrite exclusively for Sanity Studio v3

v3 Studio with @equinor/document-internationalization v1 Installed

What this plugin solves

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.metadata document
    • Best for documents that have unique, language-specific fields and no common content across languages
    • Best for translating content using Portable Text
  • 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
    • Not recommended for Portable Text

This plugin adds features to the Studio to improve handling document-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

For field-level translations you should use the sanity-plugin-internationalized-array.

Many projects use both

An example of document-level translation could be a lesson schema, the title, slug and content fields would be unique in every language.

A good use of field-level translation could be a person schema. It could have the same name and image in every language, but only the biography would need translating.

Upgrade

See the upgrade guide for instructions on how to upgrade from the previous version of Document Internationalization.

Install

npm install --save @equinor/document-internationalization

Usage

Add it as a plugin in sanity.config.ts (or .js):

Basic configuration

The only required configuration is:

  • The supportedLanguages array and
  • The schemaTypes array
// sanity.config.ts

import {createConfig} from 'sanity'
import {documentInternationalization} from '@equinor/document-internationalization'

export const createConfig({
  // ... all other config
  plugins: [
    // ... all other plugins
    documentInternationalization({
      // Required configuration
      supportedLanguages: [
        {id: 'es', title: 'Spanish'},
        {id: 'en', title: 'English'}
      ],
      schemaTypes: ['lesson'],
    })
  ]
})

Advanced configuration

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.ts

import {createConfig} from 'sanity'
import {documentInternationalization} from '@sanity/document-internationalization'

export const createConfig({
  // ... all other config
  plugins: [
    // ... all other plugins
    documentInternationalization({
      // 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
      // supportedLanguages: (client) => client.fetch(`*[_type == "language"]{id, title}`),

      // Required
      // Translations UI will only appear on these schema types
      schemaTypes: ['lesson'],

      // Optional
      // Customizes the name of the language field
      languageField: `language` // defauts to "language"

      // Optional
      // Keep translation.metadata references weak
      weakReferences: 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-api
      bulkPublish: true // defaults to false

      // Optional
      // Adds additional fields to the metadata document
      metadataFields: [
        defineField({ name: 'slug', type: 'slug' })
      ],

      // Optional
      // Define API Version for all queries
      // https://www.sanity.io/docs/api-versioning
      apiVersion: '2023-05-22'
    })
  ]
})

Language field

The schema types that use document internationalization must also have a string field type with the same name configured in the languageField 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 settings
defineField({
  // should match 'languageField' plugin configuration setting, if customized
  name: 'language',
  type: 'string',
  readOnly: true,
  hidden: true,
})

Excluding fields

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 with Sanity 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 with options.documentInternationalization.exclude:

defineField({
  name: 'title',
  title: 'Title',
  type: 'string',
  options: {
    documentInternationalization: {
      exclude: true,
    },
  },
}),

Querying translations

Querying with GROQ

To query a single document and all its translations, we use the references() 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
  },
}

Querying with GraphQL

Fortunately, the Sanity GraphQL API contains a similar filter for document references.

# In this example we retrieve a lesson by its `slug.current` field value
query GetLesson($language: String!, $slug: String!) {
  allLesson(
    limit: 1
    where: {language: {eq: $language}, slug: {current: {eq: $slug}}}
  ) {
    _id
    title
    language
    slug {
      current
    }
  }
}

# And then can run this query to find translation metadata documents that use its ID
query GetTranslations($id: ID!) {
  allTranslationMetadata(where: {_: {references: $id}}) {
    translations {
      _key
      value {
        title
        slug {
          current
        }
      }
    }
  }
}

Note on document quotas

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 type translation.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.

Documentation

For more advanced topics see the documentation. For installation see Usage.

License

MIT © Sanity.io

Develop & test

This plugin uses @sanity/plugin-kit with default configuration for build & watch scripts.

See Testing a plugin in Sanity Studio on how to run this plugin with hot reload in the studio.

Release new version

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.