JSFiddle - React, Tailwind, and code Playground
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.min.js"></script>
<p>
<label>
<span data-l11n>Change language:</span>
<span data-l11n="ru">Изменить язык:</span>
<select onchange="document.documentElement.lang=this.value">
<option value="en">English</option>
<option value="ru">Русский</option>
</select>
</label>
</p>
<h1>
<span data-l11n>Feedback</span>
<span data-l11n="ru">Обратная связь</span>
</h1>
<form action="javascript:void(0)" class="feedback-form">
<p>
<label>
<span data-l11n>Pick your colour</span>
<span data-l11n="ru">Выберите цвет</span>
</label>
<select name="color">
<option>
<span data-l11n>Red</span>
<span data-l11n="ru">Красный</span>
</option>
<option>
<span data-l11n>Green</span>
<span data-l11n="ru">Зелёный</span>
</option>
<option>
<span data-l11n>Blue</span>
<span data-l11n="ru">Синий</span>
</option>
</select>
</p>
<p>
<input type="text" placeholder="Localize me" />
</p>
<p>
<label>
<span data-l11n>Your email</span>
<span data-l11n="ru">Ваш email</span>
</label>
<input type="email" name="email"/>
</p>
<p>
<label>
<span data-l11n>Your comment</span>
<span data-l11n="ru">Ваш комментарий</span>
</label>
<textarea name="comment"></textarea>
</p>
<p>
<input type="checkbox" name="accept" style="width: auto;" />
<label>
<span data-l11n>I accept <a href="/en/privacy/">privacy policy</a> and <a href="/en/terms/">terms conditions</a>.</span>
<span data-l11n="ru">Я принимаю <a href="/ru/privacy/">политику конфиденциальности</a> и <a href="/ru/terms/">правила...
CSS
/* extra CSS rules for i18n */
[data-l11n=ru] {
display: none;
}
:lang(ru) > [data-l11n] {
display: none;
}
:lang(ru) > [data-l11n=ru] {
display: inline;
}
/* page CSS not related to i18n */
* {
padding: 0;
margin: 0;
}
body {
font: 100%/1.5 sans;
padding: 10px;
color: #333;
}
h1, p, form, hr {
margin-bottom: 16px;
}
input,
textarea,
button {
padding: 5px;
border: 1px solid;
}
.feedback-form,
.feedback-form input,
.feedback-form textarea {
width: 400px;
}
JavaScript
var template = Handlebars.compile(document.getElementById("hbs-template").innerHTML),
i18nStorage = {},
importString = function(lang, key, value) {
i18nStorage[lang] = i18nStorage[lang] || {};
i18nStorage[lang][key] = value;
};
importString("ru", "one", "один");
importString("ru", "two", "два");
importString("ru", "three", "три");
Handlebars.registerHelper("__", function(key) {
var content = "", lang;
content += "<span data-l11n>" + key + "</span>";
for (lang in i18nStorage) {
content += "<span data-l11n=" + lang + ">" + i18nStorage[lang][key] + "</span>";
}
return content;
});
document.getElementById("hbs-append").onclick = function() {
this.parentNode.insertAdjacentHTML("afterend", template());
};