@jupri-lab/i18n-core
Version:
Speak Easy. Effortless Internationalization for Your JavaScript Apps
151 lines (146 loc) • 5.01 kB
JavaScript
class EventManager {
constructor() {
this.listeners = {};
}
on(eventName, callback) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName].push(callback);
}
off(eventName, callback) {
const eventListeners = this.listeners[eventName];
if (eventListeners.length) {
const index = eventListeners.indexOf(callback);
if (index !== -1) {
eventListeners.splice(index, 1);
}
}
}
trigger(eventName, ...args) {
if (this.listeners[eventName]) {
for (const listener of this.listeners[eventName]) {
listener(...args);
}
}
}
}
class LanguageManager {
constructor(args) {
this.queryParam = "lang";
this.enableQueryParams = false;
this.currentLanguage = args.initialLanguage;
this.languages = args.languages;
this.enableQueryParams = args.enableQueryParams;
this.queryParam = args.queryParam;
}
getLanguages() {
return this.languages;
}
getCurrentLanguage() {
if (this.enableQueryParams) {
const urlParams = new URLSearchParams(window.location.search);
const param = urlParams.get(this.queryParam);
if (param) {
this.currentLanguage = param;
}
}
return this.currentLanguage;
}
changeLanguage(language, eventManager) {
if (this.currentLanguage !== language) {
this.currentLanguage = language;
if (this.enableQueryParams)
this.appendQueryParams(this.queryParam, language);
eventManager.trigger("language_changed");
}
}
appendQueryParams(key, value) {
const newURL = new URL(window.location.href);
newURL.searchParams.set(key, value);
window.history.replaceState({}, "", newURL.href);
}
}
class TranslationManager {
constructor(resources) {
this.resources = resources;
}
translate(params) {
try {
const currentTranslation = this.resources[params.currentLanguage];
if (currentTranslation) {
const parts = params.identifier.split(".");
let currentObject = currentTranslation;
for (const part of parts) {
currentObject = currentObject[part];
}
if (params.interpolation) {
return this.interpolate(currentObject, params.interpolation, params.escapeHTML);
}
if (typeof params.escapeHTML === "boolean" && params.escapeHTML)
return this.escapeHTMLTags(currentObject);
return currentObject;
}
}
catch (_) {
throw new Error("i18n resource is not initialized");
}
}
interpolate(string, values, escapeHTML = true) {
return string.replace(/\[(.*?)\]/g, (match, key) => {
const value = values[key.trim()] || match;
return escapeHTML ? this.escapeHTMLTags(value) : value;
});
}
escapeHTMLTags(string) {
return string.replace(/[&<>"'`=/]/g, (match) => {
const escapeMap = {
// eslint-disable-next-line quotes
'"': """,
"&": "&",
"'": "'",
"/": "/",
"<": "<",
"=": "=",
">": ">",
"`": "`",
};
return escapeMap[match];
});
}
}
class i18n {
constructor(configs) {
const { enableQueryParams = false, queryParam = "lang" } = configs;
this.eventManager = new EventManager();
this.languageManager = new LanguageManager({
enableQueryParams,
initialLanguage: configs.languages[0],
languages: configs.languages,
queryParam,
});
this.translationManager = new TranslationManager(configs.resources);
}
getCurrentLanguage() {
return this.languageManager.getCurrentLanguage();
}
getChangeLanguageHandler(language) {
if (language && !this.languageManager.getLanguages().includes(language))
throw new Error("Language is not recognized in the supported languages");
if (language)
this.languageManager.changeLanguage(language, this.eventManager);
}
translate(identifier, data = { escapeHTML: true }) {
const { escapeHTML = true, interpolation } = data;
return this.translationManager.translate({
currentLanguage: this.languageManager.getCurrentLanguage(),
escapeHTML,
identifier,
interpolation,
});
}
on(eventName, callback) {
this.eventManager.on(eventName, callback);
}
}
export { i18n as default };