UNPKG

@sentzunhat/zacatl

Version:

A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.

146 lines 5.02 kB
import fs from 'fs'; import path from 'path'; import { NotFoundError, BadRequestError } from '../error/index.js'; import i18n from '../third-party/i18n.js'; const isPlainObject = (value) => { return (typeof value === 'object' && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === '[object Object]'); }; const deepMerge = (base, override) => { const out = { ...base }; for (const [key, overrideValue] of Object.entries(override)) { const baseValue = out[key]; if (isPlainObject(baseValue) && isPlainObject(overrideValue)) { out[key] = deepMerge(baseValue, overrideValue); continue; } out[key] = overrideValue; } return out; }; const readJsonFile = (filePath) => { const content = fs.readFileSync(filePath, 'utf-8'); const parsed = JSON.parse(content); if (!isPlainObject(parsed)) { throw new BadRequestError({ message: `Invalid locale JSON shape in ${filePath}`, reason: 'Locale file must contain a plain JSON object', component: 'I18nNode', operation: 'readJsonFile', metadata: { filePath }, }); } return parsed; }; const findExistingDir = (candidates) => { for (const candidate of candidates) { try { if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) { return candidate; } } catch { continue; } } return null; }; export const loadCatalog = (input) => { const { localesDir, supportedLocales } = input; const catalog = {}; for (const locale of supportedLocales) { const filePath = path.join(localesDir, `${locale}.json`); if (!fs.existsSync(filePath)) { continue; } catalog[locale] = readJsonFile(filePath); } return catalog; }; const getHere = () => { if (typeof __dirname !== 'undefined') return __dirname; if (typeof process !== 'undefined' && Array.isArray(process.argv) && typeof process.argv[1] === 'string' && process.argv[1].length > 0) { return path.dirname(process.argv[1]); } return process.cwd(); }; export const resolveBuiltInLocalesDir = () => { const here = getHere(); const candidates = [ path.resolve(here, 'locales'), path.resolve(here, '../../build/localization/locales'), path.resolve(here, '../../src/localization/locales'), path.resolve(process.cwd(), 'src/localization/locales'), path.resolve(process.cwd(), 'localization/locales'), path.resolve(process.cwd(), 'locales'), ]; const found = findExistingDir(candidates); if (found == null) { throw new NotFoundError({ message: `Unable to locate built-in locales directory. Tried: ${candidates.join(', ')}`, reason: 'Built-in locales directory not found in expected paths', component: 'I18nNode', operation: 'resolveBuiltInLocalesDir', metadata: { candidates }, }); } return found; }; export const mergeCatalogs = (input) => { const { base, additions, overrideBuiltIn } = input; const out = {}; const locales = new Set([ ...Object.keys(base), ...additions.flatMap((a) => Object.keys(a)), ]); for (const locale of locales) { const baseLocale = base[locale] ?? {}; const additionsLocale = additions.reduce((acc, addition) => { const addLocale = addition[locale]; if (addLocale == null) { return acc; } return deepMerge(acc, addLocale); }, {}); out[locale] = overrideBuiltIn ? deepMerge(baseLocale, additionsLocale) : deepMerge(additionsLocale, baseLocale); } return out; }; export const configureI18nNode = (input = {}) => { const defaultLocale = input.locales?.default ?? 'en'; const supportedLocales = input.locales?.supported ?? ['en', 'es']; const objectNotation = input.objectNotation ?? true; const overrideBuiltIn = input.overrideBuiltIn ?? true; const builtInLocalesDir = resolveBuiltInLocalesDir(); const builtInCatalog = loadCatalog({ localesDir: builtInLocalesDir, supportedLocales, }); const additionalCatalogs = (input.locales?.directories ?? []) .filter((dir) => dir != null && dir.length > 0) .map((localesDir) => loadCatalog({ localesDir, supportedLocales })); const staticCatalog = mergeCatalogs({ base: builtInCatalog, additions: additionalCatalogs, overrideBuiltIn, }); i18n.configure({ locales: supportedLocales, defaultLocale, objectNotation, staticCatalog: staticCatalog, updateFiles: false, syncFiles: false, autoReload: false, }); return i18n; }; //# sourceMappingURL=i18n-node.js.map