Skip to content

Commit b5f5d53

Browse files
author
Sebi Nemeth
committed
full refactor
1 parent bf2fcac commit b5f5d53

File tree

2 files changed

+244
-239
lines changed

2 files changed

+244
-239
lines changed

dist/index.js

Lines changed: 105 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,68 @@ class ZeroWidthEncoder {
107107
return text;
108108
}
109109
}
110-
class LiveTranslatorEnabler {
111-
constructor(options, callback) {
110+
class LiveTranslatorManager {
111+
constructor(options) {
112112
this._enabled = false;
113-
this._persist = options.persist || false;
114113
this._options = options;
115-
this._callback = callback;
114+
this._zwEncoder = new ZeroWidthEncoder();
115+
// handle persistance
116116
const savedRaw = localStorage.getItem('live-translator-enabled');
117-
if (this._persist && savedRaw) {
117+
if (this._options.persist && savedRaw) {
118118
const saved = JSON.parse(savedRaw);
119119
if (typeof saved === 'boolean') {
120120
this.toggle(saved);
121121
}
122122
}
123+
// initialize UI
124+
this._enableButton = document.createElement('button');
125+
this._indicator = document.createElement('span');
126+
const style = document.createElement('style');
127+
style.id = 'live-translator-plugin-style';
128+
style.innerHTML = css;
129+
document.head.appendChild(style);
130+
this._enableButton.innerText = 'LT';
131+
this._enableButton.classList.add('live-translator-enable-button');
132+
this._indicator.classList.add('live-translator-enable-button-indicator');
133+
this._enableButton.appendChild(this._indicator);
134+
this._enableButton.addEventListener('click', () => {
135+
this.toggle();
136+
this.refreshI18n();
137+
this.render();
138+
});
139+
document.body.appendChild(this._enableButton);
140+
// initialize encode
141+
const originalFormatter = this._options.i18n.formatter;
142+
this._options.i18n.formatter = {
143+
interpolate(message, values, path) {
144+
const meta = this._zwEncoder.encode(JSON.stringify({
145+
message,
146+
values,
147+
path,
148+
locale: this._options.i18n.locale,
149+
}));
150+
const original = originalFormatter.interpolate(message, values, path);
151+
return (original && this._enabled) ? [meta, ...original] : original;
152+
},
153+
};
154+
// initialize decode & render
155+
const throttler = (0, throttle_1.default)(() => this.render(), 800);
156+
const observer = new MutationObserver(throttler);
157+
observer.observe(document.documentElement, {
158+
subtree: true,
159+
attributes: true,
160+
characterData: true,
161+
childList: false,
162+
});
163+
document.documentElement.addEventListener('mousemove', throttler);
164+
// render for the first time
165+
this.refreshI18n();
166+
this.render();
123167
}
124-
enabled() {
125-
return this._enabled;
168+
refreshI18n() {
169+
const originalLocale = this._options.i18n.locale;
170+
this._options.i18n.locale = '';
171+
this._options.i18n.locale = originalLocale;
126172
}
127173
toggle(enable) {
128174
if (enable !== undefined) {
@@ -131,14 +177,59 @@ class LiveTranslatorEnabler {
131177
else {
132178
this._enabled = !this._enabled;
133179
}
134-
if (this._persist) {
180+
if (this._options.persist) {
135181
localStorage.setItem('live-translator-enabled', JSON.stringify(this._enabled));
136182
}
137-
// Refresh translations to show immediately
138-
const originalLocale = this._options.i18n.locale;
139-
this._options.i18n.locale = '';
140-
this._options.i18n.locale = originalLocale;
141-
this._callback();
183+
}
184+
render() {
185+
const badges = document.querySelectorAll('.live-translator-badge');
186+
badges.forEach((badge) => {
187+
badge.remove();
188+
});
189+
this._indicator.style.background = this._enabled ? 'lightgreen' : 'red';
190+
if (!this._enabled) {
191+
return;
192+
}
193+
const re = new RegExp(`${this._zwEncoder.START}[${this._zwEncoder.ZERO}${this._zwEncoder.ONE}${this._zwEncoder.SPACE}]+${this._zwEncoder.END}`, 'gm');
194+
const queue = [document.documentElement];
195+
while (queue.length > 0) {
196+
const node = queue.pop();
197+
const badges = [];
198+
const parent = node.parentElement;
199+
if (node instanceof Text) {
200+
const matches = node.textContent.match(re);
201+
for (const match of matches !== null && matches !== void 0 ? matches : []) {
202+
const meta = JSON.parse(this._zwEncoder.decode(match));
203+
badges.push(createBadge(meta, this._options));
204+
}
205+
}
206+
const attributes = (node.attributes ? [...node.attributes] : [])
207+
.map((attribute) => ({ attribute, match: attribute.value.match(re) }))
208+
.filter(({ match }) => !!match);
209+
for (const { attribute, match } of attributes) {
210+
for (const m of match) {
211+
const meta = JSON.parse(this._zwEncoder.decode(m));
212+
badges.push(createBadge(meta, this._options, attribute.name));
213+
}
214+
}
215+
if (badges.length) {
216+
let container;
217+
if (node.previousElementSibling && node.previousElementSibling.classList.contains('live-translator-badge-container')) {
218+
container = node.previousElementSibling;
219+
}
220+
else {
221+
container = document.createElement('span');
222+
container.classList.add('live-translator-badge-container');
223+
parent.insertBefore(container, node);
224+
}
225+
for (const badge of badges) {
226+
container.appendChild(badge);
227+
}
228+
}
229+
for (const child of node.childNodes) {
230+
queue.push(child);
231+
}
232+
}
142233
}
143234
}
144235
const createBadge = (meta, options, attribute) => {
@@ -165,96 +256,6 @@ const createBadge = (meta, options, attribute) => {
165256
exports.LiveTranslatorPlugin = {
166257
install(app, options) {
167258
console.log('LiveTranslator is installed');
168-
// declare
169-
const enableButton = document.createElement('button');
170-
const indicator = document.createElement('span');
171-
const zw = new ZeroWidthEncoder();
172-
const visualize = () => {
173-
const badges = document.querySelectorAll('.live-translator-badge');
174-
console.log('clearing', badges.length, 'badges');
175-
badges.forEach((badge) => {
176-
badge.remove();
177-
});
178-
indicator.style.background = ltEnabler.enabled() ? 'lightgreen' : 'red';
179-
if (!ltEnabler.enabled()) {
180-
return;
181-
}
182-
const re = new RegExp(`${zw.START}[${zw.ZERO}${zw.ONE}${zw.SPACE}]+${zw.END}`, 'gm');
183-
const queue = [document.documentElement];
184-
while (queue.length > 0) {
185-
const node = queue.pop();
186-
const badges = [];
187-
const parent = node.parentElement;
188-
if (node instanceof Text) {
189-
const matches = node.textContent.match(re);
190-
for (const match of matches !== null && matches !== void 0 ? matches : []) {
191-
const meta = JSON.parse(zw.decode(match));
192-
badges.push(createBadge(meta, options));
193-
}
194-
}
195-
const attributes = (node.attributes ? [...node.attributes] : [])
196-
.map((attribute) => ({ attribute, match: attribute.value.match(re) }))
197-
.filter(({ match }) => !!match);
198-
for (const { attribute, match } of attributes) {
199-
for (const m of match) {
200-
const meta = JSON.parse(zw.decode(m));
201-
badges.push(createBadge(meta, options, attribute.name));
202-
}
203-
}
204-
if (badges.length) {
205-
let container;
206-
if (node.previousElementSibling && node.previousElementSibling.classList.contains('live-translator-badge-container')) {
207-
container = node.previousElementSibling;
208-
}
209-
else {
210-
container = document.createElement('span');
211-
container.classList.add('live-translator-badge-container');
212-
parent.insertBefore(container, node);
213-
}
214-
for (const badge of badges) {
215-
container.appendChild(badge);
216-
}
217-
}
218-
for (const child of node.childNodes) {
219-
queue.push(child);
220-
}
221-
}
222-
};
223-
const ltEnabler = new LiveTranslatorEnabler(options, visualize);
224-
// bind & style UI
225-
const style = document.createElement('style');
226-
style.id = 'live-translator-plugin-style';
227-
style.innerHTML = css;
228-
document.head.appendChild(style);
229-
enableButton.innerText = 'LT';
230-
enableButton.classList.add('live-translator-enable-button');
231-
indicator.classList.add('live-translator-enable-button-indicator');
232-
enableButton.appendChild(indicator);
233-
enableButton.addEventListener('click', () => ltEnabler.toggle());
234-
document.body.appendChild(enableButton);
235-
// encode meta to translation strings
236-
const originalFormatter = options.i18n.formatter;
237-
options.i18n.formatter = {
238-
interpolate(message, values, path) {
239-
const meta = zw.encode(JSON.stringify({
240-
message,
241-
values,
242-
path,
243-
locale: options.i18n.locale,
244-
}));
245-
const original = originalFormatter.interpolate(message, values, path);
246-
return (original && ltEnabler.enabled()) ? [meta, ...original] : original;
247-
},
248-
};
249-
// decode & visualize meta
250-
const throttler = (0, throttle_1.default)(visualize, 800);
251-
const observer = new MutationObserver(throttler);
252-
observer.observe(document.documentElement, {
253-
subtree: true,
254-
attributes: true,
255-
characterData: true,
256-
childList: false,
257-
});
258-
document.documentElement.addEventListener('mousemove', throttler);
259+
new LiveTranslatorManager(options);
259260
},
260261
};

0 commit comments

Comments
 (0)