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

Apr 6, 2025 - 09:41
 0
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:

  1. Separate websites for each language
  2. Same website but different content
  3. 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.

 href="/">Homepage

becomes

 :href="localePath('/')">{{ $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.

 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.

 aria-label="Open Menu"> menu />

becomes

 :aria-label="$t('open-menu')"> menu />

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.

</span>Welcome to my homepage!<span class="nt"><title></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"description"</span> <span class="na">content=</span><span class="s">"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."</span><span class="nt">></span>
</code></pre>

</div>



<p>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.

<h3>
  
  
  Alternate links
</h3>

<p>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:<br>


<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><link</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"de-de"</span> <span class="na">href=</span><span class="s">"https://sliplane.io/de-de"</span><span class="nt">></span>

<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"en-us"</span> <span class="na">href=</span><span class="s">"https://sliplane.io/"</span><span class="nt">></span>
</code></pre>

</div>



<p>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.

<h3>
  
  
  Sitemap
</h3>

<p>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:<br>


<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="nt"><url</span> <span class="na">xmlns=</span><span class="s">"http://www.sitemaps.org/schemas/sitemap/0.9"</span><span class="nt">><loc></span>https://sliplane.io/de-de/about<span class="nt"></loc></span>
<span class="nt"><xhtml:link</span> <span class="na">xmlns:xhtml=</span><span class="s">"http://www.w3.org/1999/xhtml"</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"de-de"</span> <span class="na">href=</span><span class="s">"https://sliplane.io/de-de/about"</span> <span class="nt">/></span>
<span class="nt"><xhtml:link</span> <span class="na">xmlns:xhtml=</span><span class="s">"http://www.w3.org/1999/xhtml"</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"en-us"</span> <span class="na">href=</span><span class="s">"https://sliplane.io/about"</span> <span class="nt">/></span>
<span class="nt"></url></span>

</code></pre>

</div>



<p>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.

<h3>
  
  
  External plugins
</h3>

<p>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.

<h3>
  
  
  Going Further
</h3>

<p>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:

<ul>
<li>Slugs: The words in your <a href="https://www.semrush.com/blog/what-is-a-url-slug/" rel="noopener noreferrer">slug has impact on SEO</a>. 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.</li>
<li>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. </li>
<li>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</li>
<li>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</li>
<li>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.</li>
</ul>

<h2>
  
  
  Summary
</h2>

<p>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 <a href="https://dev.to/wimadev/5-practical-tips-for-internationalizing-your-website-3a5c">5 practical tips</a> to help you simplify the process of internationalizing a website.                        </div>
                                            <div class="d-flex flex-row-reverse mt-4">
                            <a href="https://dev.to/wimadev/11-step-checklist-for-internationalizing-a-website-4mof" class="btn btn-md btn-custom" target="_blank" rel="nofollow">
                                Read More                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="m-l-5" viewBox="0 0 16 16">
                                    <path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
                                </svg>
                            </a>
                        </div>
                                        <div class="d-flex flex-row post-tags align-items-center mt-5">
                        <h2 class="title">Tags:</h2>
                        <ul class="d-flex flex-row">
                                                    </ul>
                    </div>
                    <div class="post-next-prev mt-5">
                        <div class="row">
                            <div class="col-sm-6 col-xs-12 left">
                                                                    <div class="head-title text-end">
                                        <a href="https://news.djaz.app/from-frontend-to-go-why-im-switching-gears">
                                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
                                                <path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"/>
                                            </svg>
                                            Previous Article                                        </a>
                                    </div>
                                    <h3 class="title text-end">
                                        <a href="https://news.djaz.app/from-frontend-to-go-why-im-switching-gears">From Frontend to Go: Why I’m Switching Gears</a>
                                    </h3>
                                                            </div>
                            <div class="col-sm-6 col-xs-12 right">
                                                                    <div class="head-title text-start">
                                        <a href="https://news.djaz.app/webassembly-integration-with-javascript">
                                            Next Article                                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
                                                <path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
                                            </svg>
                                        </a>
                                    </div>
                                    <h3 class="title text-start">
                                        <a href="https://news.djaz.app/webassembly-integration-with-javascript">WebAssembly Integration with JavaScript</a>
                                    </h3>
                                                            </div>
                        </div>
                    </div>
                                        <section class="section section-related-posts mt-5">
                        <div class="row">
                            <div class="col-12">
                                <div class="section-title">
                                    <div class="d-flex justify-content-between align-items-center">
                                        <h3 class="title">Related Posts</h3>
                                    </div>
                                </div>
                                <div class="section-content">
                                    <div class="row">
                                                                                            <div class="col-sm-12 col-md-6 col-lg-4">
                                                        <div class="post-item">
                                                                                                                            <div class="image ratio">
                                                                    <a href="https://news.djaz.app/2685-count-the-number-of-complete-components">
                                                                        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAEYAQMAAAD1c2RPAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAACVJREFUaN7twQEBAAAAgqD+r26IwAAAAAAAAAAAAAAAAAAAACDoP3AAASZRMyIAAAAASUVORK5CYII=" data-src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxexi2fucykznwo9q8ks6.png" alt="2685. Count the Number of Complete Components" class="img-fluid lazyload" width="269" height="160"/>
                                                                                                                                            </a>
                                                                </div>
                                                                                                                        <h3 class="title fsize-16"><a href="https://news.djaz.app/2685-count-the-number-of-complete-components">2685. Count the Number of Complete Components</a></h3>
                                                            <p class="small-post-meta">    <span>Mar 22, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
                                                        </div>
                                                    </div>
                                                                                                    <div class="col-sm-12 col-md-6 col-lg-4">
                                                        <div class="post-item">
                                                                                                                            <div class="image ratio">
                                                                    <a href="https://news.djaz.app/lazypromise-typed-errors-and-cancelability-for-lazy-people-who-dont-want-to-learn-a-new-api">
                                                                        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAEYAQMAAAD1c2RPAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAACVJREFUaN7twQEBAAAAgqD+r26IwAAAAAAAAAAAAAAAAAAAACDoP3AAASZRMyIAAAAASUVORK5CYII=" data-src="https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcznlhxjgtcwnod7svxxu.jpg" alt="LazyPromise: typed errors and cancelability for lazy people who don't want to learn a new API" class="img-fluid lazyload" width="269" height="160"/>
                                                                                                                                            </a>
                                                                </div>
                                                                                                                        <h3 class="title fsize-16"><a href="https://news.djaz.app/lazypromise-typed-errors-and-cancelability-for-lazy-people-who-dont-want-to-learn-a-new-api">LazyPromise: typed errors and cancelability for lazy pe...</a></h3>
                                                            <p class="small-post-meta">    <span>Feb 14, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
                                                        </div>
                                                    </div>
                                                                                                    <div class="col-sm-12 col-md-6 col-lg-4">
                                                        <div class="post-item">
                                                                                                                            <div class="image ratio">
                                                                    <a href="https://news.djaz.app/lifetimes-in-rust-preventing-dangling-references">
                                                                        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAEYAQMAAAD1c2RPAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAACVJREFUaN7twQEBAAAAgqD+r26IwAAAAAAAAAAAAAAAAAAAACDoP3AAASZRMyIAAAAASUVORK5CYII=" data-src="https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9stgoypm8bvwhupgu9h.jpg" alt="Lifetimes in Rust: Preventing Dangling References" class="img-fluid lazyload" width="269" height="160"/>
                                                                                                                                            </a>
                                                                </div>
                                                                                                                        <h3 class="title fsize-16"><a href="https://news.djaz.app/lifetimes-in-rust-preventing-dangling-references">Lifetimes in Rust: Preventing Dangling References</a></h3>
                                                            <p class="small-post-meta">    <span>Mar 29, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
                                                        </div>
                                                    </div>
                                                                                    </div>
                                </div>
                            </div>
                        </div>
                    </section>
                                            <section class="section section-comments mt-5">
                            <div class="row">
                                <div class="col-12">
                                    <div class="nav nav-tabs" id="navTabsComment" role="tablist">
                                                                                    <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#navComments" type="button" role="tab">Comments</button>
                                                                            </div>
                                    <div class="tab-content" id="navTabsComment">
                                                                                    <div class="tab-pane fade show active" id="navComments" role="tabpanel" aria-labelledby="nav-home-tab">
                                                    <form id="add_comment">
        <input type="hidden" name="parent_id" value="0">
        <input type="hidden" name="post_id" value="259978">
        <div class="form-row">
            <div class="row">
                <div class="form-group col-md-6">
                    <label>Name</label>
                    <input type="text" name="name" class="form-control form-input" maxlength="40" placeholder="Name">
                </div>
                <div class="form-group col-md-6">
                    <label>Email</label>
                    <input type="email" name="email" class="form-control form-input" maxlength="100" placeholder="Email">
                </div>
            </div>
        </div>
        <div class="form-group">
            <label>Comment</label>
            <textarea name="comment" class="form-control form-input form-textarea" maxlength="4999" placeholder="Leave your comment..."></textarea>
        </div>
        <div class="form-group">
            <script src="https://www.google.com/recaptcha/api.js?hl=en"></script><div class="g-recaptcha" data-sitekey="6LduZ7IqAAAAAKfe7AeVbVcTGz_oE2naGefqcRuL" data-theme="dark"></div>        </div>
        <button type="submit" class="btn btn-md btn-custom">Post Comment</button>
    </form>
<div id="message-comment-result" class="message-comment-result"></div>
                                                <div id="comment-result">
                                                    <input type="hidden" value="5" id="post_comment_limit">
<div class="row">
    <div class="col-sm-12">
        <div class="comments">
                        <ul class="comment-list">
                            </ul>
        </div>
    </div>
    </div>                                                </div>
                                            </div>
                                                                            </div>
                                </div>
                            </div>
                        </section>
                                    </div>
            </div>
            <div class="col-md-12 col-lg-4">
                <div class="col-sidebar sticky-lg-top">
    <div class="row">
        <div class="col-12">
                    <div class="sidebar-widget">
            <div class="widget-head"><h4 class="title">Popular Posts</h4></div>
            <div class="widget-body">
                <div class="row">
                                                <div class="col-12">
                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/16-malicious-chrome-extensions-infected-over-32-million-users">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://i0.wp.com/blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQzZtyyxiEpBIayXlL7AI1V6fCbWpv2fqRhd4xDCZKhrFEc_bMhj_7PG2QLP3LALcpnF2VggeDBu1XWL0Ella_-VpPcxp4_QnLRVZ-lueio-S9SZwrGuyUvh4V37YTvwhcUxfrAAMUXcKPkYv5ejSLMz6GPHW-xaKSEusr9RvLW4rHYx0-KRXQuQCki9w/s16000/16 Malicious Chrome Extensions Infected Over 3.2 Million Users.webp?#" alt="16 Malicious Chrome Extensions Infected Over 3.2 Million Users" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/16-malicious-chrome-extensions-infected-over-32-million-users">16 Malicious Chrome Extensions Infected Over 3.2 M...</a></h3>
        <p class="small-post-meta">    <span>Feb 25, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                            </div>
                                                    <div class="col-12">
                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/anime-reborn-codes-february-2025">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://www.pcgamesn.com/wp-content/sites/pcgamesn/2024/11/anime-reborn-codes.jpg?#" alt="Anime Reborn codes February 2025" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/anime-reborn-codes-february-2025">Anime Reborn codes February 2025</a></h3>
        <p class="small-post-meta">    <span>Feb 2, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                            </div>
                                                    <div class="col-12">
                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/outsmarting-the-clock-a-detectives-guide-to-avoiding-tle-in-programming">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fis8whgybcav4yy175v3v.jpeg" alt="Outsmarting the Clock: A Detective's Guide to Avoiding TLE in Programming" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/outsmarting-the-clock-a-detectives-guide-to-avoiding-tle-in-programming">Outsmarting the Clock: A Detective's Guide to Avoi...</a></h3>
        <p class="small-post-meta">    <span>Feb 2, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                            </div>
                                                    <div class="col-12">
                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/researchers-from-stanford-uc-berkeley-and-eth-zurich-introduces-warp-an-efficient-multi-vector-retrieval-engine-for-faster-and-scalable-search">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="http://www.marktechpost.com/wp-content/uploads/2025/02/Screenshot-2025-02-01-at-9.57.33 AM.png" alt="Researchers from Stanford, UC Berkeley and ETH Zurich Introduces WARP: An Efficient Multi-Vector Retrieval Engine for Faster and Scalable Search" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/researchers-from-stanford-uc-berkeley-and-eth-zurich-introduces-warp-an-efficient-multi-vector-retrieval-engine-for-faster-and-scalable-search">Researchers from Stanford, UC Berkeley and ETH Zur...</a></h3>
        <p class="small-post-meta">    <span>Feb 1, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                            </div>
                                                    <div class="col-12">
                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/monitoring-the-yezin-dam-a-journey-through-time-with-computer-vision">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fearthengine.google.com%2Fstatic%2Fimages%2Ffb-landsat-sofala.jpg" alt="Monitoring the Yezin Dam: A Journey Through Time with Computer Vision" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/monitoring-the-yezin-dam-a-journey-through-time-with-computer-vision">Monitoring the Yezin Dam: A Journey Through Time w...</a></h3>
        <p class="small-post-meta">    <span>Feb 2, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                            </div>
                                        </div>
            </div>
        </div>
            </div>
    </div>
</div>            </div>
        </div>
    </div>
</section>
    <style>
        .post-text img {
            display: none !important;
        }

        .post-content .post-summary {
            display: none;
        }
    </style>
<script type="application/ld+json">[{
"@context": "http://schema.org",
"@type": "Organization",
"url": "https://news.djaz.app",
"logo": {"@type": "ImageObject","width": 190,"height": 60,"url": "https://news.djaz.app/assets/img/logo.svg"},"sameAs": []
},
{
    "@context": "http://schema.org",
    "@type": "WebSite",
    "url": "https://news.djaz.app",
    "potentialAction": {
        "@type": "SearchAction",
        "target": "https://news.djaz.app/search?q={search_term_string}",
        "query-input": "required name=search_term_string"
    }
}]
</script>
<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "NewsArticle",
    "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://news.djaz.app/11-step-checklist-for-internationalizing-a-website"
    },
    "headline": "11 Step Checklist for Internationalizing a Website",
    "name": "11 Step Checklist for Internationalizing a Website",
    "articleSection": "dev.to",
    "image": {
        "@type": "ImageObject",
        "url": "https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdl9srygk743d24ml1kfc.jpg",
        "width": 750,
        "height": 500
    },
    "datePublished": "2025-04-06T09:41:32+0100",
    "dateModified": "2025-04-06T09:41:32+0100",
    "inLanguage": "en",
    "keywords": ", Step, Checklist, for, Internationalizing, Website",
    "author": {
        "@type": "Person",
        "name": "tedwalid"
    },
    "publisher": {
    "@type": "Organization",
    "name": "Djaz News",
    "logo": {
        "@type": "ImageObject",
        "width": 190,
        "height": 60,
        "url": "https://news.djaz.app/assets/img/logo.svg"
    }
    },
    "description": "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"
}
</script>
    <footer id="footer">
        <div class="footer-inner">
            <div class="container-xl">
                <div class="row justify-content-between">
                    <div class="col-sm-12 col-md-6 col-lg-4 footer-widget footer-widget-about">
                        <div class="footer-logo">
                            <img src="https://news.djaz.app/assets/img/logo-footer.svg" alt="logo" class="logo" width="240" height="90">
                        </div>
                        <div class="footer-about">
                            Djaz News is your go-to source for the latest tech news and insightful analysis, specifically curated for users of AI-powered web tools within the Djaz ecosystem. We provide in-depth coverage of emerging trends, practical tips for leveraging AI in your workflows, and exclusive updates on Djaz platform features to help you maximize your productivity and stay ahead of the curve.                        </div>
                    </div>
                    <div class="col-sm-12 col-md-6 col-lg-4 footer-widget">
                        <h4 class="widget-title">Most Viewed Posts</h4>
                        <div class="footer-posts">
                                                                <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/16-malicious-chrome-extensions-infected-over-32-million-users">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://i0.wp.com/blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQzZtyyxiEpBIayXlL7AI1V6fCbWpv2fqRhd4xDCZKhrFEc_bMhj_7PG2QLP3LALcpnF2VggeDBu1XWL0Ella_-VpPcxp4_QnLRVZ-lueio-S9SZwrGuyUvh4V37YTvwhcUxfrAAMUXcKPkYv5ejSLMz6GPHW-xaKSEusr9RvLW4rHYx0-KRXQuQCki9w/s16000/16 Malicious Chrome Extensions Infected Over 3.2 Million Users.webp?#" alt="16 Malicious Chrome Extensions Infected Over 3.2 Million Users" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/16-malicious-chrome-extensions-infected-over-32-million-users">16 Malicious Chrome Extensions Infected Over 3.2 M...</a></h3>
        <p class="small-post-meta">    <span>Feb 25, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                                                                    <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/anime-reborn-codes-february-2025">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://www.pcgamesn.com/wp-content/sites/pcgamesn/2024/11/anime-reborn-codes.jpg?#" alt="Anime Reborn codes February 2025" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/anime-reborn-codes-february-2025">Anime Reborn codes February 2025</a></h3>
        <p class="small-post-meta">    <span>Feb 2, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                                                                    <div class="tbl-container post-item-small">
            <div class="tbl-cell left">
                            <div class="image">
                    <a href="https://news.djaz.app/outsmarting-the-clock-a-detectives-guide-to-avoiding-tle-in-programming">
                        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fis8whgybcav4yy175v3v.jpeg" alt="Outsmarting the Clock: A Detective's Guide to Avoiding TLE in Programming" class="img-fluid lazyload" width="130" height="91"/>
                                            </a>
                </div>
                    </div>
        <div class="tbl-cell right">
        <h3 class="title"><a href="https://news.djaz.app/outsmarting-the-clock-a-detectives-guide-to-avoiding-tle-in-programming">Outsmarting the Clock: A Detective's Guide to Avoi...</a></h3>
        <p class="small-post-meta">    <span>Feb 2, 2025</span>
    <span><i class="icon-comment"></i> 0</span>
</p>
    </div>
</div>                                                        </div>
                    </div>
                    <div class="col-sm-12 col-md-6 col-lg-4 footer-widget">
                                                    <h4 class="widget-title">Newsletter</h4>
                            <div class="newsletter">
                                <p class="description">Join our subscribers list to get the latest news, updates and special offers directly in your inbox</p>
                                <form id="form_newsletter_footer" class="form-newsletter">
                                    <div class="newsletter-inputs">
                                        <input type="email" name="email" class="form-control form-input newsletter-input" maxlength="199" placeholder="Email">
                                        <button type="submit" name="submit" value="form" class="btn btn-custom newsletter-button">Subscribe</button>
                                    </div>
                                    <input type="text" name="url">
                                    <div id="form_newsletter_response"></div>
                                </form>
                            </div>
                                                <div class="footer-social-links">
                            <ul>
                                                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="footer-copyright">
            <div class="container-xl">
                <div class="row align-items-center">
                    <div class="col-sm-12 col-md-6">
                        <div class="copyright text-start">
                            Copyright 2025 Djaz.app - All Rights Reserved                        </div>
                    </div>
                    <div class="col-sm-12 col-md-6">
                        <div class="nav-footer text-end">
                            <ul>
                                                                            <li><a href="https://news.djaz.app/terms-conditions">Terms & Conditions </a></li>
                                                                                    <li><a href="https://news.djaz.app/cookies-policy">Cookies Policy </a></li>
                                                                                    <li><a href="https://news.djaz.app/privacy-policy">Privacy Policy </a></li>
                                                                                    <li><a href="https://news.djaz.app/publish-with-us">Publish With Us </a></li>
                                                                                    <li><a href="https://news.djaz.app/download-app">Download App </a></li>
                                                                                    <li><a href="https://news.djaz.app/delete-your-account">Delete Your Account </a></li>
                                                                    </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </footer>
    <a href="#" class="scrollup"><i class="icon-arrow-up"></i></a>
    <div class="cookies-warning">
        <button type="button" aria-label="close" class="close" onclick="closeCookiesWarning();">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
                <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
            </svg>
        </button>
        <div class="text">
            <p>This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies.</p>        </div>
        <button type="button" class="btn btn-md btn-block btn-custom" aria-label="close" onclick="closeCookiesWarning();">Accept Cookies</button>
    </div>
    <script src="https://news.djaz.app/assets/themes/magazine/js/jquery-3.6.1.min.js "></script>
    <script src="https://news.djaz.app/assets/vendor/bootstrap/js/bootstrap.bundle.min.js "></script>
    <script src="https://news.djaz.app/assets/themes/magazine/js/plugins-2.3.js "></script>
    <script src="https://news.djaz.app/assets/themes/magazine/js/script-2.3.min.js "></script>
    <script>$("form[method='post']").append("<input type='hidden' name='sys_lang_id' value='1'>");</script>
    <script>if ('serviceWorker' in navigator) {window.addEventListener('load', function () {navigator.serviceWorker.register('https://news.djaz.app/pwa-sw.js').then(function (registration) {}, function (err) {console.log('ServiceWorker registration failed: ', err);}).catch(function (err) {console.log(err);});});} else {console.log('service worker is not supported');}</script>
<!-- Matomo -->
<script>
  var _paq = window._paq = window._paq || [];
  /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
  _paq.push(['trackPageView']);
  _paq.push(['enableLinkTracking']);
  (function() {
    var u="//analytics.djaz.one/";
    _paq.push(['setTrackerUrl', u+'matomo.php']);
    _paq.push(['setSiteId', '17']);
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
    g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
  })();
</script>
<!-- End Matomo Code -->    </body>
    </html>