Translate React Public HTML files
Mbonu Blessing
Posted on February 14, 2020
I was recently tasked with translating the html files in the public folder in our react app and what I thought was a straightforward task took longer than I expected.
My research lead me to a couple of libraries like google api, translater.js, linguminum etc. But I finally settled for Transalator.js. Here is a link to the original post that introduced me to it.
What I was looking for
I needed a service that
- Would be hosted online
- Was easy to setup and lightweight
- The repository was maintained regularly
- Would be able to properly render embedded html tags
I only got 2 out of the options and that was option 2 and 3. You had to download the repo locally to use and it didn't render embedded html tags properly. The absence of option one was a good thing as it gave me the option to go through the code, seeing how it works and giving me the opportunity to make the changes I need to get what I wanted. If you are not so concerned with embedded html tags, then you just need to download the library and host it anywhere you chose.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title eo-translator="title"></title>
</head>
<body>
<div class="container">
<h1 eo-translator="greeting"></h1>
<h2 eo-translator="question"></h2>
</div>
<script src="/path/to/translate.min.js"></script>
<script type="text/javascript">
// Construct your dictionary
const dictionary = {
en: {
greeting: 'Hello',
question: 'How are you doing?',
title: 'Test title'
},
es: {
greeting: 'Hola',
question: '¿Como estas?',
title: 'Título de la prueba'
}
};
// Get the user's preferred language from the browser
let language = (window.navigator.userLanguage || window.navigator.language).split('-')[0]
const displayLanguage = language === 'es' ? 'es' : 'en';
let translator = new EOTranslator(dictionary, displayLanguage);
// The translator takes care of everything else
translator.translateDOM();
</script>
</body>
</html>
My Solution
Since the above didn't solve the display of embedded html tags, I had to kind of extract the part involved in the translator.js file, tweaked it abit and added the javascript to my html file. Here was my finally result.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title eo-translator="title"></title>
</head>
<body>
<div class="container">
<h1 eo-translator="greeting"></h1>
<h2 eo-translator="question"></h2>
</div>
<script src="/path/to/translate.min.js"></script>
<script type="text/javascript">
(function () {
// Construct your dictionary
const dictionary = {
en: {
greeting: 'Hello',
question: 'How are you doing?',
title: 'Test title'
},
es: {
greeting: 'Hola',
question: '¿Como estas?',
title: 'Título de la prueba'
}
};
// Get the user's preferred language from the browser
let language = (window.navigator.userLanguage || window.navigator.language).split('-')[0]
const displayLanguage = language === 'es' ? 'es' : 'en';
translateDOM();
function translateDOM(DOMContainer) {
const language = displayLanguage;
const container = DOMContainer || typeof document === 'object' ? document : null;
if (container) {
const elements = container.querySelectorAll('[eo-translator]');
elements.forEach((element) => translateElement(element, language));
}
}
function translateElement(DOMElement, lang) {
if (DOMElement) {
const input = DOMElement.attributes['eo-translator'].value || DOMElement.textContent || DOMElement.innerText || DOMElement.innerHTML;
// Here was where i made the change to display the embedded html tags. In the translator.js file, this line was DOMElement.innerText = translate(input);
DOMElement.innerHTML = translate(input);
}
}
function translate(input = '') {
const language = displayLanguage;
const fallback = input;
const params = {};
const frags = input.split('.').filter(frag => frag.length > 0);
let output = dictionary.hasOwnProperty(displayLanguage);
if (output) {
if (frags.length > 1) {
output = extractValue(dictionary, language, frags);
} else {
output = dictionary[language][input];
}
}
return output ? assignParams(output, params) : fallback;
}
function assignParams(raw, params) {
Object.keys(params).forEach(key => {
const pattern = new RegExp(`{${key}}`, 'g');
raw = raw.replace(pattern, params[key]);
});
return raw;
};
function extractValue(dictionary, language, frags) {
let temp = dictionary[language];
for (const frag of frags) {
temp = temp[frag] || undefined
if (!temp) {
break;
}
}
return temp;
}
})((typeof window !== 'undefined') ? window : this);
</script>
</body>
</html>
Just before I drop off, Translater.js was another good option if you are not concerned with embedded html tags. Now that I think about it, I could have worked around it. Boom, here is what I could have done. I can't believe I missed that because I was looking for a something straight forward. Here you have it. This below is a better option...
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title eo-translator="title">
Test title
<!--{es} Título de la prueba -->
</title>
</head>
<body>
<div class="container">
<h2>
Hello
<!--{es}Hola! -->
<a href="https://www.google.com/chrome/">
Link to Google Chrome
<!--{es}Enlace a Google Chrome -->
</a>
</h2>
</div>
<script src="https://unpkg.com/translater.js/dist/translater.js" type="text/javascript"></script>
<script type="text/javascript">
// You can still get the user's language with the example above and pass it to the lang option here.
var tran = new Translater({
lang:"es"
});
</script>
</body>
</html>
If you ever run into issues with no space between words with nested elements, all you have to do it add double spaces after the language definition
<!--{es} Enlace a Google Chrome-->
In conclusion, the second option gives you more control as you don't have to worry about whether the package is updated or not. It's quite simple and more bulky but I settled for that.
Sorry If it's disoriented. I am just trying my hand at writing my thinking process solving a problem. Feel free to drop comments
Posted on February 14, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.