11 Step Checklist for Internationalizing a Website
At sliplane.io we recently added another language to our website and since I did this in another project before, I knew it was going to be a huge hassle! That's why this time, I derived a checklist, that you can follow when translating your own website. I use nuxt/i18n in the examples, but the checklist generally applies to all technologies. Strategies: Before we get into it, let me clarify what this checklist is for. There are several strategies when it comes to internationalizing a website: Separate websites for each language Same website but different content Same website, same content, but localized I will only focus on number 3 here, I think in most cases this is the least effort to pull off and maintain. Checklist Before getting started, create a list of all layouts, pages and components that need to be translated. It helps to do this in advance, to keep an overview over the status of translations. You then go through each layout/ page/ component and follow the checklist below: html lang attribute The first thing to do is to add the html lang attribute: ... Two letter language codes like de also work, but it's best practice to include the country. Internal and external links You want to make sure, that on a German site, internal and external links point to German content. I18n plugins usually provide utilities to deal with internal routing in SPAs, for external links it's important to adjust the href accordingly. Homepage becomes {{ $t('homepage') }} Images Sometimes images contain text or they are localized in another way, so that it's necessary to show a different image for each language. Remember to localize the alt and src property if necessary. becomes Aria Labels Aria labels are sometimes overlooked, when translating a website. In case you use them, remember to also translate Aria labels. becomes Variables and pluralization in Text: Variables in text can be challenging to translate, since you can't just provide a static string, but have to run some sort of interpolation to inject your variables and dynamically adjust the pluralization. Usually the i18n plugin ships with this feature, but it can be pain to use it consistently since it just adds a lot of code, so sometimes it's easier to rephrase your sentences like this: "There are 5 apples in my box" becomes: "Apples in my box: 5" There are {{ numberOfApples }} in my box. becomes {{ $t('applesText', numberOfApples) }} Datetime and number formatting Make sure to adjust how date times and numbers are formatted. In the US, decimals are separated by . in Europe it's a ,. Also adjust currency formats: in some languages the currency symbol is in front of the number $5.00 sometimes it's after 10,00€. On top you need to consider conversions of other units like distance (feet to meters), weight (pounds to kilograms) or volumes (ounces to liters)… You can either leverage your i18n plugin or use native functionality like date.toLocaleDateString(locale, {...}) (toLocaleDateString) or new Intl.NumberFormat(locale,).format(count)(Intl). Total cost: $5.00 becomes {{ $t('totalCost') }}: {{ $n(5, 'currency', locale) }} HTML In a couple of cases I stumbled over this situation: This is very important! In order to translate that, without loosing the styling, you can translate the html and render it on screen: { ... translated: 'Das ist sehr wichtig!' } In Vue you can render this using the v-html directive: In plain js you would do something like this: document.getElementById("myDiv").innerHTML = $t('translated'); Note: Rendering html strings is a potential XSS vulnerability, so you either want to avoid it if possible, or sanitize your html string before with a library like sanitize-html: import * as sanitizeHtml from 'sanitize-html'; const translatedString = "Das ist sehr wichtig!" const sanitized = sanitizeHtml(translatedString) document.getElementById("myDiv").innerHTML = sanitized; Meta tags Don't forget to translate your pages meta tags. Meta title, description, and potentially the image are all important for consistent localization. Welcome to my homepage! This meta section is often generated by the framework so make sure to localize it there and don't forget to check the generated html. Alternate links Add references to your markup, to indicate to search engines, that there are alternate versions of the page in different languages. You can do so using alternate links: Make sure to include all languages, even a reference to the current page itself. If you set these links via your framework it's best to inspect the generated html code, in order to make sure that everything went as expected. Sitemap There are different strategies for your si

At sliplane.io we recently added another language to our website and since I did this in another project before, I knew it was going to be a huge hassle! That's why this time, I derived a checklist, that you can follow when translating your own website. I use nuxt/i18n in the examples, but the checklist generally applies to all technologies.
Strategies:
Before we get into it, let me clarify what this checklist is for. There are several strategies when it comes to internationalizing a website:
- Separate websites for each language
- Same website but different content
- Same website, same content, but localized
I will only focus on number 3 here, I think in most cases this is the least effort to pull off and maintain.
Checklist
Before getting started, create a list of all layouts, pages and components that need to be translated. It helps to do this in advance, to keep an overview over the status of translations. You then go through each layout/ page/ component and follow the checklist below:
html lang attribute
The first thing to do is to add the html lang
attribute:
lang="de-de">
...
Two letter language codes like de
also work, but it's best practice to include the country.
Internal and external links
You want to make sure, that on a German site, internal and external links point to German content. I18n plugins usually provide utilities to deal with internal routing in SPAs, for external links it's important to adjust the href accordingly.
becomes
Images
Sometimes images contain text or they are localized in another way, so that it's necessary to show a different image for each language. Remember to localize the alt
and src
property if necessary.
alt="A picture of myself" src="/me.png" >
becomes
:alt="$t('me.alt')" :src="$t('me.src')" >
Aria Labels
Aria labels are sometimes overlooked, when translating a website. In case you use them, remember to also translate Aria labels.
becomes
Variables and pluralization in Text:
Variables in text can be challenging to translate, since you can't just provide a static string, but have to run some sort of interpolation to inject your variables and dynamically adjust the pluralization.
Usually the i18n plugin ships with this feature, but it can be pain to use it consistently since it just adds a lot of code, so sometimes it's easier to rephrase your sentences like this: "There are 5 apples in my box" becomes: "Apples in my box: 5"
There are {{ numberOfApples }} in my box.
becomes
{{ $t('applesText', numberOfApples) }}
Datetime and number formatting
Make sure to adjust how date times and numbers are formatted. In the US, decimals are separated by .
in Europe it's a ,
. Also adjust currency formats: in some languages the currency symbol is in front of the number $5.00
sometimes it's after 10,00€
. On top you need to consider conversions of other units like distance (feet to meters), weight (pounds to kilograms) or volumes (ounces to liters)…
You can either leverage your i18n plugin or use native functionality like date.toLocaleDateString(locale, {...})
(toLocaleDateString) or new Intl.NumberFormat(locale,).format(count)
(Intl).
Total cost: $5.00
becomes
{{ $t('totalCost') }}: {{ $n(5, 'currency', locale) }}
HTML
In a couple of cases I stumbled over this situation:
This is very important!
In order to translate that, without loosing the styling, you can translate the html and render it on screen:
{
...
translated: 'Das ist sehr wichtig!'
}
In Vue you can render this using the v-html
directive:
v-html="$t('translated')" />
In plain js you would do something like this:
document.getElementById("myDiv").innerHTML = $t('translated');
Note:
Rendering html strings is a potential XSS vulnerability, so you either want to avoid it if possible, or sanitize your html string before with a library like sanitize-html:
import * as sanitizeHtml from 'sanitize-html';
const translatedString = "Das ist sehr wichtig!"
const sanitized = sanitizeHtml(translatedString)
document.getElementById("myDiv").innerHTML = sanitized;
Meta tags
Don't forget to translate your pages meta tags. Meta title, description, and potentially the image are all important for consistent localization.
Welcome to my homepage!
name="description" content="Sliplane is a fully managed container as a service platform, made to simplify Docker hosting. It provides you with the tools to manage and scale your containerized applications.">
This meta section is often generated by the framework so make sure to localize it there and don't forget to check the generated html.
Alternate links
Add references to your markup, to indicate to search engines, that there are alternate versions of the page in different languages. You can do so using alternate links:
rel="alternate" hreflang="de-de" href="https://sliplane.io/de-de">
rel="alternate" hreflang="en-us" href="https://sliplane.io/">
Make sure to include all languages, even a reference to the current page itself. If you set these links via your framework it's best to inspect the generated html code, in order to make sure that everything went as expected.
Sitemap
There are different strategies for your sitemap and it mainly depends how big your sitemap is. As a reference, everything below 10k individual pages is considered small. You can get away with just a single sitemap and add alternate links like this:
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> https://sliplane.io/de-de/about
xmlns:xhtml="http://www.w3.org/1999/xhtml" rel="alternate" hreflang="de-de" href="https://sliplane.io/de-de/about" />
xmlns:xhtml="http://www.w3.org/1999/xhtml" rel="alternate" hreflang="en-us" href="https://sliplane.io/about" />
If your page is bigger, you want to work with multiple sitemaps for each language and maybe even some more nesting of sitemaps below that level.
External plugins
It's important to also localize any external plugins that you might use on your website, e.g. a chat widget or a third party form. If the whole website is in english, but the checkout form is German, it will be very bad for conversion rate, so make sure the third party plugin comes with localization.
Going Further
Now we went pretty far already and for a lot of cases that will do the trick, but I want to quickly mention some more areas that make localization tricky:
- Slugs: The words in your slug has impact on SEO. Ideally, your German pages use German words in the slug and your English pages use English words. However, maintaining a different slug for every language is making things complicated and even big companies like Microsoft Azure don't do this. It's hard to quantify the actual SEO impact, but we decided not to go down this rabbit hole and leave the slugs as they are.
- Legal requirements: different countries have different legal requirements, for example if you decide to expand to Europe, there are specific obligations to make your website compliant with the local law, like including a cookie banner, creating a data privacy statement or the way buttons have to be named and how you display prices on your website.
- Localizing your fonts: Different countries use different special characters. In Russia or China for example, you need a completely different glyph set. You only want to serve the glyphs that are needed
- Right-to left support: similar to different fonts, some countries write right to left or top to bottom. If you want to go all in on localization, this is something to think about - but it's probably going to mess up everything and makes you reconsider your career choice - so let's just all agree on silently ignoring this
- Advertising: if you run ads to your website, you might have to consider localizing them as well. Although I did not deal with this use case, a close friend of mine is running shopping ads in several different countries and it's important that the localized ads point to the right pages and pricing and shipping info is adjusted on each page.
Summary
Doing localization right, is actually pretty hard and it goes way beyond just your website. There are numerous things to keep in mind and it's very easy to miss something. By using this checklist, I was able to pull it off for our website, but it still remains a lot of work. I also made a post about 5 practical tips to help you simplify the process of internationalizing a website.