diff options
author | Raphael <mail@raphaelkabo.com> | 2025-05-28 18:58:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-28 18:58:46 +0100 |
commit | 3d84891118f8a81af3ddb978af9b3f8b02fd5d65 (patch) | |
tree | 0a8d344e331a0551b73435bbbb3919107737f69f /src/app.ts | |
parent | 6f0b7a44b995b6b66baf42a9369182fc05a90b34 (diff) | |
parent | 4664b6968fdcaca54268d60f400da02364213f05 (diff) |
Merge branch 'main' into main
Diffstat (limited to 'src/app.ts')
-rwxr-xr-x | src/app.ts | 208 |
1 files changed, 148 insertions, 60 deletions
@@ -1,6 +1,20 @@ import express from "express"; import cookieParser from "cookie-parser"; import { create as createHandlebars, ExpressHandlebars } from "express-handlebars"; +import i18next from "i18next"; +import Backend from "i18next-fs-backend"; +import { LanguageDetector, handle } from 'i18next-http-middleware'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import path from 'path'; + +const require = createRequire(import.meta.url); +const handlebarsI18next = require('handlebars-i18next'); + +// Recreate __dirname in ES module +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); import routes from "./routes.js"; import frontend from "./routes/frontend.js"; @@ -9,76 +23,150 @@ import event from "./routes/event.js"; import group from "./routes/group.js"; import staticPages from "./routes/static.js"; import magicLink from "./routes/magicLink.js"; +import { getI18nHelpers } from "./helpers.js"; import { activityPubContentType, alternateActivityPubContentType, } from "./lib/activitypub.js"; +import moment from "moment"; import { EmailService } from "./lib/email.js"; import getConfig from "./lib/config.js"; const app = express(); const config = getConfig(); -const hbsInstance = createHandlebars({ - defaultLayout: "main", - partialsDir: ["views/partials/"], - layoutsDir: "views/layouts/", - helpers: { - plural: function (number: number, text: string) { - const singular = number === 1; - // If no text parameter was given, just return a conditional s. - if (typeof text !== "string") return singular ? "" : "s"; - // Split with regex into group1/group2 or group1(group3) - const match = text.match(/^([^()\/]+)(?:\/(.+))?(?:\((\w+)\))?/); - // If no match, just append a conditional s. - if (!match) return text + (singular ? "" : "s"); - // We have a good match, so fire away - return ( - (singular && match[1]) || // Singular case - match[2] || // Plural case: 'bagel/bagels' --> bagels - match[1] + (match[3] || "s") - ); // Plural case: 'bagel(s)' or 'bagel' --> bagels - }, - json: function (context: object) { - return JSON.stringify(context); +// function to construct __dirname with ES module +const getLocalesPath = () => { + const __filename = fileURLToPath(import.meta.url); + const __dirname = dirname(__filename); + return path.join(__dirname, '..', 'locales'); +}; + +async function initializeApp() { + // Cookies // + app.use(cookieParser()); + + // i18next configuration + await i18next + .use(Backend) + .use(LanguageDetector) + .init({ + backend: { + loadPath: path.join(getLocalesPath(), '{{lng}}.json'), + }, + fallbackLng: 'en', + preload: ['en', 'ja'], + supportedLngs: ['en', 'ja'], + nonExplicitSupportedLngs: true, + load: 'languageOnly', + debug: false, + detection: { + order: ['header', 'cookie'], + lookupHeader: 'accept-language', + lookupCookie: 'i18next', + caches: ['cookie'] + }, + interpolation: { + escapeValue: false + } + }); + + app.use(handle(i18next)); + + // to Switch language + app.use((req, res, next) => { + const currentLanguage = i18next.language; + i18next.changeLanguage(req.language); + const newLanguage = i18next.language; +// Uncomment for debugging +// console.log('Language Change:', { +// header: req.headers['accept-language'], +// detected: req.language, +// currentLanguage: currentLanguage, +// newLanguage: newLanguage +// }); + next(); + }); + +// Uncomment for debugging +// app.use((req, res, next) => { +// console.log('Language Detection:', { +// header: req.headers['accept-language'], +// detected: req.language, +// i18next: i18next.language +// }); +// next(); +// }); + + // View engine // + const hbsInstance = createHandlebars({ + defaultLayout: "main", + partialsDir: ["views/partials/"], + layoutsDir: "views/layouts/", + helpers: { + // add i18next helpers + ...getI18nHelpers(), + plural: function (key: string, count: number, options: any) { // Register the plural helper + const translation = i18next.t(key, { count: count }); + return translation; + }, + json: function (context: object) { + return JSON.stringify(context); + } }, - }, -}); - -const emailService = new EmailService(config, hbsInstance); -emailService.verify(); - -app.use((req: express.Request, _: express.Response, next: express.NextFunction) => { - req.hbsInstance = hbsInstance; - req.emailService = emailService; - next() - return -}) - -// View engine // -app.engine("handlebars", hbsInstance.engine); -app.set("view engine", "handlebars"); -app.set("hbsInstance", hbsInstance); - -// Static files // -app.use(express.static("public")); - -// Body parser // -app.use(express.json({ type: alternateActivityPubContentType })); -app.use(express.json({ type: activityPubContentType })); -app.use(express.json({ type: "application/json" })); -app.use(express.urlencoded({ extended: true })); - -// Cookies // -app.use(cookieParser()); - -// Router // -app.use("/", staticPages); -app.use("/", frontend); -app.use("/", activitypub); -app.use("/", event); -app.use("/", group); -app.use("/", magicLink); -app.use("/", routes); + }); + + const emailService = new EmailService(config, hbsInstance); + emailService.verify(); + + app.use((req: express.Request, _: express.Response, next: express.NextFunction) => { + req.hbsInstance = hbsInstance; + req.emailService = emailService; + next() + return + }) + + // View engine // + app.engine("handlebars", hbsInstance.engine); + app.set("view engine", "handlebars"); + app.set("hbsInstance", hbsInstance); + + // calling i18nextHelper + if (typeof handlebarsI18next === 'function') { + handlebarsI18next(hbsInstance.handlebars, i18next); + } else if (typeof handlebarsI18next.default === 'function') { + handlebarsI18next.default(hbsInstance.handlebars, i18next); + } else { + console.error('handlebars-i18next helper is not properly loaded'); + } + + i18next.on('languageChanged', function(lng) { + moment.locale(lng); + }); + + app.engine("handlebars", hbsInstance.engine); + app.set("view engine", "handlebars"); + app.set("hbsInstance", hbsInstance); + + // Static files // + app.use(express.static("public")); + + // Body parser // + app.use(express.json({ type: alternateActivityPubContentType })); + app.use(express.json({ type: activityPubContentType })); + app.use(express.json({ type: "application/json" })); + app.use(express.urlencoded({ extended: true })); + + // Router // + app.use("/", staticPages); + app.use("/", frontend); + app.use("/", activitypub); + app.use("/", event); + app.use("/", group); + app.use("/", magicLink); + app.use("/", routes); +} + +initializeApp().catch(console.error); export default app; |