Display localized field titles in Sanity Studio with custom schema

4 replies
Last updated: Jan 27, 2021
Problems with locale fields
Working with localization, and first tried the intl-plugin, but couldn't get it to work, so figured I'd go with standard field level translations by following the docs, but I can't figure out how to display the correct title for the post in the studio. Right now the title looks like this (image).

I kind of understand how it would work if I followed the docs and had the localized string in the same doc as the page schema, but is there any way I can pass down the title from my localeString.js to my page.js schema type?

Providing my localeString.js as well as my page.js in the thread
AI Update

I can help you with displaying localized titles in Sanity Studio! The key is using the preview configuration with the prepare function to extract and display the correct language from your localized string object.

When you're using field-level translations, your localized strings are stored as objects (like {en: "English Title", fr: "French Title"}). To display these properly in Studio lists and references, you need to configure the preview in your schema to extract the appropriate language.

Here's how to handle this in your page.js schema:

// page.js
import { localeString } from './localeString'

export default {
  name: 'page',
  type: 'document',
  fields: [
    {
      name: 'title',
      type: 'localeString', // your custom localized string type
    },
    // ... other fields
  ],
  preview: {
    select: {
      title: 'title', // This will select the entire localeString object
    },
    prepare({ title }) {
      // Extract the language you want to display (e.g., 'en')
      // You can make this dynamic based on user preference
      return {
        title: title?.en || title?.fr || 'Untitled', // Fallback through languages
      }
    }
  }
}

If you want to make it more sophisticated and show which languages have translations, you could do:

preview: {
  select: {
    title: 'title',
  },
  prepare({ title }) {
    const languages = Object.keys(title || {}).join(', ')
    return {
      title: title?.en || Object.values(title || {})[0] || 'Untitled',
      subtitle: languages ? `Available in: ${languages}` : 'No translations'
    }
  }
}

The preview configuration works by:

  1. select: Grabs the fields you need from the document
  2. prepare: Transforms that data into what Studio displays (title, subtitle, media)

Since your localeString is a custom type, the preview on the page document needs to handle the object structure that type creates. The prepare function is where you "unwrap" the localized object and choose which language to display.

This approach is cleaner than trying to pass down the title from localeString.js because each document type controls its own preview display, and the page needs to decide how to present its localized content in lists.

If you're still having issues, make sure your localeString.js type is properly structured as an object type with fields for each language you're supporting. You can find more details about field-level localization in the Sanity docs.

Show original thread
4 replies
localeString.js
const supportedLanguages = [
  { id: 'en', title: 'English', isDefault: true },
  { id: 'se', title: 'Swedish' },
];

export default {
  title: 'Localized string',
  name: 'localeString',
  type: 'object',
  // Fieldsets can be used to group object fields.
  // Here we omit a fieldset for the "default language",
  // making it stand out as the main field.
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
      options: { collapsible: true },
    },
  ],
  // Dynamically define one field per language
  fields: supportedLanguages.map((lang) => ({
    title: lang.title,
    name: lang.id,
    type: 'string',
    fieldset: lang.isDefault ? null : 'translations',
  })),
};
--

page.js

import { FaFileAlt } from 'react-icons/fa';

export default {
  name: 'page',
  title: 'Pages',
  type: 'document',
  icon: FaFileAlt,
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'localeString',
    },

    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96,
      },
    },

    {
      name: 'date',
      title: 'Date',
      type: 'date',
      options: {
        dateFormat: 'YYYY-MM-DD',
        calendarTodayLabel: 'Set current date',
      },
    },

    {
      name: 'pageContent',
      title: 'Content',
      type: 'blockContent',
    },
  ],
};
something like this in page.js

{
   ...,
   preview: {
    select: {
      title: "title.en",
    },
  },
}
Well shit it really was that simple...
Hahaha thanks a ton! Much appreciated

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?