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