Internationalization for static Next.js

Internationalization for static Next.js


While working on my personal website, I decided to add internationalization for fun. I wanted a simple language picker on the top right of the website, which would switch the top navbar’s strings to the translated versions.
I chose a tech stack for my website that was both simple and flexible in terms of styling: Next.js with Tailwind CSS on the frontend, and GitHub Pages as the host. The 's possible to statically generate the content (SSG). GitHub provides a fantastic .yml template for actions that handles everything for you; all you need to do is push the code to your repo, and voila, it's up and running.
Next.js has supported internationalization out of the box (since v10). With a few config files, your site will automatically redirect users to the appropriate sub-path or sub-domain. The user's preferred language will be detected automatically (if enabled), and they will be redirected accordingly. For instance, a user with French as their preferred language would be redirected from to (sub-path) or (sub-domain).
However, Next.js doesn't provide the actual management of translations and hooks to integrate them into your application. Fortunately, there's a library specifically designed for this purpose: next-i18next. With just a couple of lines of code, this library allows you to easily set up localization. There is a catch, though.

The catch

As I mentioned earlier, we want to statically generate our site content. However, next-i18n provides translations via server-side rendering (SSR), which makes it impossible to export the site statically since we rely on network calls for our translations.
One option to provide translations locally is to route to another HTML page on changing language. So if we have 3 languages which we want to support, we would have 3 versions of each page. This solution is not very elegant and definitely not scalable in the long run.
Luckily, some awesome folks built next-export-18n to address this exact issue. It provides convenient internationalization with a useTranslation hook, exactly as with i18next, but does not rely on SSR. It achieves this by saving the localization as a query parameter instead of using sub-paths or sub-domains.
I will not be providing a tutorial for the setup in this post, as the devs of the module have already done a great job explaining it in the readme of their repo. However, I want to show how I integrated this into my site.
The languages that I support on my website are English, German, Slovak and Swedish. I created a translations.X.json file for each of the languages, where the X is the 2-letter code of the language. As the module supports nesting in the .json files, I split up my translations into views, such as home, about, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "home": { "title": "Stanislav Kosorin", "description": "The personal website of Stanislav Kosorin." }, "contact": { "title": "Contact", "description": "The contact page of Stanislav Kosorin.", "linkedin": "Connect with me on LinkedIn 🌍", "github": "Check out my projects on Github 🐱‍💻", "email": "Send me an email 📧" } }
Then, in each component, I used the useTranslation hook and picked the right localization string using t(), e.g. t(‘view.title’).
1 2 3 4 5 6 7 8 9 10 const { t } = useTranslation(); return ( <> <NextSeo title={t('contact.title')} description={t('contact.description')} /> {"///your component goes here///"} </> );
I also created a minimal language picker, which you can reuse from here. But since you're reading this, you get an exclusive chance to check it out live on this very site, in the .


With the help of next-export-i18n, you can easily add internationalization to a statically exported Next.js site, without relying on server-side rendering. The library allows convenient localization with a useTranslation hook and saves the localization as a query parameter instead of using sub-paths or sub-domains.

Thank you for reading, and happy translating!