import { resources, JsonAsset, director, SpriteFrame, SpriteAtlas, sp } from 'cc'; export class I18nManager { private static _instance: I18nManager; spriteFrameCache: Map = new Map(); spineCache: Map = new Map(); _sfTasks = new Map>(); languageData: Record = {}; currentLanguage: string = 'en'; ready: boolean = false; static get instance() { return this._instance || (this._instance = new I18nManager()); } private constructor() { } // yield/await 风格:谁都可以 await,且只加载一次 ensureI18nSprite(lang: string, name: string): Promise { const key = `${lang}_${name}`; const path = `i18nSprite/${lang}/${name}/spriteFrame`; const cached = this.spriteFrameCache.get(key); if (cached) return Promise.resolve(cached); const pending = this._sfTasks.get(key); if (pending) return pending; const task = new Promise((resolve, reject) => { resources.load(path, SpriteFrame, (err, sf) => { this._sfTasks.delete(key); if (err || !sf) return reject(err); this.spriteFrameCache.set(key, sf); resolve(sf); }); }); this._sfTasks.set(key, task); return task; } /** * 初始化国际化管理器 * @param language 初始语言代码 * @param languageJson 语言数据JSON资源 */ public async init(language: string = 'en', languageJson: JsonAsset = null): Promise { this.currentLanguage = language; try { if (languageJson) { this.languageData = languageJson.json; } // 预加载资源(目前资源列表为空,可根据需要添加) let okSprite = await this.preloadAssets('spriteFrame', [ '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', '29', '30', '98', '99', 'Buy_5', 'sysgift_completed', 'sysgift_continue', 'sysgift_fbs', 'sysgift_info_fbs', 'sysgift_received', 'sysgift_symbols', 'sysgift_total', 'sysgift_win', ]); let okAtlas = await this.preloadAssets('atlas', []); let okSpine = await this.preloadAssets('spine', []); this.ready = okSprite && okAtlas && okSpine; } catch (error) { console.error('I18nManager init failed:', error); } } /** * 预加载指定类型的资源 * @param type 资源类型 * @param names 资源名称列表 */ private async preloadAssets(type: 'spriteFrame' | 'atlas' | 'spine', names: string[]): Promise { let results = await Promise.all(names.map(name => this.loadAsset(type, name))); return results.every(ok => ok); } /** * 加载单个资源 * @param type 资源类型 * @param name 资源名称 */ private loadAsset(type: 'spriteFrame' | 'atlas' | 'spine', name: string): Promise { return new Promise(resolve => { let cacheKey = `${this.currentLanguage}_${name}`; if (type === 'spriteFrame') { let path = `i18nSprite/${this.currentLanguage}/${name}/spriteFrame`; resources.load(path, SpriteFrame, (err, asset) => { if (!err && asset) { this.spriteFrameCache.set(cacheKey, asset); resolve(true); } else { console.warn(`[i18n] spriteFrame load failed: ${path}`, err?.message || err); resolve(false); } }); } else if (type === 'atlas') { let path = `i18nSprite/${this.currentLanguage}/${name}`; resources.load(path, SpriteAtlas, (err, atlas) => { if (!err && atlas) { atlas.getSpriteFrames().forEach(frame => { this.spriteFrameCache.set(`${this.currentLanguage}_${frame.name}`, frame); }); resolve(true); } else { console.warn(`[i18n] atlas load failed: ${path}`, err?.message || err); resolve(false); } }); } else if (type === 'spine') { let path = `i18nSpine/${this.currentLanguage}/${name}`; resources.load(path, sp.SkeletonData, (err, asset) => { if (!err && asset) { this.spineCache.set(cacheKey, asset); resolve(true); } else { console.warn(`[i18n] spine load failed: ${path}`, err?.message || err); resolve(false); } }); } }); } /** * 设置当前语言 * @param language 语言代码 */ public setLanguage(language: string): void { if (this.languageData[language]) { this.currentLanguage = language; this.updateSceneRenderers(); } } /** * 翻译文本 * @param key 翻译键 * @returns 翻译后的文本,如果找不到则返回键本身 */ public t(key: string): string { return this.languageData[this.currentLanguage]?.[key] || key; } public getSpriteFrame(spriteName: string): SpriteFrame { let cacheKey = `${this.currentLanguage}_${spriteName}`; let cachedFrame = this.spriteFrameCache.get(cacheKey); return cachedFrame; } /** * 更新场景中的所有本地化组件 * 在语言切换时调用,刷新所有本地化文本和资源 */ public updateSceneRenderers(): void { if (!this.ready) { console.warn('I18nManager is not ready.'); return; } let scene = director.getScene(); if (!scene?.isValid) return; // 收集所有本地化组件 let allLocalizedLabels = []; let allLocalizedSprites = []; scene.children.forEach(node => { allLocalizedLabels.push(...node.getComponentsInChildren('LocalizedLabel')); allLocalizedSprites.push(...node.getComponentsInChildren('LocalizedSprite')); }); // 更新所有激活的本地化组件 [...allLocalizedLabels, ...allLocalizedSprites].forEach(component => { if (component.node.active) { // 调用对应的更新方法 component.updateLabel?.() || component.updateSprite?.(); } }); } }