#!/usr/bin/env node /** * Builds toju-app/public/i18n/en.json from the base catalog plus extracted * labels from theme-registry and access-control constants. */ import { readFileSync, readdirSync, writeFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const repoRoot = resolve(__dirname, '..'); const catalogDir = resolve(repoRoot, 'toju-app/public/i18n/catalog'); function extractLabelDescriptionBlocks(source, blockPattern) { const entries = {}; let match; while ((match = blockPattern.exec(source)) !== null) { const [, key, label, description] = match; entries[key] = { label, description }; } return entries; } function extractThemeRegistry(source) { return extractLabelDescriptionBlocks( source, /key: '([^']+)',\s*\n\s*label: '((?:\\'|[^'])*)',\s*\n\s*description: '((?:\\'|[^'])*)',/g ); } function extractPermissions(source) { return extractLabelDescriptionBlocks( source, /key: '([^']+)',\s*\n\s*label: '((?:\\'|[^'])*)',\s*\n\s*description: '((?:\\'|[^'])*)'/g ); } function unescape(value) { return value.replace(/\\'/g, "'"); } function deepMerge(target, source) { for (const [key, value] of Object.entries(source)) { if (value && typeof value === 'object' && !Array.isArray(value)) { target[key] ??= {}; deepMerge(target[key], value); continue; } target[key] = value; } return target; } function mergeAtPath(catalog, pathSegments) { let target = catalog; for (const segment of pathSegments) { target[segment] ??= {}; target = target[segment]; } return target; } function mergeExtracted(catalog, extracted, prefix) { const target = mergeAtPath(catalog, prefix.split('.')); for (const [key, { label, description }] of Object.entries(extracted)) { target[key] ??= {}; target[key].label = unescape(label); target[key].description = unescape(description); } } function loadCatalogFragments() { const catalog = {}; const files = readdirSync(catalogDir) .filter((name) => name.endsWith('.json')) .sort(); for (const file of files) { const fragment = JSON.parse(readFileSync(resolve(catalogDir, file), 'utf8')); deepMerge(catalog, fragment); } return catalog; } function main() { const themeRegistrySource = readFileSync( resolve(repoRoot, 'toju-app/src/app/domains/theme/domain/logic/theme-registry.logic.ts'), 'utf8' ); const permissionsSource = readFileSync( resolve(repoRoot, 'toju-app/src/app/domains/access-control/domain/constants/access-control.constants.ts'), 'utf8' ); const catalog = loadCatalogFragments(); mergeExtracted(catalog, extractThemeRegistry(themeRegistrySource), 'theme.registry'); mergeExtracted(catalog, extractPermissions(permissionsSource), 'permissions'); const outputPath = resolve(repoRoot, 'toju-app/public/i18n/en.json'); writeFileSync(outputPath, `${JSON.stringify(catalog, null, 2)}\n`, 'utf8'); console.log(`Wrote ${outputPath}`); } main();