[TUTORIAL] Full Multi Language Site - Easy to set-up and to use!

Its finally possible. Multi Language Webflow Pages

You can use it in your CMS or Static pages and you can change the language with Buttons or with the Link - how you like. Its really easy to implement and it’s free!

You find the actually Tutorial with the newest Updates and a Live Example here:
New Tutorial

Lets do it:

Put this into your Before </body> tag:

<script>
var DEAFULT_LANG = "en";
var LANG_REG_EXP = /\[\[([a-z]{2})\]\]([^\[]+)/g;
var isStorageEnabled = ! (typeof localStorage == 'undefined');
var user_lang = (navigator.userLanguage||navigator.browserLanguage||navigator.language||DEAFULT_LANG).substr(0,2);

var getLangParam = function(){
    var arr = /lang=([a-z]{2})/g.exec(location.search);
    return arr ? arr[1] : null;
}

var getLangFromStorage = function(){
    return isStorageEnabled ? localStorage.getItem('lang') : undefined;
}

var setLang = function(lang){
    user_lang = lang;
    if(isStorageEnabled){
        localStorage.setItem('lang', user_lang);
    }
    applyLang();
}

var applyLang = function(){
    globalDict.forEach(function(v){
        $(v.elm).html(v.dict[user_lang]);
    });
}

function textNodesUnder(el){
  var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_ALL,null,false);
  while(n=walk.nextNode()){
      a.push(n);
  }
  return a;
}

var globalDict = [];

$(function(){
    
    user_lang = getLangParam() || getLangFromStorage() || user_lang;

    if(isStorageEnabled){
        localStorage.setItem('lang', user_lang);
    }

    // bugfix for IE11
    // if multilingual sentence is divided into sevaral text node, restore original text node
    $("*").each(function(i,v){
        if(LANG_REG_EXP.test($(this).text().replace(/\n/g,"")) && $(this).html().indexOf("<") == -1){
            $(this).text($(this).text().replace(/\n/g,""));
        }
        var $v = $("#" + $(this).attr("id"));
        if($v.length > 0 && LANG_REG_EXP.test($v.text().replace(/\n/g,"")) && $v.html().indexOf("<") == -1){
            $v.text($v.text().replace(/\n/g,""));
        }
    })
    
    textNodesUnder(document).filter(function(v){
        return LANG_REG_EXP.test(v.nodeValue);
    }).forEach(function(v,i){
        var dict = {};
        var arr;
        while((arr = LANG_REG_EXP.exec(v.nodeValue)) != null){
            dict[arr[1]] = arr[2];
        }
        globalDict.push({elm:$(v).parent()[0], dict:dict});        
    });
    applyLang();
});
</script>

Thats it! Now lets use it:

If you write your Text, write it like this:
[[en]]This is English[[de]]Das ist Deutsch

You can add as many Languages you want.

To select the right language, you have now two ways.

Way 1 - The Link Way

To open the website with the right language, just type ?lang=en at the end of your link
The script shows you now only the right language.

Way 2 - The Button Way

You want a Button that can change the language without a reload? Then use this.

Put this into your Inside <head> tag:

<script>
    window.onload = function() {
        var anchors = document.getElementsByTagName('*');
        for(var i = 0; i < anchors.length; i++) {
            var anchor = anchors[i];
            anchor.onclick = function() {
                code = this.getAttribute('whenClick');
                eval(code);   
            }
        }
    }
</script>

Put a Button on your Page and set a custom Attribute
Name: whenClick
Value: setLang('en')

multilanguage

__

You can use this method on your Collection Pages too.
The selected language kept in memory.

So, i hope it helps a bit.

Extra - The Dream for your Clients

If you want two separate Text Blocks in your CMS Collection, there is a great way.

Set your Text Blocks up in the Collection Settings like you normally do.

Put this after your Script in Before </body> tag

<!-- Language: Englisch -->
    <script>
    	$(".english").html(function(i,v) {
        return "[[en]]" + v + "[[de]] <b></b>";});
    </script>

Copy this for every language you want and change the [[en]] and the .english to your language. Then the second [[de]] to the language they should don’t show up .

Example Germany:

<!-- Language: German -->
    <script>
    	$(".german").html(function(i,v) {
        return "[[de]]" + v + "[[en]] <b></b>";});
    </script>

If you now add your texts to your Page, add the class of your language .english for example.
Include both Texts on your Page - The Script will only show the right one.

Thats it!

43 Likes

You’re an angel - life saver solution

3 Likes

This sounds not bad, but what is with SEO will this affect it and in which way? Also I would know how the workflow for clients would be when they edit text on the page.

3 Likes

Its awesome for Clients! :slight_smile: If you use the CMS Variant, the Client have no differences to before. For the other its just adding the [[en]]

1 Like

Ok I will try this out, but whats about the SEO. Will this affect the SEO positive or negative?

2 Likes

Google see all versions of the text, because you write it on your page. So i think it’s worked.

2 Likes

Hi angro,

This is great. I was able to make my project multilingual by following your instructions. (I used the 2nd way)

However, I’m having trouble doing the two separate Text Blocks in CMS Collection. Can you make a video to explain step-by-step? That would be awesome.

Thanks,
Simon

2 Likes

Nice work with this @angro :star2:

1 Like

one Text Block for Englisch, one for your second language.
In the CMS Page you must show both - but with the Code Snippet it shows only the right language if you open your website

Hope this makes it a bit clearer :slight_smile:

Still no luck.

Here’s what I have:

  1. the button header code

  2. the buttons with the custom attribute

  3. the footer code

  4. 2 Extra code below the footer code

  5. In the Collection List, 2 separate Collection Fields: one for English, one for Chinese. The text inside is written normally, without [[en]] or [[zh]] in the beginning.

  6. 2 Text Blocks for each language. The Text Block for English has a .english class, the Text Block for Chinese has a .chinese class.

I published my site and preview. I see all the English text with [[en]] on both ends. Then when I click the Chinese Button, nothing happens. Then when I click the English Button, the all the Text Blocks with .english class disappears.

Here’s my website.

Am I missing something?
Thanks.

2 Likes

can you send me a read only link?

Here you go.

https://preview.webflow.com/preview/kanmedicalspa?utm_source=kanmedicalspa&preview=e88d37b5c3d121d522da80d2271d2989

Ah damn it! I wrote it wrong in the tutorial

So, you have to write

$(".english").html(function(i,v) {
    return "[[en]]" + v + "[[zh]] <b></b>";});

Your First Language and then your second - I’m sorry.

It worked! Thank you @angro !

Just add more clarity to future readers. Here’s the full script.

<!-- Language: Englisch -->
<script>
	$(".english").html(function(i,v) {
    return "[[en]]" + v + "[[zh]] <b></b>";});
</script>

<!-- Language: Chinese -->
<script>
	$(".chinese").html(function(i,v) {
    return "[[zh]]" + v + "[[en]] <b></b>";});
</script>
2 Likes

nice - glad to help :slight_smile:
I changed the Tutorial a bit so i hope everyone will understand it now.

I have a problem with rich textfields. the unselected language and its tag [[en]] are still visible. the rest works as expected. https://stadt-land-fluss.webflow.io/tmp/kirchturmdenken-sprachentest?lang=de

3 Likes

There you go @jgautier I figured it out yesterday with @angro

You’ll need to change the default language and the 2 languages in the last 2 scripts.

<script>
var DEAFULT_LANG = "fr";
var LANG_REG_EXP = /\[\[([a-z]{2})\]\]([^\[]+)/g;
var isStorageEnabled = ! (typeof localStorage == 'undefined');
var user_lang = (navigator.userLanguage||navigator.browserLanguage||navigator.language||DEAFULT_LANG).substr(0,2);

var getLangParam = function(){
    var arr = /lang=([a-z]{2})/g.exec(location.search);
    return arr ? arr[1] : null;
}

var getLangFromStorage = function(){
    return isStorageEnabled ? localStorage.getItem('lang') : undefined;
}

var setLang = function(lang){
    user_lang = lang;
    if(isStorageEnabled){
        localStorage.setItem('lang', user_lang);
    }
    applyLang();
}

var applyLang = function(){
    globalDict.forEach(function(v){
        $(v.elm).html(v.dict[user_lang]);
    });
}

function textNodesUnder(el){
  var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_ALL,null,false);
  while(n=walk.nextNode()){
      a.push(n);
  }
  return a;
}

var globalDict = [];

$(function(){
    
    user_lang = getLangParam() || getLangFromStorage() || user_lang;

    if(isStorageEnabled){
        localStorage.setItem('lang', user_lang);
    }

    // bugfix for IE11
    // if multilingual sentence is divided into sevaral text node, restore original text node
    $("*").each(function(i,v){
        if(LANG_REG_EXP.test($(this).text().replace(/\n/g,"")) && $(this).html().indexOf("<") == -1){
            $(this).text($(this).text().replace(/\n/g,""));
        }
        var $v = $("#" + $(this).attr("id"));
        if($v.length > 0 && LANG_REG_EXP.test($v.text().replace(/\n/g,"")) && $v.html().indexOf("<") == -1){
            $v.text($v.text().replace(/\n/g,""));
        }
    })
    
    textNodesUnder(document).filter(function(v){
        return LANG_REG_EXP.test(v.nodeValue);
    }).forEach(function(v,i){
        var dict = {};
        var arr;
        while((arr = LANG_REG_EXP.exec(v.nodeValue)) != null){
            dict[arr[1]] = arr[2];
        }
        globalDict.push({elm:$(v).parent()[0], dict:dict});        
    });
    applyLang();
});
</script>

<!-- Language: French -->
<script>
	$(".french *:not(ul)").html(function(i,v) {
    return "[[fr]]" + v + "[[en]]  <b></b>";});
</script>

<!-- Language: English -->
<script>
	$(".english *:not(ul)").html(function(i,v) {
    return "[[en]]" + v + "[[fr]]  <b></b>";});
</script>

<style>
   ul{
    list-style:none;
   }
</style>
2 Likes

There it is!
Live Example

I hope everything is now a bit clearer - if not, you can ask here :slight_smile:

2 Likes

@donaldsv @angro I have still the same problem. Do I have to use the .english-class on cms pages? Right now I am using the tags to separate the languages in the same collection-field. This works but not for rich-text-fields.

You need 2 separate fields in your collection. On your site you want to have 2 text blocks with the language class (no need to put [[en]]). So in the designer you’ll see 2 text blocks and on the published site you’ll only see 1 language at a time.

Example: For static text, use [[en]]your text

and for CMS collections, use 2 fields for each types and give them respective classes.

Example:
Title EN (.english)
Title FR (.french)
Text EN (.english)
Text FR (.french)

Note that if you use the code for rich text elements for your CMS content, if you have some Plain text, you’ll need to put it inside a div and give the language class to the div and not to the plain text since this code is looking for divs with a language class instead of the [[en]] tag. (That’s only apply to CMS)