Where to place translation script file in Sanity project structure?

7 replies
Last updated: Apr 30, 2021
user A
trying to fire up your awesome translation gist - where should this file live in the sanity project ? Trying to run as a script in the root and getting errors https://gist.github.com/bjoerge/6c0b5bdcd316fb3631f48bdaefaf1456

import client from 'part:@sanity/base/client'
^^^^^^

SyntaxError: Cannot use import statement outside a module
AI Update

The issue you're hitting is that this gist was written for Sanity v2 and uses the old part:@sanity/base/client import system, which only worked inside the Studio's build context. That syntax doesn't work in modern Sanity v3+ projects or as a standalone Node.js script.

For Sanity v3 (Current Version)

Replace the old import with the modern client setup. Create your script (e.g., scripts/translate.js) like this:

import {createClient} from '@sanity/client'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  token: 'your-token-with-write-access',
  apiVersion: '2024-01-01',
  useCdn: false
})

// ... rest of the translation script code from the gist

Steps to make it work:

  1. Install the client: npm install @sanity/client
  2. Get a token from your Sanity project dashboard with write permissions
  3. Add "type": "module" to your package.json to use ES modules, OR convert all imports to CommonJS require() syntax
  4. Run with: node scripts/translate.js

If You're Still on Sanity v2

The part: system only works inside the Studio's execution context. Place the script in scripts/translate.js and run:

sanity exec scripts/translate.js --with-user-token

The --with-user-token flag gives your script access to the Studio's parts system and authenticated client.

Modern Alternative: Sanity Functions

For ongoing translation needs (not just one-time migrations), consider using Sanity Functions which give you serverless compute with built-in client access. But for a migration script like this, the standalone approach above is simpler.

The key takeaway: part:@sanity/base/client was v2-specific Studio magic that's been replaced by the standard @sanity/client package you can use anywhere.

Show original thread
7 replies
Figured it out - need to run using
sanity exec
👍
I think the code expects there to be a
document.field._type
field that indicates which type of field it is, but it's not included in the data returned from the
/export
endpoint: https://gist.github.com/bjoerge/6c0b5bdcd316fb3631f48bdaefaf1456#file-translate-js-L53
Can anyone from the sanity side speak to whether or not this assumption is correct or if I'm missing something?
user M
so I definitely am using field level translations (per that article) - at least I think I am : ) for whatever reason I'm not seeing any other fields beyond the value in the output (on the field level)
here's the schema of the document I'm trying to localize:

import { MdLocalMovies as icon } from 'react-icons/md'

export default {
  name: 'film',
  title: 'Film',
  type: 'document',
  icon,
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'localeString',
      validation: Rule => Rule.required()
    },
    {
      name: 'filmId',
      title: 'Film Id',
      type: 'string'
    },
    {
      name: 'latinTitle',
      title: 'Latin Title',
      type: 'string'
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'latinTitle',
        maxLength: 200 // will be ignored if slugify is set
      },
      validation: Rule => Rule.required()
    },
    {
      name: 'releaseDate',
      title: 'Release date',
      type: 'number'
    },
    {
      name: 'subject',
      title: 'Subject',
      type: 'localeString',
    },
    {
      name: 'productionId',
      title: 'Production ID',
      type: 'string'
    },
    {
      name: 'type',
      title: 'Type',
      type: 'localeString',
    },
    {
      name: 'abstract',
      title: 'Abstract',
      type: 'localeString',
    },
    {
      title: 'Keywords',
      name: 'keywords',
      type: 'localeArray'
    },
    {
      title: 'Latin Keywords',
      name: 'latinKeywords',
      type: 'array',
      of: [{ type: 'string' }]
    },
    {
      name: 'studio',
      title: 'Studio',
      type: 'localeString',
    },
    {
      name: 'director',
      title: 'Director',
      type: 'localeString'
    },
    {
      name: 'operators',
      title: 'Operators',
      type: 'localeString'
    },
    {
      name: 'otherCreators',
      title: 'Other Creators',
      type: 'localeString'
    },
    {
      name: 'sound',
      title: 'Sound',
      type: 'string'
    },
    {
      name: 'color',
      title: 'Color',
      type: 'string'
    },
    {
      name: 'numberOfParts',
      title: 'Number of Parts',
      type: 'number'
    },
    {
      name: 'meterage',
      title: 'Meterage',
      type: 'string'
    },
    {
      name: 'oldUrl',
      title: 'Old URL',
      type: 'url',
      readOnly: true
    }
  ]
}
and the localeString schema:

import {SUPPORTED_LANGUAGES} from './languages'

export default {
    type: 'object',
    name: 'localeString',
    fields: SUPPORTED_LANGUAGES.map((lang) => ({
      name: lang.id,
      type: 'string',
      title: lang.title,
    })),
  }
user M
so I definitely am using field level translations (per that article) - at least I think I am : ) for whatever reason I'm not seeing any other fields beyond the value in the output (on the field level)
here's the schema of the document I'm trying to localize:

import { MdLocalMovies as icon } from 'react-icons/md'

export default {
  name: 'film',
  title: 'Film',
  type: 'document',
  icon,
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'localeString',
      validation: Rule => Rule.required()
    },
    {
      name: 'filmId',
      title: 'Film Id',
      type: 'string'
    },
    {
      name: 'latinTitle',
      title: 'Latin Title',
      type: 'string'
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'latinTitle',
        maxLength: 200 // will be ignored if slugify is set
      },
      validation: Rule => Rule.required()
    },
    {
      name: 'releaseDate',
      title: 'Release date',
      type: 'number'
    },
    {
      name: 'subject',
      title: 'Subject',
      type: 'localeString',
    },
    {
      name: 'productionId',
      title: 'Production ID',
      type: 'string'
    },
    {
      name: 'type',
      title: 'Type',
      type: 'localeString',
    },
    {
      name: 'abstract',
      title: 'Abstract',
      type: 'localeString',
    },
    {
      title: 'Keywords',
      name: 'keywords',
      type: 'localeArray'
    },
    {
      title: 'Latin Keywords',
      name: 'latinKeywords',
      type: 'array',
      of: [{ type: 'string' }]
    },
    {
      name: 'studio',
      title: 'Studio',
      type: 'localeString',
    },
    {
      name: 'director',
      title: 'Director',
      type: 'localeString'
    },
    {
      name: 'operators',
      title: 'Operators',
      type: 'localeString'
    },
    {
      name: 'otherCreators',
      title: 'Other Creators',
      type: 'localeString'
    },
    {
      name: 'sound',
      title: 'Sound',
      type: 'string'
    },
    {
      name: 'color',
      title: 'Color',
      type: 'string'
    },
    {
      name: 'numberOfParts',
      title: 'Number of Parts',
      type: 'number'
    },
    {
      name: 'meterage',
      title: 'Meterage',
      type: 'string'
    },
    {
      name: 'oldUrl',
      title: 'Old URL',
      type: 'url',
      readOnly: true
    }
  ]
}
and the localeString schema:

import {SUPPORTED_LANGUAGES} from './languages'

export default {
    type: 'object',
    name: 'localeString',
    fields: SUPPORTED_LANGUAGES.map((lang) => ({
      name: lang.id,
      type: 'string',
      title: lang.title,
    })),
  }
Ended up giving up and writing my own script that's a bit more simple, but curious to know what I was doing wrong
Ended up giving up and writing my own script that's a bit more simple, but curious to know what I was doing wrong

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?