-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtranslator.js
More file actions
113 lines (102 loc) · 3.8 KB
/
Copy pathtranslator.js
File metadata and controls
113 lines (102 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* @file translator.js
* @description This library is a simple translator for multi-lingual content.
*
* @version 1.0.0
* @license GPLv3
* @author Hylke Hellinga
*/
/**
* Translator: A minimal, dependency-free translation library.
*/
export default class Translator {
/**
* @param {string} initialLang - The initial language code (e.g., 'en').
*/
constructor(initialLang = 'en') {
this.currentLang = initialLang;
this.translations = {};
}
/**
* Loads the translation data. This object can be fetched from a JSON file,
* an API, or generated from a database/CSV.
* @param {object} translationsData - The object containing all language translations.
*/
load(translationsData) {
this.translations = translationsData;
}
/**
* Sets the current language and re-renders the translations on the page.
* @param {string} lang - The language code to switch to.
*/
setLanguage(lang) {
if (!this.translations[lang]) {
console.error(`Language "${lang}" not found in translations.`);
return;
}
this.currentLang = lang;
console.log(`Language changed to: ${lang}`);
this.apply();
}
/**
* Retrieves a translation string for a given key.
* Handles nested keys (e.g., 'page.header.title').
* @param {string} key - The translation key.
* @param {object} [vars={}] - An object with placeholder values to replace.
* @returns {string} The translated string or the key itself if not found.
*/
get(key, vars = {}) {
// Navigate through the nested object
const langTranslations = this.translations[this.currentLang];
if (!langTranslations) return key;
let text = key.split('.').reduce((obj, i) => (obj ? obj[i] : null), langTranslations);
if (!text) {
console.warn(`Translation key "${key}" not found for language "${this.currentLang}".`);
return key;
}
// Replace placeholders like {user} with values from the vars object
for (const varName in vars) {
const regex = new RegExp(`{${varName}}`, 'g');
text = text.replace(regex, vars[varName]);
}
return text;
}
/**
* Scans the DOM, including Shadow DOMs, for elements with `data-translate`
* attributes and applies the corresponding translations.
*/
apply() {
// Start the recursive search from the main document body
this.translateNode(document.body);
}
/**
* A recursive function that translates elements in a given node and
* dives into any shadow roots it finds.
* @param {Node} rootNode - The node to search within (e.g., document.body or a shadowRoot).
*/
translateNode(rootNode) {
// 1. Find and translate all elements with the attribute in the current root
const elementsToTranslate = rootNode.querySelectorAll('[data-translate]');
elementsToTranslate.forEach(el => {
const key = el.getAttribute('data-translate');
let vars = {};
const varsAttr = el.getAttribute('data-translate-vars');
if (varsAttr) {
try {
vars = JSON.parse(varsAttr);
} catch (e) {
console.error(`Error parsing data-translate-vars for key "${key}":`, e);
}
}
el.innerHTML = this.get(key, vars);
});
// 2. Find all elements in the current root that might have a shadow root
const allElements = rootNode.querySelectorAll('*');
allElements.forEach(el => {
// 3. If an element has a shadowRoot, recurse into it
if (el.shadowRoot) {
this.translateNode(el.shadowRoot);
}
});
}
}