Skip to content

Instantly share code, notes, and snippets.

@friendlyanon
Last active October 17, 2019 18:31
Show Gist options
  • Save friendlyanon/a6712241dad2f889067aecf6fdca3be3 to your computer and use it in GitHub Desktop.
Save friendlyanon/a6712241dad2f889067aecf6fdca3be3 to your computer and use it in GitHub Desktop.
const languageCache = new Map();
let visitedNodes;
let language;
const resetVisitedNodes = () => {
visitedNodes = new WeakSet();
};
const formatRegex = /\{([^\}]*)\}/gui;
function format(formatString, substitutions = "[]") {
const array = JSON.parse(substitutions);
let cursor = 0;
return formatString.replace(
formatRegex,
(_, key) => array[key === "" ? cursor++ : parseInt(key)],
);
}
function translate(node) {
visitedNodes.add(node);
const { langKey, langArgs } = node.dataset;
const translation = languageCache.get(language)[key];
const { isHTML, formatString } = typeof translation === "string" ?
{ isHTML: false, formatString: translation } :
translation;
node[isHTML ? "innerHTML" : "textContent"] = format(formatString, langArgs);
}
let observer;
function disableObserver() {
if (observer) {
observer.disconnect();
observer = null;
}
}
const shouldTranslate = node => node.nodeType === Node.ELEMENT_NODE &&
node.dataset.langKey &&
!visitedNodes.has(node);
function enableObserver() {
if (!observer) {
observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (shouldTranslate(node)) {
translate(node);
}
}
}
});
}
}
const getElementsToTranslate = () =>
document.body.querySelectorAll("[data-lang-key]");
const checkLanguage = (lang = language) => {
if (!languageCache.has(lang)) {
throw new Error("Unknown language: " + lang);
}
};
export function render() {
checkLanguage();
disableObserver();
visitedNodes.clear();
for (const element of getElementsToTranslate()) {
translate(element);
}
enableObserver();
}
export async function addRemoteLanguage(languageKey, url) {
const response = await fetch(url);
const data = await response.json();
languageCache.set(languageKey, data);
}
export function addLanguage(languageKey, data) {
languageCache.set(languageKey, data);
}
export function setLanguage(languageKey) {
checkLanguage(languageKey);
language = languageKey;
return render;
}
/*
Usage (JS):
import { setLanguage, addRemoteLanguage } from "./translate";
addRemoteLanguage("en", "/translation.en.json")
.then(() => setLanguage("en")())
Usage (HTML):
<p data-lang-key="welcome-paragraph" data-lang-args="[&quot;World&quot;]">
Usage (JSON):
{
"welcome-paragraph": {
"isHTML": false,
"formatString": "Hello, {}!"
}
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment