besper-frontend-site-dev-main
Version:
Professional B-esper Frontend Site - Site-wide integration toolkit for full website bot deployment
331 lines (291 loc) • 9.43 kB
JavaScript
/**
* Management Interface I18n Service
* Enhanced internationalization service for the bot management interface
*/
import { managementTranslations } from './managementTranslations.js';
export class ManagementI18nService {
constructor() {
this.currentLanguage = 'en';
this.translations = managementTranslations;
this.supportedLanguages = ['en', 'de'];
this.fallbackLanguage = 'en';
// Auto-detect and set browser language
this.detectAndSetLanguage();
// Language change callbacks
this.languageChangeCallbacks = new Set();
}
/**
* Detect browser language and set it if supported
*/
detectAndSetLanguage() {
try {
// Get browser language preference
const browserLang = this.getBrowserLanguage();
// Set language if supported
if (this.supportedLanguages.includes(browserLang)) {
this.currentLanguage = browserLang;
console.log(
`[Management i18n] Language set to: ${browserLang} (browser detected)`
);
} else {
console.log(
`[Management i18n] Browser language ${browserLang} not supported, using: ${this.fallbackLanguage}`
);
this.currentLanguage = this.fallbackLanguage;
}
} catch (error) {
console.warn('[Management i18n] Language detection failed:', error);
this.currentLanguage = this.fallbackLanguage;
}
}
/**
* Get browser language with enhanced detection
*/
getBrowserLanguage() {
let language = 'en';
if (typeof navigator !== 'undefined') {
// Try multiple sources for better compatibility
language =
navigator.language ||
navigator.userLanguage ||
navigator.browserLanguage ||
navigator.systemLanguage ||
'en';
// Handle language arrays (some browsers provide multiple preferences)
if (
Array.isArray(navigator.languages) &&
navigator.languages.length > 0
) {
language = navigator.languages[0];
}
}
// Extract language code (remove country code)
return language.split('-')[0].toLowerCase();
}
/**
* Set current language
* @param {string} language - Language code ('en', 'de')
* @returns {boolean} Success status
*/
setLanguage(language) {
if (!this.supportedLanguages.includes(language)) {
console.warn(`[Management i18n] Language '${language}' not supported`);
return false;
}
const previousLanguage = this.currentLanguage;
this.currentLanguage = language;
console.log(
`[Management i18n] Language changed from '${previousLanguage}' to '${language}'`
);
// Notify listeners about language change
this.notifyLanguageChange(language, previousLanguage);
return true;
}
/**
* Get current language
* @returns {string} Current language code
*/
getCurrentLanguage() {
return this.currentLanguage;
}
/**
* Get supported languages with display names
* @returns {Array} Array of language objects with code and name
*/
getSupportedLanguages() {
return this.supportedLanguages.map(code => ({
code,
name: this.getLanguageDisplayName(code),
nativeName: this.getLanguageNativeName(code),
}));
}
/**
* Get language display name in English
* @param {string} langCode - Language code
* @returns {string} Display name
*/
getLanguageDisplayName(langCode) {
const names = {
en: 'English',
de: 'German',
};
return names[langCode] || langCode.toUpperCase();
}
/**
* Get language native name
* @param {string} langCode - Language code
* @returns {string} Native name
*/
getLanguageNativeName(langCode) {
const nativeNames = {
en: 'English',
de: 'Deutsch',
};
return nativeNames[langCode] || langCode.toUpperCase();
}
/**
* Translate a key with variable substitution
* @param {string} key - Translation key (supports dot notation like 'tabs.general')
* @param {Object} variables - Variables for substitution (e.g., {count: 5})
* @returns {string} Translated text
*/
t(key, variables = {}) {
// Get translation from current language
let translation = this.getNestedTranslation(this.currentLanguage, key);
// Fallback to default language if not found
if (translation === undefined) {
translation = this.getNestedTranslation(this.fallbackLanguage, key);
}
// Final fallback to the key itself
if (translation === undefined) {
console.warn(`[Management i18n] Translation missing for key: ${key}`);
translation = key;
}
// Perform variable substitution
if (typeof translation === 'string' && Object.keys(variables).length > 0) {
translation = this.substituteVariables(translation, variables);
}
return translation;
}
/**
* Get nested translation using dot notation
* @param {string} language - Language code
* @param {string} key - Dot notation key (e.g., 'tabs.general')
* @returns {string|undefined} Translation or undefined if not found
*/
getNestedTranslation(language, key) {
const langTranslations = this.translations[language];
if (!langTranslations) return undefined;
// Split key by dots and traverse object
const keys = key.split('.');
let current = langTranslations;
for (const k of keys) {
if (current && typeof current === 'object' && k in current) {
current = current[k];
} else {
return undefined;
}
}
return current;
}
/**
* Substitute variables in translation string
* @param {string} translation - Translation with variables like {{variable}}
* @param {Object} variables - Variables to substitute
* @returns {string} Translation with substituted variables
*/
substituteVariables(translation, variables) {
return translation.replace(/\{\{(\w+)\}\}/g, (match, variableName) => {
return variables[variableName] !== undefined
? variables[variableName]
: match;
});
}
/**
* Check if a translation exists
* @param {string} key - Translation key
* @returns {boolean} True if translation exists
*/
hasTranslation(key) {
return (
this.getNestedTranslation(this.currentLanguage, key) !== undefined ||
this.getNestedTranslation(this.fallbackLanguage, key) !== undefined
);
}
/**
* Add language change listener
* @param {Function} callback - Callback function (newLang, oldLang) => {}
*/
onLanguageChange(callback) {
if (typeof callback === 'function') {
this.languageChangeCallbacks.add(callback);
}
}
/**
* Remove language change listener
* @param {Function} callback - Callback function to remove
*/
offLanguageChange(callback) {
this.languageChangeCallbacks.delete(callback);
}
/**
* Notify all listeners about language change
* @param {string} newLanguage - New language code
* @param {string} oldLanguage - Previous language code
*/
notifyLanguageChange(newLanguage, oldLanguage) {
this.languageChangeCallbacks.forEach(callback => {
try {
callback(newLanguage, oldLanguage);
} catch (error) {
console.error(
'[Management i18n] Error in language change callback:',
error
);
}
});
}
/**
* Get all translations for a category in current language
* @param {string} category - Category key (e.g., 'tabs', 'common')
* @returns {Object} Object with all translations in category
*/
getCategory(category) {
const categoryTranslations =
this.getNestedTranslation(this.currentLanguage, category) ||
this.getNestedTranslation(this.fallbackLanguage, category) ||
{};
return { ...categoryTranslations };
}
/**
* Create a translation function bound to a specific prefix
* @param {string} prefix - Translation key prefix (e.g., 'configuration')
* @returns {Function} Translation function for the prefix
*/
createScopedTranslator(prefix) {
return (key, variables = {}) => {
const fullKey = prefix ? `${prefix}.${key}` : key;
return this.t(fullKey, variables);
};
}
/**
* Get language direction (for future RTL support)
* @param {string} langCode - Language code
* @returns {string} 'ltr' or 'rtl'
*/
getLanguageDirection(langCode = this.currentLanguage) {
// Currently all supported languages are LTR
// This can be expanded for RTL languages like Arabic
const rtlLanguages = ['ar', 'he', 'fa'];
return rtlLanguages.includes(langCode) ? 'rtl' : 'ltr';
}
/**
* Format number according to current language
* @param {number} number - Number to format
* @returns {string} Formatted number
*/
formatNumber(number) {
try {
return new Intl.NumberFormat(this.currentLanguage).format(number);
} catch (error) {
return number.toString();
}
}
/**
* Format date according to current language
* @param {Date} date - Date to format
* @param {Object} options - Intl.DateTimeFormat options
* @returns {string} Formatted date
*/
formatDate(date, options = {}) {
try {
return new Intl.DateTimeFormat(this.currentLanguage, options).format(
date
);
} catch (error) {
return date.toString();
}
}
}
// Create and export default instance
export const managementI18n = new ManagementI18nService();