feat(i18n): add fallback toast and cached bundle fallback logic

1. add global i18n fallback toast on lang:fallback event, only show once per process
2. add getCachedBundleAny to retrieve expired cache for boot fallback
3. add cache fallback logic when remote i18n bundle load fails
This commit is contained in:
2026-07-02 16:33:22 +08:00
parent c86e688e47
commit e1ce2ae268
3 changed files with 102 additions and 8 deletions

View File

@@ -368,6 +368,47 @@ export function getCachedBundle(options = {}) {
return null;
}
/**
* 获取任意已缓存的 bundle(不检查 TTL),仅用于 boot 失败兜底
*
* 与 getCachedBundle 的区别:
* - getCachedBundle: 受 BUNDLE_CACHE_TTL 限制,过期返回 null(用于日常 $lang 渲染)
* - getCachedBundleAny: 不检查 TTL,只要内存或 storage 中有就返回(用于失败兜底)
*
* 设计意图:
* boot() 强制重试失败时,如果用户本地还有哪怕过期的 bundle,
* 也比"$lang() 全返回空串"用户体验更好 — 至少能展示残缺文案。
* 这是显式的"宁可用过期缓存,也不空白"的取舍。
*
* 优先返回内存缓存(更快),其次返回 storage 缓存。
*
* @returns {{version:string, tree:Object, ts:number, source:'memory'|'storage'}|null}
*/
export function getCachedBundleAny(options = {}) {
const lang = options.lang || getCurrentLocale();
const appType = options.appType || getCurrentAppType();
const uniacid = options.uniacid !== undefined ? options.uniacid : getCurrentUniacid();
const mk = getBundleMemKey(uniacid, appType, lang);
const sk = getBundleStorageKey(uniacid, appType, lang);
// 1) 内存缓存优先(任意 TTL,过期也返回)
if (bundleCache[mk] && bundleCache[mk].tree) {
return { ...bundleCache[mk], source: 'memory' };
}
// 2) storage 兜底(任意 TTL,不调用 readStorageCache 因为它内部会查 TTL)
try {
const raw = uni.getStorageSync(sk);
if (raw && raw.tree && typeof raw.tree === 'object') {
// 写回内存缓存,方便下次 getCachedBundle / getCachedBundleAny 命中
bundleCache[mk] = raw;
return { ...raw, source: 'storage' };
}
} catch (e) { /* 忽略 */ }
return null;
}
/**
* 清除 bundle 缓存
*/