Feature Showcase: Website Mulitilanguage Support (Internationalization)

We are currently adding multilanguage support (internationalization) to the ERPNext website. This is what we have planned, we are looking forward to your feedback.

Adding the Language Picker to the Navigation on the Website

I would integrate this language picker Bootstrap Snippet Internationalization using HTML CSS jQuery flag icons can be defined in the backend but we would suggest using 2600 Flag Icon Set - Resources from GoSquared which is under MIT license. The flags would be in the current Language DocType, we should also consider uploading the flag icons with 25px to Frappe Core for usage wherever people want and populating the Flags field with those by default.

Currently the navigation looks like this:

I would add the language picker betweeen the login and the navigation like this mockup (collapsed):

When the user clicks the language they can pick among the available language options (expanded):

Adding Languages to Website Settings

  1. In Website Settings add a new section called Website Languages
  2. Add checkboxes Enable Multilanguage Support and Display Language Picker in Top Bar. Without Multilanguage Support being enabled we hide the language features in blogs, knowledge base and pages.
  3. Add a new child table Website Languages with fields Language (Link: Language), Route (Data), Default (Check, unique)

The route would be a default prefix that is added to the individual Web Page, Help Article or Blog Post route for example the “Company History” pages route in English would be route “/en” and then “company-history” so www.website.com/en/company-history while the German could be www.website.com/de/unternehmensgeschichte. This is SEO friendly.

Changes in Other DocTypes

This example is for Web Page, similar procedure would have to be done for Help Article and for Blog Post.

  1. In Web Page add Language (Link: Language [javascript limited to children in Website Languages child table]) and show it in list view if Website Settings -> Enable Multilanguage Support == TRUE.
  2. When creating a new Web Page by default put in the default language from Website Settings -> Website Languages -> default.
  3. If Web Page Language not the default language, add new field Translation of Web Page (Link: Web Page) which will link the translations with its parent.
  4. In Web Page show linked translations in Dashboard

How Language Switching Works

  1. If the user is logged in we also change his language preference in his user under /me
  2. Open the page of the translation of the source site, if you are on the home page in English you should switch to the home page in German. If you are on the contact page in English you should switch to that in German. If there is no translation for that site display the default languages site.
21 Likes

I’m surprised there has been no response to this topic. this looks like a great addition! I was just searching for how to set internationalization up with Erpnext and came across this post. sadly I see it is not in erpnext v12?

@nostahl There is open PR to solve this issue:

https://github.com/frappe/frappe/pull/8142

I want to share a workaround for multilingual support in website that I am using.

For simplicity, it is assumed that only two languages are used: English and Spanish.

First, in every Web Page add a custom metatag that set the link to another Web Page in the other language. For example, if you have a Web Page in English you have to set a metatag (Web Page > Meta Tags Section > Add Custom Tags) that sets the link to the Spanish Web Page, like this:

Do the same for every Web Page that you want to add multilingual support.

Secondly, add the following script in Website Script:

// add language picker in navbar
$("#navbarSupportedContent").after("<select id='language-select' class='form-control' style='width: auto;'><option value='es'>Español</option><option value='en'>English</option></select>")

// get the preferred language in this session and set it on the language picker
var preferred_language = frappe.get_cookie("preferred_language")
if (preferred_language){
    $('#language-select').val(preferred_language)
}

// whenever the language changes, redirect to its corresponding page in the other language
$('#language-select').on('change', function() {
  frappe.call("frappe.translate.set_preferred_language_cookie", {
    preferred_language: this.value
  }).then(function() {
    var preferred_language = frappe.get_cookie("preferred_language")
    var english_version = $('meta[name=english_version]').prop('content'); // get the link to the english version
    var spanish_version = $('meta[name=spanish_version]').prop('content'); // get the link to the spanish version
    if (preferred_language === "en" && english_version){
        window.location.pathname = english_version
    } else if (preferred_language === "es" && spanish_version){
        window.location.pathname = spanish_version
    }
  })
});

That’s it. Whenever the user change the language, is redirected to the link for the web page in the corresponding language.

Hope this helps.

4 Likes

Nice idea.

Thanks a lot for sharing this work around! It’s awesome. I didn’t even know you could add something on the navbar without editing the template html on the server.

Did you come up with this yourself by manually inspecting and trial or is there documentation to do such things?

I love the /all-products web page but I don’t think I can provide any translations there as I can’t even find it’s edit page on the desk app. Perhaps can be done by creating another custom html web page to replace it.

For now I came up with another solution, I made slight changes to @roquegv 's contribution to send webpage to same URL with “?_lang=de” addition

// add language picker in navbar
$("#navbarSupportedContent").after("<select id='language-select' class='form-control' style='margin-left: 10px; width: auto;'><option value='en'>English</option><option value='de'>Deutsch</option></select>");

// get the preferred language in this session and set it on the language picker
var preferred_language = frappe.get_cookie("preferred_language");
if (preferred_language){
    $('#language-select').val(preferred_language);
}

// whenever the language changes, redirect to its corresponding page in the other language
$('#language-select').on('change', function() {
  frappe.call("frappe.translate.set_preferred_language_cookie", {
    preferred_language: this.value
  }).then(function() {
    var preferred_language = frappe.get_cookie("preferred_language");
    var english_translation = window.location.href.split("?").shift().split("/").pop() + '?_lang=en'; // add the path to the english translation
    var german_translation = window.location.href.split("?").shift().split("/").pop() + '?_lang=de'; // add the pathto the german translation
    
    if (preferred_language === "en" && english_translation){
        window.location = english_translation;
    } else if (preferred_language === "de" && german_translation){
        window.location = german_translation;
    }
  });
});

This works well if you couple it with the Translations doc and you edit your html as described in this post → Multilingual website feature - #3 by KanchanChauhan

Advantage is default website navbar also gets translated into German. I’ve translated the privacy policy page and when language is selected it goes to the same url with “?_lang=de” addition and it translates to German.

One problem now is that although the tag remembers the language cookie, I don’t know how to implement the JS logic to go to “?_lang=de” version of the next URL. Right now if I go to home page from the German Privacy Policy, I land on the english homepage.

Does this solution can affect SEO performance?

Hello @Y_S.

This is just a hacky solution, there is no documentation for this.

If you want to translate the /all-products page and some other texts from the cart, you could go to the Website Script (/app/website-script) and put the following code (replace the Spanish texts with your German texts):

frappe._messages["Search for Products"] = "Buscar producto"
frappe._messages["Explore"] = "Ver opciones"
frappe._messages["Add to Cart"] = "Añadir al Carrito"
frappe._messages["Go to Cart"] = "Ir al Carrito"
frappe._messages["Item Group"] = "Categoría"

That is, if you find a text that does not translate automatically nor with the Translation Doctype (/app/translation), as is often the case because in the website side the frappe._messages dict, which stores the translations, is empty. So, with this code you populate the dict with the translation texts you need.

The only way I see this could work is by forcing it with JavaScript. For example, put the folllowing code in the Website Script:

var german_homepage = "/de" //change this
$("a.navbar-brand").attr("href", german_homapage)

This will change the URL from the logo in navbar. Of course, you sould implement some logic for this in order to prevent from going every time to the German homepage (ask for the preferred_language to be equals to “de”, for example). Apply the same for every link.

Hope this help.

1 Like

Probably not, because every page is independent, so the crawlers could read them without problems.

Thanks a lot for your response, I will try this out!
But will this change the text for good regardless of the language selection cookie? Meaning even for the English speaking users?

To have it documented, I just added this piece of code to the script I edited before. Again thanks to the code you shared initially. Because my code yesterday didn’t redirect to the German page if you change tabs. Now it routes to the German page (if its the user selection).

var english_translation = window.location.href.split("?").shift().split("/").pop() + '?_lang=en'; // add the path to the english translation
var german_translation = window.location.href.split("?").shift().split("/").pop() + '?_lang=de'; // add the pathto the german translation

if(preferred_language === "en" && window.location.href.split("?").pop() !== "_lang=en" ){
    window.location.href = english_translation;
    } else if (preferred_language === "de" && window.location.href.split("?").pop() !== "_lang=de" ){
        window.location.href = german_translation;
    }

Yes, you sould implement some logic for this. For example, ask for the preferred_language and then populate the frappe._messages dict.

Great!

1 Like