@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
JavaScript
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