Manage Multilingual Content in Storyblok and Astro
Storyblok is the first headless CMS that works for developers & marketers alike.
Let’s see how to add and manage multiple languages on our website. We'll statically generate all the pages for each language. Additionally, we'll develop a simple language switcher in our header. For the backend, we will learn how to manage multiple languages in our Storyblok space and how to get the translations from our codebase.
You can read more about how to Add i18n features in Astro here.
Before starting with the practical part, it is important to know that Storyblok has three different approaches to implementing internationalization. In this tutorial, we will be using Field-Level translation. Depending on your requirements it will make sense to use one instead of the other. You can read all about the different approaches in our Internationalization guide.
If you’re in a hurry, have a look at our live demo in Netlify! Alternatively, you can explore or fork the code from the Astro Ultimate Tutorial GitHub Repository.
Requirements
This tutorial is part 6 of the Ultimate Tutorial Series for Astro. We recommend that you follow the previous tutorials before starting this one.
We will use the code from the previous tutorial as a starting point. You can find it here.
Adding a language in Storyblok
First, let's add a new language to our Storyblok space. Go to Settings {1} and click on Internationalization {2}. Here, you will find the configuration for field-level translation.
Although you can select any language you want, for the purpose of this tutorial we will use Spanish as the second language.
Let's select the Spanish language from the drop-down {3} and hit the Add button {4}. Once the Spanish language is added, save the changes by clicking on the Save button {5}.
If we now go to the Content section and open any Story, we will see a language drop-down in the action bar {1}.
Switching the language from the drop-down won't work yet because we haven't translated anything yet.
Since we’re using the field-level translation approach, we need to make the fields translatable in our component schema in order to allow the translation of our content. Let's edit the title
field of the article
Content Type and mark it as (Translatable) {1}. Hit the Save & Back to Fields button after changing the field {2}.
If we change the language now, we will get a 404 error in Astro. This is because we have not generated these new routes.
Looking now at the translatable field, you will notice a change in the UI. You will see that the field is non-editable and contains the default language's content {1}. If we want to translate it, we must click on the Translate checkbox {2}.
By activating it, we will be able to edit the field and add the content in Spanish. But not only that. As we can see in the screenshot below, when we activate the translate option, an arrow button {1} appears. If we expand it, we will see the default language content {2}, a button to add the default language content in the new language {3}, and the option to go to Google Translate {4}.
Let's hit the Publish button with the translated content and configure our code in the frontend to make it work.
Implementing i18n in Astro
Let's first generate all the URLs for our Astro site so we don't get a 404 error.
Open pages/[...slug].astro
and update the getStaticPaths
function to generate all new language pages.
Okay, we did quite a bit of refactoring, so let's understand what's happening. We are using the Storyblok's Links API to generate all the pages like before but now we are adding all the language paths, too. It's controlled via the languages
array at the top. Now in the future, if we want to add a new language in Storyblok we just have to add that language to this array and it will generate all the pages for that language, too.
Moreover, this time we are passing multiple values via the props
as well. This will help us to fetch the correct data for each language and add a language switcher in the header.
Next, we are going to get the slug
from Astro.props
along with all the other information we passed in the getStaticPaths
function.
Previously we used Astro.params
to get the slug
but that will not work here, as the Storyblok API takes the language as a parameter, not as the part of URL.
We also have to pass the language
as a parameter in the storyblokApi.get
function. This will now fetch the proper data for each page.
Just by doing this, we can see our article pages now showing correct data based on the language and we are not seeing 404 pages.
For the home page, as it has a real path set to it, it won't change the path when the language is switched from the dropdown. It will only work fine in the browser. You can use the Advanced paths app to configure the preview url programmatically for the visual editor.
Translating the AllArticles Component
If we take a look at our Blog home story, it does not work properly.
We can see the title in Spanish but all the article cards are still showing in English. Let's fix this issue by passing the language
as a prop to all the components.
We are also passing a few props to the BaseLayout
component which will help create the language toggle in the future.
Next, we also have to pass this language
prop in the storyblok/Page.astro
component.
Now, we can get this language prop from our storyblok/AllArticles.astro
component. Let's, pass the language
as a parameter in the storyblokApi.get
function and as a prop in ArticleCard
component.
Looking at the Blog home story again, we can see all the article cards showing the correct data.
Refactoring internal links
Now that we can see our pages translated, we must refactor our internal links. This way, the links of our page will have the corresponding locale automatically added to each URL.
First, we have to pass the language
prop in a few components. Let’s start with the storyblok/PopularArticle.astro
component:
Here we are also using the shared ArticleCard
component. We have to pass the language
as a prop in the ArticleCard
component.
Now let's create a small utility function that will help us manage translated links.
We can now use this function in our ArticleCard
component.
Next, Let's pass this language
prop in the Header.astro
component. Also, we can add the language as a dynamic variable in the HTML lang attribute. <html lang={language}>
If you remember, we have passed language
and langSwitch
props in the BaseLayout
component from pages/[...slug].astro
We are simply forwarding it to the Header
component.
That's it, this is how easily you can translate your content and manage multiple languages with Storyblok and Astro. Similarly, you can translate all the blogs and any other content you want. Let's also do one more thing, add a language switcher in the navigation.
Adding a Language Switcher
This will be pretty straightforward, we already have the langSwitch
data in our Header
component.
Wrapping Up
Congratulations, you are now able to build a full-blown multilingual Astro website using Storyblok. In this tutorial, you saw how to add and manage multiple languages in your Astro and Storyblok website with Storyblok's field-level translation. We also added a basic language switcher on the navigation bar of the website, so that your project is ready to go to production and serve as a base for future projects.
In the next part, We will learn how to create a preview environment for your Astro application.
Resource | Link |
---|---|
Add i18n features in Astro | https://docs.astro.build/en/recipes/i18n/ |
Astro | https://astro.build/ |
The Storyblok Astro Ultimate Tutorial | https://www.storyblok.com/tp/the-storyblok-astro-ultimate-tutorial/ |
Storyblok SDK for Astro | https://github.com/storyblok/storyblok-astro |
Storyblok SDK Live Demo | https://stackblitz.com/edit/astro-sdk-demo |
Storyblok in the Astro Docs | https://docs.astro.build/en/guides/cms/storyblok/ |
Building Future-Proof High-Performance Websites With Astro Islands And Headless CMS | https://www.smashingmagazine.com/2023/02/building-future-proof-high-performance-websites-astro-islands-headless-cms-storyblok/ |
Storyblok APIs | https://www.storyblok.com/docs/api |