👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Discussion on dynamically loading languages in Sanity schemas

34 replies
Last updated: Apr 27, 2020
Hi there ☆I would like to change this `localeString.js`:

const supportedLanguages = [
  {
    name: 'fr',
    title: 'Français',
    isDefault: true, // important
  },
  {
    name: 'ja',
    title: '日本語',
  },
  {
    name: 'en',
    title: 'English',
  },
]

export default {
  name: 'localeString',
  title: 'LocaleString',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
    },
  ],
  fields: supportedLanguages.map(langx => (
    {
      name: langx.name,
      title: langx.title,
      type: 'text',
      fieldset: langx.isDefault ? null : 'translations',
    }
  )),
}
to make it point out instead of
const supportedLanguages
the data that are in a document called
lang.js
.this is `lang.js`:

export default {
    name: 'lang',
    title: 'Langue',
    type: 'document',
    fields: [
        {
            name: 'name',
            title: 'Name',
            type: 'string',
        },
        {
            name: 'title',
            title: 'Title',
            type: 'string',
        },
        {
            name: 'isDefault',
            title: 'IsDefault',
            type: 'boolean',
        },
    ],
}
In VISION I have this query:

*[_type == 'lang' ]{name, title, isDefault}
giving this result:

"result":[3 items
0:{3 items
"isDefault":false
"name":"ja"
"title":"日本語"
}
1:{3 items
"isDefault":true
"name":"fr"
"title":"Français"
}
2:{3 items
"isDefault":false
"name":"en"
"title":"English"
In the Docs the query example is:

const query = '*[_type == "bike" && seats >= $minSeats] {name, seats}'
const params = {minSeats: 2}

client.fetch(query, params).then(bikes => {
  console.log('Bikes with more than one seat:')
  bikes.forEach(bike => {
    console.log(`${bike.name} (${bike.seats} seats)`)
  })
})
So I modify
localeString.js
different ways but I am not able so far to find how to insert that into
fields
so it fits correctly in:
import client from 'part:@sanity/base/client'
import groq from "groq"

const query = groq `*[_type == 'lang']{
  name,
  title,
  isDefault
}`
const params = {}

export default {
  name: 'localeString',
  title: 'LocaleString',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
    },
  ],
  fields: [
    {
      client.fetch(query, params).then(langs => {
      langs.forEach(lang => ({
        name: lang.name,
        title: lang.title,
        type: 'text',
        fieldset: lang.isDefault ? null : 'translations',
      }))
      })
    }
  ]
}
I know that I miss the keys and everything with fields but I really don't know how I should do...
Apr 27, 2020, 3:29 PM
Hi User, you’re pretty close already I believe 🙂 What’s the error you’re getting at the moment? Could you add
console.log(lang)
to the
forEach
loop to see if that output matches what you expect?
Apr 27, 2020, 4:02 PM
yes of course but I don't know how to look at console.log(lang).
Apr 27, 2020, 4:03 PM
What browser are you using?
Apr 27, 2020, 4:04 PM
firefox, chrome as you like. Right now firefox.
Apr 27, 2020, 4:04 PM
But before console.log() I have some errors displayed.
Apr 27, 2020, 4:05 PM
In Chrome, go to View > Developer > JavaScript console, or if you’re on a Mac, you can use CMD+ALT+J (not sure what the Windows shortcut is).
Apr 27, 2020, 4:08 PM
And feel free to paste those errors here 😉
Apr 27, 2020, 4:09 PM
Yes but where would you like the console.log(lang) to be placed?
Apr 27, 2020, 4:09 PM
From localeString, I query lang.js to get the references from lang.js for the languages
Apr 27, 2020, 4:10 PM
User, could you place the console.log(lang) in there please:
export default {
  name: 'localeString',
  title: 'LocaleString',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
    },
  ],
  fields: [
    {
      client.fetch(query, params).then(langs => {
      langs.forEach(lang => ({
        name: lang.name,
        title: lang.title,
        type: 'string',
        fieldset: lang.isDefault ? null : 'translations',
      }))
      })
    }
  ]
}
Apr 27, 2020, 4:12 PM
Please forgive me of being so clumsy.I did this:

import client from 'part:@sanity/base/client'
import groq from "groq"

const query = groq `*[_type == 'lang']{
  name,
  title,
  isDefault
}`
const params = {}

export default {
  name: 'localeString',
  title: 'LocaleString',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
    },
  ],
  fields: [
    {
      console.log(lang),
      client.fetch(query, params).then(langs => {
      langs.forEach(lang => ({
        name: lang.name,
        title: lang.title,
        type: 'string',
        fieldset: lang.isDefault ? null : 'translations',
      }))
      })
    }
  ]
}
and I got that:

ERROR in ./schemas/localeString.js
Module build failed: SyntaxError: C:\xampp\htdocs\<http://francois-vidit-2.com|francois-vidit-2.com>\studio\schemas\localeString.js: Unexpected token, expected "," (46:13)

  44 |   fields: [
  45 |     {
> 46 |       console.log(lang),
     |              ^
  47 |       client.fetch(query, params).then(langs => {
  48 |       langs.forEach(lang => ({
  49 |         name: lang.name,
Nothing fits.
Apr 27, 2020, 4:20 PM
If I understand correctly, you want to load your languages dynamically from your
lang
document type instead of using
const supportedLanguages
.
Have you tried something like this?

import client from 'part:@sanity/base/client'
import groq from "groq"
const query = groq `*[_type == 'lang']{
  name,
  title,
  isDefault
}`
const params = {}
export default {
  name: 'localeString',
  title: 'LocaleString',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
    },
  ],
  fields: 
    client.fetch(query, params).then(langs => {
    langs.map(lang => ({
      name: lang.name,
      title: lang.title,
      type: 'string',
      fieldset: lang.isDefault ? null : 'translations',
    }))
  })
}
Apr 27, 2020, 4:28 PM
yes completelythis is the error I get doing so:

The "fields" property must be an array of fields. Instead saw "object
Apr 27, 2020, 4:30 PM
you understand correctly.
Apr 27, 2020, 4:33 PM
Hi again. Actually, it’s not as straightforward as it seems. There’s nothing wrong with your approach per se, and it should actually return an array of language-specific fields, but it might be that Promises are not supported in type definitions. I’m checking this internally.
Apr 27, 2020, 5:39 PM
Thank you User.
Apr 27, 2020, 5:40 PM
Do you need more information?
Apr 27, 2020, 5:40 PM
I focusing on that point because my idea using Sanity is to avoid redondance of data: I would like all my localeSomething to point a single source of truth.
Apr 27, 2020, 5:42 PM
I feel better to realise that my question is been object of attention: I am a little bit more confident now.
Apr 27, 2020, 5:44 PM
Also I would like to link other field later on the same way.
Apr 27, 2020, 5:45 PM
We went on using the fetch method from that example.But on the Youtube video with User on initialValue, he is using async await.
I don't really understand the difference, I tried also that side.... miserabily!
Apr 27, 2020, 5:48 PM
It’s a perfectly valid approach - just not in type definitions perhaps if they don’t support Promises 🙂 I’ll let you know if there’s a workaround.
Apr 27, 2020, 6:02 PM
Yes 🌻 please!!!There must be one
👻
Apr 27, 2020, 6:05 PM
I’m afraid I have to confirm that async schemas are currently not supported, so you’ll have to go the traditional route with this for now. Sorry about the time you lost researching this issue but hopefully you still learned a few things to use elsewhere? 🙂
Apr 27, 2020, 6:35 PM
what do you mean by the traditional way?
Apr 27, 2020, 6:36 PM
Ah by
const supportedLanguages
?
Apr 27, 2020, 6:37 PM
I have already found a solution by having a central const
supportedLanguages
in a data folder and
import
it, but it is not really SANITY!!
Apr 27, 2020, 6:38 PM
I am glad to have an answer at least, it is now clear.
Apr 27, 2020, 6:39 PM
Indeed, that way 🙂 I’m glad you have a solution at least (and it’s a single source) 👍 Hopefully future versions will give you a way to make the languages load dynamically.
Apr 27, 2020, 6:40 PM
you mean that the team at Sanity consider this as sufficiently important to consider working on it?
Apr 27, 2020, 6:41 PM
In terms of principles, It cannot be denied... It would push the concept of Sanity to a accomplishment... I am surprised that you seem to be surprised!!
Apr 27, 2020, 6:43 PM
I would love to be able to do that in a near future... 🌜
Apr 27, 2020, 6:45 PM
If I may ask another question:in the Docs there is a piece of code:

function localize(value, languages) {
  if (Array.isArray(value)) {
    return value.map(v => localize(v, languages))
  } else if (typeof value == 'object') {
    if (/^locale[A-Z]/.test(value._type)) {
      const language = languages.find(lang => value[lang])
      return value[language]
    }
    
    return Object.keys(value).reduce((result, key) => {
      result[key] = localize(value[key], languages)
      return result
    }, {})
  }
  return value
}
Am I getting it right if I say that this function has to be placed not in Sanity but where the localized data arrives to be rendered (whatever the javascript system) (for instance, for me, Gatsby)?
Apr 27, 2020, 6:49 PM
It is not precise where this should be, what role it plays and it is not obvious to me...
Apr 27, 2020, 6:50 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?