Skip to main content

How to swap i18n content in Storyblok using their field type translations feature

    Try Storyblok

    Storyblok is the first headless CMS that works for developers & marketers alike.

    Did you create a beautiful website with a lot of content and then someone came along with a request to change the default language? If the answer is yes, then don't worry, it's pretty easy to swap translated content using the Management API of Storyblok.

    Detailed Case Description

    We have experienced this situation at Wondrous LTD. As we set up the project, we already knew that the page would have to be multilingual and would require at least 2 language versions of content to go live. Storyblok offers you 4 different solutions for internationalization - Single tree and field-level translation, folder-level translation, multiple trees, and mixed approach. In this case, we choose option 1 - Single tree and field level translation. This means that you have one default language version and you can add as many additional language versions as you want through settings. We added German as a secondary language and set up the whole NuxtJS project with English as the default language in mind.

    A while after it was published, they asked if it would be possible to make the German translation default and English secondary. After that, an editor would see “Default” and “English” listed in the dropdown.

    Basically, you have two options how to do it:

    - You can do it manually and copy & paste all content

    - You can use the Management API of Storyblok and write a short script, which will do it for you.

    I believe you would choose the second option.

    I am going to show you how to do it in NodeJS, but you could also do it in Ruby, Python, PHP, Java, Swift, or C#. See the Storyblok documentation for more info.

    Pseudo Code

    • Get schemas of all your components and for each component save which fields are translatable

    • Loop through all your stories

    • Recursively find all translatable fields in stories content

    • Move content of fields to default language and create new translation fields for original content

      •  Create new field “headline__i18n__en” and fill in value of “headline”

      • "headline" value replace with value of “headline__i18n__de”

    • Update original story with new content

    Setup script

    Install storyblok-js-client and get your personal access token from your account settings - this is not a space token, but an account token. Then, get the id of the space where you will want to perform a swap of content and finally define the new default language and where you want to move the old default content.

    • fromLang define postfix of i18n field - eg. headline__de__

    • toLang define postfix of new field, where the default content will be save - eg. headline__en__

    // Get schemas of all components
    Storyblok.get(`spaces/${spaceId}/components`, {})
    .then(response => {
      // Find translatable fields on each component => addTransKeyFromComponent(component))
      // After get of all translatable fields start content swapping
      swapContentOnStories(1) // start contant swapping
    }).catch(error => {
    // Finds translatable fields in components and save them into i18nComponentsFields
    function addTransKeyFromComponent(component) {
      const compName =
      const compSchema = component.schema
      const compKeys = Object.keys(compSchema)
      let i18nKeys = [] key => {
        if (compSchema[key].translatable) {
      i18nComponentsFields[compName] = i18nKeys
      console.log(`✅   got all i18n fields of ${compName}`)

    Get all stories

    Now we know which content of the fields needs to be swapped. So we need to get all stories. The function Storyblok.get(spaces/${spaceId}/stories, {}) returns a paged response of all stories. We get all the pages and, for each story, we will request the content of the story using

    function swapContentOnStories(page=1) {
      Storyblok.get(`spaces/${spaceId}/stories`, {
        page // starting with first page
      .then(response => { => getStoryContent(
        if ( - response.perPage * page > 0) {
          // recursively get all stories from Storyblok - response is paged!!
          swapContentOnStories(page + 1) 
      }).catch(error => {

    Get story content

    Here we will get content of the story by calling the swapContentOfStory function to swap i18n content. After we swap content, we have to update/push the new content of the story to Storyblok using the function updateStory.

    function getStoryContent(storyId) {
      Storyblok.get(`spaces/${spaceId}/stories/${storyId}`, {})
      .then(response => {
        swapContentOfStory( // swap content
        console.log(`✅   swapped content on ${}`)
        updateStory(storyId, // update story in Storyblok
      }).catch(error => {

    Swap content

    First we need to know which fields of the current content are translatable. For that, we will use the name of the component (content.component) and the previously created object i18mComponentsFields, which contains all translatable fields sorted per component.

    Now we look at the keys of the current content if it contains a match with the translatable field of the component. We will perform a swap of content for these fields (lines 6-8).   

    If the key is not translatable and it is an Array, we will recursively call one more time this function swapContentOfStory. For all other cases, we will do nothing because these are the fields which are not translatable (like _uid etc.).

    function swapContentOfStory(content) {
      const i18nContentKeys = i18nComponentsFields[content.component]
      const contentKeys = Object.keys(content) => {
        if(i18nContentKeys && i18nContentKeys.includes(key)) {
          content[`${key}__i18n__${toLang}`] = content[key]
          // if no translation leave the default
          content[key] = content[`${key}__i18n__${fromLang}`] ? content[`${key}__i18n__${fromLang}`] : content[key]
        } else if (Array.isArray(content[key])) {
          content[key].map(component => swapContentOfStory(component))
        } else {
          // Do nothing - not i18n field

    Update story

    The last step of the script is an update of the story with new content. For that, you need to have an id of the story and new content and call this function.

    function updateStory(storyId, updatedStory) {
      Storyblok.put(`spaces/${spaceId}/stories/${storyId}`, {
        "story": updatedStory,
        "force_update": 1
      }).then(response => {
        console.log(`⬆️   successfully updated - [${}] `)
      }).catch(error => {

    Switching Default Language in Storyblok

    At this moment your story in Storyblok will look like the code below. But we are not done yet, you still have to go to the language setting of the space in Storyblok (Space → Settings → Internationalization) and remove the German language and add the English language.

    By the way, don't worry about adding/removing languages as this action will not remove content from your stories. It will correctly set up your visual editor of Storyblok and delivery API of Storyblok.

    That's it - you can now swap your content as often as you want. And don't worry at all, you will always have backups and the version tree in Storyblok.