UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

186 lines (152 loc) • 4.89 kB
import { app } from 'electron'; import i18next from 'i18next'; import { App } from '@/core/App'; import { loadResources } from '@/locales/resources'; import { createLogger } from '@/utils/logger'; // Create logger const logger = createLogger('core:I18nManager'); export class I18nManager { private i18n: typeof i18next; private initialized: boolean = false; private app: App; constructor(app: App) { logger.debug('Initializing I18nManager'); this.app = app; this.i18n = i18next.createInstance(); } /** * Initialize i18next instance */ async init(lang?: string) { if (this.initialized) { logger.debug('I18nManager already initialized, skipping'); return this.i18n; } // Priority: parameter language > stored locale > system language const storedLocale = this.app.storeManager.get('locale', 'auto') as string; const defaultLanguage = lang || (storedLocale !== 'auto' ? storedLocale : app.getLocale()) || 'en-US'; logger.info( `Initializing i18n, app locale: ${defaultLanguage}, stored locale: ${storedLocale}`, ); await this.i18n.init({ defaultNS: 'menu', fallbackLng: 'en-US', // Load resources as needed initAsync: true, interpolation: { escapeValue: false, }, lng: defaultLanguage, ns: ['menu', 'dialog', 'common'], partialBundledLanguages: true, }); logger.info(`i18n initialized, language: ${this.i18n.language}`); // Preload base namespaces await this.loadLocale(this.i18n.language); this.initialized = true; this.refreshMainUI(); // Listen for language change events this.i18n.on('languageChanged', this.handleLanguageChanged); return this.i18n; } /** * Basic translation function */ t = (key: string, options?: any) => { const result = this.i18n.t(key, options) as string; // If translation result is the same as key, translation might be missing if (result === key) { logger.warn(`${this.i18n.language} key: ${key} is not found`); } return result; }; /** * Create a translation function bound to a specific namespace * @param namespace Namespace * @returns Translation function bound to namespace */ createNamespacedT(namespace: string) { return (key: string, options: any = {}) => { // Copy options to avoid modifying the original object const mergedOptions = { ...options }; // Set namespace mergedOptions.ns = namespace; return this.t(key, mergedOptions); }; } /** * Get translation function by namespace * Provides a more convenient calling method */ ns = (namespace: string) => this.createNamespacedT(namespace); /** * Get current language */ getCurrentLanguage() { return this.i18n.language; } /** * Change application language * @param lng Target language */ public async changeLanguage(lng: string): Promise<void> { logger.info(`Changing language to: ${lng}`); if (!this.initialized) { await this.init(); } await this.i18n.changeLanguage(lng); // Language change event will trigger handleLanguageChanged } /** * Handle language change event */ private handleLanguageChanged = async (lang: string) => { logger.info(`Language changed to: ${lang}`); await this.loadLocale(lang); // Notify other parts of main process to refresh UI this.refreshMainUI(); }; /** * Refresh main process UI (menus, etc.) */ private refreshMainUI() { logger.debug('Refreshing main UI after language change'); this.app.menuManager.refreshMenus(); } /** * Notify renderer process that language has changed */ private notifyRendererProcess(lng: string) { logger.debug(`Notifying renderer process of language change: ${lng}`); // Send language change event to all windows // const windows = this.app.browserManager.windows; // // if (windows && windows.length > 0) { // windows.forEach((window) => { // if (window?.webContents) { // window.webContents.send('language-changed', lng); // } // }); // } } private async loadLocale(language: string) { logger.debug(`Loading locale for language: ${language}`); // Preload base namespaces await Promise.all(['menu', 'dialog', 'common'].map((ns) => this.loadNamespace(language, ns))); } /** * Load translation resources for specific namespace */ private async loadNamespace(lng: string, ns: string) { try { logger.debug(`Loading namespace: ${lng}/${ns}`); const resources = await loadResources(lng, ns); this.i18n.addResourceBundle(lng, ns, resources, true, true); return true; } catch (error) { logger.error(`Failed to load namespace: ${lng}/${ns}`, error); return false; } } }