@dwp/govuk-casa
Version:
A framework for building GOVUK Collect-And-Submit-Applications
107 lines • 4.48 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = i18nMiddleware;
const i18next_1 = require("i18next");
const i18next_http_middleware_1 = require("i18next-http-middleware");
const node_path_1 = require("node:path");
const node_fs_1 = require("node:fs");
const deepmerge_1 = __importDefault(require("deepmerge"));
const js_yaml_1 = require("js-yaml");
const logger_js_1 = __importDefault(require("../lib/logger.js"));
/**
* @typedef {import("express").RequestHandler} RequestHandler
* @access private
*/
const log = (0, logger_js_1.default)("middleware:i18n");
const loadJson = (file) => {
// Strip out newlines (this is a legacy feature which we're keeping for
// backwards compatibility).
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
const json = (0, node_fs_1.readFileSync)(file, "utf8");
return JSON.parse(json.replace(/[\r\n]/g, ""));
};
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
const loadYaml = (file) => (0, js_yaml_1.load)((0, node_fs_1.readFileSync)(file, "utf8"));
const extract = (file) => {
const ext = /.yaml$/i.test(file) ? ".yaml" : ".json";
const data = ext === ".yaml" ? loadYaml(file) : loadJson(file);
return {
ns: (0, node_path_1.basename)(file, ext),
data,
};
};
const loadResources = (languages, directories) => {
const store = Object.create(null);
for (const language of languages) {
// ESLint disabled as `store`, `language` and `ns` are all dev-controlled,
// and this function is only called once, at boot-time.
/* eslint-disable security/detect-object-injection */
store[language] = Object.create(null);
for (const basedir of directories) {
const dir = (0, node_path_1.resolve)(basedir, language);
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
if (!(0, node_fs_1.existsSync)(dir)) {
continue;
}
log.info("Loading %s language from %s ...", language, dir);
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
for (const file of (0, node_fs_1.readdirSync)(dir)) {
const { ns, data } = extract((0, node_path_1.resolve)(dir, file));
if (store[language][ns] === undefined) {
store[language][ns] = Object.create(null);
}
store[language][ns] = (0, deepmerge_1.default)(store[language][ns], data);
}
}
/* eslint-enable security/detect-object-injection */
}
return store;
};
/**
* Internationalisation middleware.
*
* @param {object} opts Options
* @param {string[]} [opts.languages] Language codes
* @param {string[]} [opts.fallbackLng] Fallback language
* @param {string[]} [opts.directories] Source translations directories
* @returns {RequestHandler[]} Middleware functions
*/
function i18nMiddleware({ languages = ["en", "cy"], fallbackLng = false, directories = [], }) {
// Load _all_ translations, from all directories into memory.
const resources = loadResources(languages, directories);
// Configure i18next
const i18nInstance = (0, i18next_1.createInstance)();
i18nInstance.use(i18next_http_middleware_1.LanguageDetector).init({
initAsync: false, // because we need synchronous loading
supportedLngs: languages,
fallbackLng,
defaultNS: "common",
// debug: true,
// All translation resources
resources,
// LanguageDetector options
detection: {
lookupQuerystring: "lang",
lookupSession: "language",
order: ["querystring", "session"],
},
});
// 2 middleware: one to read/set the session language, and one to enhance the
// req/res objects with i18n features
return [
(req, res, next) => {
if (!req.session.language) {
req.session.language = languages[0];
}
if (req?.query.lang && languages.includes(req.query.lang)) {
req.session.language = String(req.query.lang);
}
next();
},
(0, i18next_http_middleware_1.handle)(i18nInstance),
];
}
//# sourceMappingURL=i18n.js.map