Express+EJS+i18next SSR项目:前端语言切换及路由优化问题
解决方案
1. 配置前缀式路由与i18next语言检测
调整入口文件(app.js)的配置,实现路径前缀语言识别、Cookie缓存及多方式语言检测:
const express = require('express'); const i18next = require('i18next'); const i18nextMiddleware = require('i18next-http-middleware'); const Backend = require('i18next-fs-backend'); const cookieParser = require('cookie-parser'); const app = express(); app.use(cookieParser()); // 初始化i18next i18next .use(Backend) .use(i18nextMiddleware.LanguageDetector) .init({ fallbackLng: 'en', supportedLngs: ['en', 'de'], // 替换为你支持的语言列表 backend: { loadPath: './locales/{{lng}}/{{ns}}.json' // 翻译文件存储路径 }, detection: { order: ['path', 'cookie', 'querystring'], // 优先级:路径前缀 > Cookie > 查询参数 lookupPath: 'lng', lookupCookie: 'i18next', lookupQuerystring: 'lang', caches: ['cookie'], // 将语言偏好缓存到Cookie excludeRoutes: ['/api'] // 排除不需要国际化的路由(如API) } }); // 应用i18next中间件 app.use(i18nextMiddleware.handle(i18next)); // 处理语言前缀路由:提取路径中的语言标识,修正路由匹配 app.use((req, res, next) => { const supportedLngs = i18next.options.supportedLngs; const urlSegments = req.path.split('/'); const detectedLng = urlSegments[1]; if (supportedLngs.includes(detectedLng)) { req.i18n.changeLanguage(detectedLng); // 移除语言前缀,让后续路由正常匹配 req.url = urlSegments.slice(2).join('/') || '/'; } next(); }); // 配置EJS模板引擎 app.set('view engine', 'ejs'); app.set('views', './views'); // 挂载路由 const routes = require('./routes'); app.use('/', routes); app.listen(3000, () => console.log('Server running on port 3000'));
2. 简化路由文件
路由文件(routes/index.js)无需重复编写语言前缀,直接保留原有路由结构即可:
const express = require('express'); const router = express.Router(); router.get('/', (req, res) => { res.render('index', { currentLng: req.i18n.language, supportedLngs: req.i18n.options.supportedLngs }); }); router.get('/about', (req, res) => { res.render('about', { currentLng: req.i18n.language, supportedLngs: req.i18n.options.supportedLngs }); }); module.exports = router;
3. 实现前端语言切换器
在EJS模板中添加语言切换组件,动态生成对应语言前缀的路由:
语言切换组件(views/partials/language-switcher.ejs)
<div class="language-switcher"> <% supportedLngs.forEach(lng => { %> <% if (lng !== currentLng) { %> <%// 生成当前页面的对应语言路由 %> <% const cleanPath = req.originalUrl.replace(/^\/[a-z]{2}/, '') || '/'; %> <a href="/<%= lng %><%= cleanPath %>" class="lang-link"><%= lng.toUpperCase() %></a> <% } else { %> <span class="lang-active"><%= lng.toUpperCase() %></span> <% } %> <% }) %> </div>
在页面模板中引入组件(如views/index.ejs)
<!DOCTYPE html> <html lang="<%= currentLng %>"> <head> <meta charset="UTF-8"> <title><%= t('home.title') %></title> </head> <body> <% include partials/language-switcher.ejs %> <h1><%= t('home.heading') %></h1> <p><%= t('home.description') %></p> </body> </html>
4. 前端JS触发语言切换(可选)
如果需要用按钮而非链接触发切换,可添加以下JS逻辑(嵌入模板或单独JS文件):
function changeLanguage(targetLng) { // 设置Cookie存储语言偏好 document.cookie = `i18next=${targetLng}; path=/; max-age=31536000`; // 生成对应语言的当前页面路由并跳转 const cleanPath = window.location.pathname.replace(/^\/[a-z]{2}/, '') || '/'; window.location.href = `/${targetLng}${cleanPath}`; } // 绑定按钮事件 document.querySelectorAll('.lang-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); changeLanguage(btn.dataset.lng); }); });
对应的按钮形式切换组件:
<div class="language-switcher"> <% supportedLngs.forEach(lng => { %> <% if (lng !== currentLng) { %> <button class="lang-btn" data-lng="<%= lng %>"><%= lng.toUpperCase() %></button> <% } else { %> <span class="lang-active"><%= lng.toUpperCase() %></span> <% } %> <% }) %> </div>
5. 验证依赖配置
确保package.json包含必要依赖:
{ "name": "ssr-i18n-app", "version": "1.0.0", "dependencies": { "express": "^4.18.2", "ejs": "^3.1.9", "i18next": "^23.7.16", "i18next-fs-backend": "^2.3.1", "i18next-http-middleware": "^3.5.0", "cookie-parser": "^1.4.6" } }
关键说明
- 路径前缀匹配:访问
/de/about会自动识别德语,渲染对应翻译内容;访问/en/about则用英语。 - Cookie缓存:切换语言后,Cookie会存储偏好,下次访问自动加载对应语言。
- 查询参数兼容:临时切换可使用
/?lang=de,访问后Cookie会更新该语言偏好。
内容的提问来源于stack exchange,提问作者druj_nasu
相关产品推荐
相关产品推荐

