splatoon3.ink/app/data/LocalizationProcessor.mjs
Matt Isenhower 7adb9c313e Add mkdirp utility to avoid verbose fs.mkdir calls
Added mkdirp() helper to app/common/fs.mjs that wraps
fs.mkdir(dir, { recursive: true }). Updated all call sites to use it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 11:09:02 -08:00

114 lines
2.7 KiB
JavaScript

import fs from 'fs/promises';
import path from 'path';
import { mkdirp } from '../common/fs.mjs';
import { jsonpathQuery } from '../common/util.mjs';
import get from 'lodash/get.js';
import set from 'lodash/set.js';
import pLimit from 'p-limit';
const limit = pLimit(1);
function makeArray(value) {
return Array.isArray(value) ? value : [value];
}
export class LocalizationProcessor {
outputDirectory = 'dist/data/locale';
constructor(locale, rulesets) {
this.locale = locale;
this.rulesets = rulesets;
}
get filename() {
return `${this.outputDirectory}/${this.locale.code}.json`;
}
*rulesetIterations() {
for (let ruleset of this.rulesets) {
for (let node of makeArray(ruleset.nodes)) {
for (let value of makeArray(ruleset.values)) {
yield {
key: ruleset.key,
node,
id: ruleset.id,
value,
};
}
}
}
}
*dataIterations(data) {
for (let ruleset of this.rulesetIterations()) {
for (let node of jsonpathQuery(data, ruleset.node)) {
if (!node) continue;
let id = get(node, ruleset.id);
yield {
ruleset,
node,
id,
value: get(node, ruleset.value),
path: `${ruleset.key}.${id}.${ruleset.value}`,
};
}
}
}
async updateLocalizations(data) {
// We're reading, modifying, and writing back to the same file,
// so we have to make sure the whole operation is atomic.
return limit(() => this._updateLocalizations(data));
}
async _updateLocalizations(data) {
let localizations = await this.readData();
for (let { path, value } of this.dataIterations(data)) {
set(localizations, path, value);
}
await this.writeData(localizations);
}
async hasMissingLocalizations(data) {
let localizations = await this.readData();
for (let { path } of this.dataIterations(data)) {
if (get(localizations, path) === undefined) {
return true;
}
}
return false;
}
async writeData(data) {
// If we're running in debug mode, format the JSON output so it's easier to read
let debug = !!process.env.DEBUG;
let space = debug ? 2 : undefined;
data = JSON.stringify(data, undefined, space);
await mkdirp(path.dirname(this.filename));
await fs.writeFile(this.filename, data);
}
async readData() {
try {
let result = await fs.readFile(this.filename);
return JSON.parse(result) || {};
} catch (e) {
// File doesn't exist yet or is invalid - return empty object
if (e.code !== 'ENOENT') {
console.warn(`Failed to read localization file ${this.filename}:`, e.message);
}
}
return {};
}
}