如何在Node.js Koa2与EJS中实现多语言支持
Great question! Your current approach works for two languages, but it'll quickly become unmanageable as you add more locales or expand your text content. Let's refactor this into a clean, scalable setup that's easy to maintain.
Step 1: Create Language Configuration Files
First, move all your translated content out of your code and into dedicated JSON files. This makes adding new languages or updating text a breeze without touching your app logic.
Create a locales folder in your project root, then add files for each language:
locales/en.json:
{ "title": "Title", "welcome": "Welcome to our website!", "button": "Click Me" }
locales/de.json:
{ "title": "Titel", "welcome": "Willkommen auf unserer Website!", "button": "Klicken Sie mich" }
Step 2: Add a Language Loading Utility
Create a simple utility to load the correct language file based on the user's locale. This keeps your route logic clean.
Add a utils/i18n.js file:
const fs = require('fs'); const path = require('path'); function getLanguage(lang) { // Default to English if the requested language doesn't exist const validLangs = ['en', 'de']; const targetLang = validLangs.includes(lang) ? lang : 'en'; const filePath = path.join(__dirname, '../locales', `${targetLang}.json`); return JSON.parse(fs.readFileSync(filePath, 'utf8')); } module.exports = { getLanguage };
Step 3: Enhance Language Detection Middleware
Right now, your language variable is undefined—let's add logic to detect the user's preferred language from common sources (URL parameter, cookie, or request header):
Update your index.js:
const Koa = require('koa'); const views = require('koa-views'); const path = require('path'); const { getLanguage } = require('./utils/i18n'); const app = new Koa(); // Configure EJS views app.use(views(path.join(__dirname, './view'), { extension: 'ejs' })); // Middleware to detect user language app.use(async (ctx, next) => { // Priority order: URL param > Cookie > Accept-Language header > default to en let lang = ctx.query.lang || ctx.cookies.get('language'); // Parse Accept-Language header if no other source is found if (!lang) { const acceptLang = ctx.request.headers['accept-language']; if (acceptLang) { lang = acceptLang.split(',')[0].split('-')[0]; // Get first language code (e.g., 'de' from 'de-DE') } } // Load the language pack and attach to context ctx.state.lang = getLanguage(lang); // Set a cookie to remember the user's language preference ctx.cookies.set('language', ctx.state.lang.title === 'Title' ? 'en' : 'de', { maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days }); await next(); }); // Main route handler app.use(async (ctx) => { // Pass the entire language pack to the template await ctx.render('index', { lang: ctx.state.lang }); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
Step 4: Update Your EJS Template
Now you can use all translated content directly in your view/index.ejs template:
<!DOCTYPE html> <html lang="<%= lang.title === 'Title' ? 'en' : 'de' %>"> <head> <meta charset="UTF-8"> <title><%= lang.title %></title> </head> <body> <h1><%= lang.welcome %></h1> <button><%= lang.button %></button> <!-- Add language switcher links --> <div> <a href="?lang=en">English</a> | <a href="?lang=de">Deutsch</a> </div> </body> </html>
Bonus Optimizations
- Cache Language Files: For production, cache the loaded JSON files so you don't read from disk on every request.
- Add More Languages: Just add a new JSON file in the
localesfolder and update thevalidLangsarray inutils/i18n.js. - Handle Complex Translations: If you need plurals, gender-specific text, or variable substitutions later, you could switch to a library like
i18next—but this setup works perfectly for most small to medium apps.
内容的提问来源于stack exchange,提问作者yupang

