闭包不直接缓存语言包,而是封装语言状态(locale、messages)和加载逻辑,提供私有、安全的访问接口;语言数据实际缓存在闭包变量中,实现内存缓存、作用域隔离与行为复用,并可结合localstorage持久化用户偏好。

闭包在国际化场景中不直接“缓存语言包”,而是通过封装状态、控制变量作用域,为语言包加载、切换和复用提供安全、隔离的运行环境。真正被缓存的是语言数据(如 JSON 内容),而闭包负责把这部分数据“封起来”,不让外部随意修改,同时暴露可控的访问接口。
用闭包封装语言状态与加载逻辑
把当前语言、已加载的语言包、加载函数等都放在一个外层函数作用域内,内部返回的函数(如 t()、setLocale())形成闭包,持续持有对这些私有状态的引用:
- 语言包对象(messages)存在闭包内,外部无法直接赋值或清空
- 加载过程(loadLocale)只允许按需触发,避免重复请求
- 切换语言时,仅更新闭包内的 messages 并触发重渲染,不污染全局
典型闭包式 i18n 实例
下面是一个轻量但完整的闭包实现,它把语言包缓存在闭包作用域中,而非全局或 window 上:
const createI18n = () => {
let locale = 'en';
let messages = {};
const loadLocale = async (lang) => {
try {
const mod = await import(`./locales/${lang}.json`);
messages = mod.default || mod;
locale = lang;
applyTranslations();
} catch {
// 加载失败时降级到 en,仍保持闭包内状态一致
if (lang !== 'en') await loadLocale('en');
}
};
const t = (key) => messages[key] ?? key;
const applyTranslations = () => {
document.querySelectorAll('[data-i18n]').forEach(el => {
el.textContent = t(el.dataset.i18n);
});
};
return { t, setLocale: loadLocale, currentLocale: () => locale };
};
// 使用:单例实例,所有调用共享同一份闭包状态
const i18n = createI18n();
i18n.setLocale('zh-CN'); // 触发加载并更新闭包内 messages
console.log(i18n.t('welcome')); // 读取闭包内最新 messages
为什么说这是“缓存”?
这个模式实现了三重缓存效果:
- 内存缓存:语言 JSON 数据保留在闭包变量 messages 中,后续 t() 调用无需重复解析或请求
- 作用域缓存:locale 和 messages 不暴露给全局,避免被意外覆盖或调试误改
- 行为缓存:同一 i18n 实例多次调用 setLocale('zh-CN'),只要包已加载,就跳过重复 import(可加 loadedMap 进一步优化)
配合 localStorage 做持久化并不破坏闭包
用户选择的语言偏好可以存 localStorage,下次初始化时读取并传入闭包 —— 闭包本身不负责存储,但它能安全使用该值启动:
- 初始化时:const savedLang = localStorage.getItem('lang') || 'en';
- 然后:i18n.setLocale(savedLang);
- 闭包内逻辑照常执行,状态依然私有
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











