Internationalization
On May 13th, 2024, Storyblok started gradually rolling out a new design for its Visual Editor. Therefore, the Visual Editor product screenshots depicted in this resource may not match what you encounter in the Storyblok App. For more information and a detailed reference, please consult this FAQ on the new Visual Editor design.
Many businesses operate on a global scale and in order to be successful and tell a compelling story it is necessary to speak the language of your customers. Storyblok supports content in multiple languages and integrates with various tools to manage translations. These integrations support you in making your content available internationally.
Storyblok doesn’t charge extra for internationalization of your content. We know that a project can grow quite fast and we don’t want to punish customers for tapping into new markets.
There are three main options for managing multi-language and multi-country content in Storyblok. Different use cases require different approaches which is why we offer field level translation, folder level translation, and space level translation. We also permit adding two or more types of approaches as Mixed level translation.
- Field level translation is a good choice if the structure of your content in a different language is the same as in your default language.
- Folder level translation is preferable when the content for different languages is managed by separate teams or your project is structured differently for each market.
- Space level translation is a good choice if you have multiple spaces and need to manage your languages for the different spaces such as a
dev
andlive
space for your project.
Keep in mind that this decision depends on your content strategy and should be made after considering all the advantages and disadvantages of the solution.
The decision to use Folder, Field Space or Mixed Level Translation is only influenced by your content structure and not the size of your team.
Field Level Translation
Storyblok can store multi-language content on the field level. This means that you only need one content tree and you don’t have to create a stand-alone folder for each country. Each translatable field will be stored in the content tree as a separate stand-alone property with a suffix of the language it belongs to.
As long as your site content is very similar from one language to another, this is the best option. You will have fewer stories to manage and the system takes care of serving the correct language automatically.
Setup
To set up field-level translations, you need to add a new language under the tab Internationalization {1} in the settings {2} of your space.
We recommend using -
instead of _
in language codes. If you decide to use underscores instead of hyphens, it is recommended to do so consistently throughout all language codes. For example, defining both pt-br
and pt_br
may lead to issues, as the system will consider them the same language code.
Next, define which fields you want to be translated into the schema of each component. Once the field is translatable, you should see a language switch {1} as shown below:
Enable translatable fields
To enable a field to be translatable, Navigate to the block library {1} of your space, click on the field you want to translate, and toggle on the checkbox {2} in the schema description of that field.
Once the field is enabled for translations, switching the language in the UI allows you to add a translation for each previously activated field separately. Fields that are not enabled for translation are included but use the default language in every story.
To translate the content of the field, first, activate the checkbox {1} and add your content {2}. When the menu is open the text in the default language is shown to support the translation, you can also check the language switch on which languages you'd like the content to be translated.
Translatable slugs
When using field level translations there is often the requirement to translate the slug of a story as well. You do not need to switch to folder level translation if this comes up in your project. With the translatable slugs app, it is possible to define slugs for folders and stories in different languages.
Visit the Storyblok App Store to install Translatable Slugs in your space.
Once the App is installed, a language selector {1} appears next to the slug input field. It is now possible to change the slug for each language. If a slug for a specific language is not defined, then the system uses the slug of the default language.
To generate a navigation in the context of a web application or direct search engines to index alternate versions of your stories additional links are added to the links endpoint of Content Delivery API.
Custom Fallback Language
This feature allows you to define a custom fallback language if you use field level translations using the parameter fallback_lang
.
Example: The following API call will use the Portuguese version as fallback for Brazilian Portuguese and if that one doesn’t exist it will use the default language defined in the space settings.
GET https://api.storyblok.com/v1/cdn/stories?fallback_lang=pt&language=pt-br
Export and Import of Translations
Export and Import functionality can be added to your space by installing a new app from the Storyblok App Store. For most use-cases, it is probably best to install both apps.
Visit the page of these apps and press the “Install” button. Once the apps are installed, you can export content from your favorite translation tool and import a specific language into the system using these tools.
The following steps show how it could look to have a generic workflow for field level translations with an external tool.
- Install the “Import” and “Export” app.
- Define your specific language in the space settings (e.g.: English = default or German = de).
- Begin by filling in content in the default language.
- Enable translatable for fields you want to translate.
- Select the language your listed language from the menu bar.
- Go to the config tab of the content item and click export {1}.
- Use the exported JSON or XML file in the translation tool of your choice to translate the default language (e.g.: English) to your target language (e.g.: German).
- Import the now translated language using the import button {2} with the target language selected from the menu bar. Keep in mind that newly imported content will overwrite your existing content.
Read this article to see a real world example on how to use Storyblok with a translation tool: https://www.storyblok.com/docs/editor-guides/translate-with-lingohub
3rd Party Integrations
You can also translate content with a 3rd party application. This can be done either manually or automatically according to your project needs. However, this service is only available for enterprise clients. You can contact the team here.
You can learn more about how to set up field and folder level translations here
API and Data Structure
You can access the localized version of the content by adding the language code to your API requests. The following examples show how the language code (de in this case) can be used in the URL.
Get a single story
This request will fetch a single story from the content delivery API.
Default Api V2:
GET https://api.storyblok.com/v2/cdn/stories/home?language=de
Get all stories from one folder
Use this request to fetch all stories from a single folder (e.g.: news
). The result is paginated with returning 25 stories per default. Use the per_page
parameter to increase this number to up to 100 stories.
Default version Api V2:
GET https://api.storyblok.com/v2/cdn/stories?starts_with=news&language=de
Get all translated stories at the root level in Api V2
To get all stories on the root level in the api V2 it is necessary to add the *
operator after the language.
GET https://api.storyblok.com/v2/cdn/stories?starts_with=de/*
The *
operator only works in conjunction with languages on the root level. This is a feature to allow folders with the same name as the language.
You can read more about how to use the Content Delivery API in our API reference.
Get a single story (GraphQL)
This request will fetch a single story (e.g.: story with the id 9248263
) in a certain language (e.g.: de-DE
) from the GraphQL API.
ArticleItem(id: "9248263", language: "de-DE") {
content {
template
content
component
title
trip_state
}
lang
}
Get all translated stories at the root level (GraphQL)
Similar to the REST API above the starts_with
directive is used to filter articles in a certain language (de-DE
) and the *
operator to indicate that we are looking for the language and not a folder.
ArticleItems(starts_with: "de-DE/*") {
total
items {
content {
title
}
id
lang
}
}
Get all languages
For certain implementations it is necessary to know which and how many languages are needed in advance. You can retrieve that information from the language_codes
property in the space information. Find out more about the space object in Content Delivery API docs.
Example
GET https://api.storyblok.com/v2/cdn/spaces/me
Example response
{
"space": {
"id": 123,
"name": "Storyblok.com",
"domain": "https://www.storyblok.com/",
"version": 1544117388,
"language_codes": [
"de-de",
"en-gb"
]
}
}
Update a story
Internationalization is also a topic when updating a story. There are two different ways of handling the update for translated content. First, you can pass a lang
parameter with your request defining the target language.
Example:
PUT mapi.storyblok.com/v1/spaces/<space-id>/stories/<story-id>
{
"story": {
"name": "Story Name",
...
},
...
"lang": "de"
}
Second, if you want to update multiple languages at once, it is possible to provide the values for the translations within the same content object by appending __i18n__
followed by the language code.
Make sure to activate the component field option translatable
to make field level translations work.
Example:
POST mapi.storyblok.com/v1/spaces/<space-id>/stories
{
"story": {
"name": "My First Article",
"slug": "first-post",
"content": {
"component": "post",
"headline": "This is awesome!",
"headline__i18n__de": "Das ist toll!"
}
},
"publish": 1
}
A great real world example on how to use the __i18n__
functionality is swapping the default language of a complete space. Read more about it in this article: https://www.storyblok.com/tp/how-to-swap-i18n-content-in-storyblok-using-their-field-type-translations-feature
Searching content: how to use filter queries with translated stories
The searching capabilities of the Content Delivery API of Storyblok allow you, as a developer, to search content in a field with a specific translation.
For example, if you want to search all the stories that contain the headline
field with the word "welcome" you can use the query function provided by the Content Delivery API via the filter_query
URL parameter:
GET https://api.storyblok.com/v2/cdn/stories?version=draft&token=<youraccesstoken>&filter_query[headline][like]=*Welcome*
Where, in this specific example, we have the API URL consist out of these parts:
https://api.storyblok.com/v2/cdn/stories
is the Content Delivery API's endpoint for retrieving stories (if your space is created in the US region, the hostname should beapi-us.storyblok.com
)version=draft
is the parameter for retrieving not yet published stories. If you want to retrieve published stories you need to setversion=published
token
is the access token related to the space you want to accessfilter_query[headline][like]=*Welcome*
is the filter for theheadline
field, using thelike
operator for the termWelcome
(the asterisks act as a wildcard)
Now that we understand how to filter stories via Content Delivery API, we can adjust and add the filter parameters for retrieving the content for a specific language.
For example, if you want to search on the headline
field and you want to force the search for a specific language (the Italian language that has the language code it
), you have to append to the field name the string __i18n__
followed by the language code. So, in this case, instead of using the headline
as the field name, you have to use headline__i18n__it
as the keyword for filtering the headline
field in the Italian language.
Therefore, the complete request should be:
This way, you can filter the stories by the headline
field in Italian, but you will receive the default language as a result.
In order to retrieve the stories in a specific language, you have to set the language
parameter.
With the same logic, for example, if you want to filter the description
field in the Spanish language (language code es
), you need to use the description__i18n_es
as filter query field, and if you want to obtain the stories in Spanish you have to set language=es
. If you forget to set the language
parameter, the stories are filtered by Spanish description (because of description__i18n_es
) but you will obtain the filtered stories in the default language.
You can see an example request below.:
Folder Level Translation
Folder level translation uses separate content trees for each language. One content tree for each language. For example one for English, one for German and so on. With this structure some of the content is duplicated but each localization can be completely customized on the level of structure and order of the components of the story.
Folder level translation is a great choice when your content tree or the structure of your stories is different in every language. Another reason to opt for folder level translation is having separate teams or special needs for different markets.
Folder level translations can be used to differentiate regions and markets and not only languages.
Dimensions App
We recommend installing the Dimensions app to create links between stories in different languages. These links are needed to find alternate versions of a story. In the context of a web application, HTML tags, HTTP headers or sitemaps can be used to inform search engines about these alternate versions (hreflang
). After installing this app there will be a Dimensions button {1} at the top right corner of the content editor.
The screenshot shows different actions that are available now. See the table below for an explanation on all features of the Dimensions app.
Action | Description |
---|---|
Dimensions {1} | The dimensions app for managing different versions of each language for your content. |
Clone to all {2} | Duplicate the current story into all available folders. |
Merge to all {3} | Merge the changes into all connected stories. |
Overwrite {4} | Overwrite the existing story in the folder with the current story. |
Merge {5} | Merge the changes to the selected story. |
Create Clone {6} | Use the current story to generate a duplicate in the selected folder |
You can mix folder and field level translations for your project. In the case of folder level, you use path /folder_lang/story
and for field level, you use the parameter story?lang=de
.
Space Level Translation
Space level translation uses multi spaces to manage your content for different languages. You can opt for space level translation if your application is very huge, and you treat your content in different languages as different projects (meaning you have separate teams, components, content, etc). That way, you can have Space-A for your content in English and Space B for your content in German. You can see the screenshot below for two spaces with different languages, French (fr) {1} and German (de) {2}.
With space level translation, you can use the Storyblok CLI and management API for sharing components, stories, and schema.
You can learn about the different Locationalization options and comparisons with this blogpost.