语言裁定需明确优先级:登录用户优先取数据库preferred_language,未登录用户依赖Accept-Language与带过期时间的Cookie;URL路径仅用于显式切换,不自动持久化。
用户语言偏好存在多个来源,得先理清优先级
浏览器 accept-language 头、url 路径(如 /zh-cn/home)、用户登录后的数据库字段(如 user.preferred_language)、cookie(如 lang=ja)——这些都可能携带语言信息,但它们不天然一致,也不该被同等对待。
实际路由或渲染前必须做一次明确的“语言裁定”。常见错误是直接信任 Cookie 或 URL 参数,结果用户换设备后语言突然回退到浏览器默认值,或者管理员调试时误用路径参数污染了长期偏好。
- 登录用户:优先取
user.preferred_language,它代表明确意图;若为空,再 fallback 到Accept-Language - 未登录用户:只依赖
Accept-Language+ 可选的 Cookie 缓存(需带过期时间,且不能覆盖用户主动切换行为) - URL 路径中的语言码(如
/en/about)应仅用于显式切换场景,不自动持久化;否则会和用户设置冲突
Node.js / Express 中如何安全提取并标准化语言标签
Accept-Language 是个逗号分隔、带权重(q=0.8)的字符串,比如 zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7。直接截取第一个值(zh-CN)看似简单,但容易忽略区域变体兼容性问题——比如你只支持 zh,但用户发来的是 zh-HK。
推荐用 accept-language 这类轻量库解析,而非手撕正则。它能按权重排序、合并同语种不同区域(zh-HK → zh),也支持白名单过滤。
const acceptLanguage = require('accept-language');
acceptLanguage.languages(['en', 'zh', 'ja', 'ko']);
app.use((req, res, next) => {
const lang = acceptLanguage.get(req.headers['accept-language']);
req.locale = lang || 'en'; // 最终确定的 locale 字符串
next();
});
注意:acceptLanguage.get() 返回的是你在 languages() 里声明过的值之一,不会返回 zh-TW 这类未注册项——这点常被忽略,导致 fallback 失效。
Django 中 LocaleMiddleware 和用户数据库字段怎么协同
Django 默认的 LocaleMiddleware 只看 URL、Cookie、Accept-Language,完全无视用户模型里的 preferred_language 字段。想让它生效,必须自定义中间件,且顺序很关键:必须在 LocaleMiddleware 之前运行,并把结果写入 request.LANGUAGE_CODE。
- 不要覆盖
request.LANGUAGE_CODE后再调LocaleMiddleware——它会再次覆盖你 - 确保你的中间件在
MIDDLEWARE配置中排在'django.middleware.locale.LocaleMiddleware'前面 - 对未登录用户,仍需 fallback 到
LocaleMiddleware的原有逻辑,别一刀切
示例片段(放在中间件里):
if hasattr(request, 'user') and request.user.is_authenticated:
lang = getattr(request.user, 'preferred_language', None)
if lang in settings.LANGUAGES:
request.LANGUAGE_CODE = lang
前端切换语言时,怎么避免刷新丢失或服务端不一致
用户点「切换为日语」,前端发请求改数据库字段,然后 window.location.reload() ——这看起来没问题,但 reload 后服务端重新读取 user.preferred_language 时,可能因缓存、DB 主从延迟或 session 未同步,短暂显示旧语言。
更稳的做法是:切换请求成功后,前端主动设置一个短期有效的 Cookie(如 lang=ja; path=/; max-age=300),同时服务端中间件优先检查这个 Cookie(仅限本次请求),再查 DB。这样即使 DB 同步慢,当前页也能立即响应。
注意两个坑:
- Cookie 名别用
django_language(Django 自己在用),否则和LocaleMiddleware冲突 - 前端设置 Cookie 后不要立刻 reload,等服务端返回确认再 reload,避免竞态
- 如果用 JWT,也可以把语言存进 token payload,但要注意 token 过期策略和刷新成本
多服务器部署时,语言判定逻辑必须完全一致,且所有节点共享同一份语言配置(比如支持哪些 locale、它们的 fallback 关系),否则 A 服务器认为 zh-Hans 等价于 zh,B 服务器却当成未知值 fallback 成 en,用户就会看到混搭界面。










