UNPKG

@pulzar/core

Version:

Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support

204 lines 7.93 kB
import { I18nManager } from "./i18n"; import { logger } from "../utils/logger"; // Create a default i18n manager instance const i18nManager = new I18nManager(); export class I18nMiddleware { options; constructor(options = {}) { this.options = { queryParam: "lang", cookieName: "language", headerName: "accept-language", defaultLanguage: "en", setCookie: true, cookieOptions: { maxAge: 365 * 24 * 60 * 60 * 1000, // 1 year httpOnly: false, // Allow client-side access secure: process.env.NODE_ENV === "production", sameSite: "lax", path: "/", }, ...options, }; } /** * Express middleware for i18n */ middleware() { return (req, res, next) => { try { // Extract language from various sources const queryLang = req.query[this.options.queryParam]; const cookieLang = req.cookies?.[this.options.cookieName]; const headerLang = req.headers[this.options.headerName]; // Detect language (simplified detection logic for Express) const detectedLanguage = queryLang || cookieLang || this.parseAcceptLanguage(headerLang) || this.options.defaultLanguage; // Create i18n context for this request (simplified since we don't have FastifyRequest) const i18nContext = { language: detectedLanguage, t: (key, options) => key, // Simplified translation function i18n: i18nManager.getInstance(), // Add required i18n instance detectLanguage: () => detectedLanguage, changeLanguage: async (lang) => lang, getLanguages: () => i18nManager.getSupportedLanguages(), getResourceBundle: () => ({}), }; // Attach context to request req.i18n = i18nContext; req.t = i18nContext.t; req.language = detectedLanguage; // Set cookie if language was detected from query or header if (this.options.setCookie && (queryLang || !cookieLang)) { res.cookie(this.options.cookieName, detectedLanguage, this.options.cookieOptions); } // Add helper methods to response res.locals.t = i18nContext.t; res.locals.language = detectedLanguage; res.locals.i18n = i18nContext; // Add language change helper (simplified) res.changeLanguage = async (language) => { // Simplified implementation const supportedLanguages = i18nManager.getSupportedLanguages(); if (!supportedLanguages.includes(language)) { throw new Error(`Language '${language}' is not supported`); } // Update request context req.language = language; res.locals.language = language; if (this.options.setCookie) { res.cookie(this.options.cookieName, language, this.options.cookieOptions); } return i18nContext; }; logger.debug("i18n middleware applied", { detectedLanguage, queryLang, cookieLang, headerLang, }); next(); } catch (error) { logger.error("i18n middleware error", { error }); // Fallback to default language const fallbackContext = { language: this.options.defaultLanguage, t: (key, options) => key, i18n: i18nManager.getInstance(), detectLanguage: () => this.options.defaultLanguage, changeLanguage: async (lang) => lang, getLanguages: () => i18nManager.getSupportedLanguages(), getResourceBundle: () => ({}), }; req.i18n = fallbackContext; req.t = fallbackContext.t; req.language = this.options.defaultLanguage; res.locals.t = fallbackContext.t; res.locals.language = this.options.defaultLanguage; res.locals.i18n = fallbackContext; next(); } }; } /** * Parse Accept-Language header */ parseAcceptLanguage(acceptLanguage) { if (!acceptLanguage) return null; const languages = acceptLanguage .split(",") .map((lang) => { const [code] = lang.trim().split(";"); return code?.split("-")[0]; // Take only language part, ignore region }) .filter((lang) => Boolean(lang)); const supportedLanguages = i18nManager.getSupportedLanguages(); for (const lang of languages) { if (supportedLanguages.includes(lang)) { return lang; } } return null; } /** * Get current options */ getOptions() { return { ...this.options }; } /** * Update options */ updateOptions(options) { this.options = { ...this.options, ...options }; } } // Default middleware instance export const i18nMiddleware = new I18nMiddleware(); // Helper function to create middleware with custom options export function createI18nMiddleware(options) { const middleware = new I18nMiddleware(options); return middleware.middleware(); } // Express helper to get i18n context from request export function getI18nContext(req) { const context = req.i18n; if (!context) { throw new Error("i18n context not found. Make sure i18n middleware is applied."); } return context; } // Express helper to get translation function from request export function getTranslationFunction(req) { const t = req.t; if (!t) { throw new Error("Translation function not found. Make sure i18n middleware is applied."); } return t; } // Express helper to get current language from request export function getCurrentLanguage(req) { const language = req.language; if (!language) { throw new Error("Language not found. Make sure i18n middleware is applied."); } return language; } // Language change middleware export function languageChangeMiddleware() { return async (req, res, next) => { const { language } = req.body; if (!language) { return res.status(400).json({ error: "Language parameter required" }); } if (!i18nManager.getSupportedLanguages().includes(language)) { return res.status(400).json({ error: "Unsupported language", supportedLanguages: i18nManager.getSupportedLanguages(), }); } try { // Change language using response helper const changeLanguage = res.changeLanguage; if (changeLanguage) { await changeLanguage(language); } return res.json({ success: true, language, message: "Language changed successfully", }); } catch (error) { logger.error("Language change failed", { error, language }); return res.status(500).json({ error: "Failed to change language" }); } }; } // Default export export default i18nMiddleware.middleware(); //# sourceMappingURL=middleware.js.map