diff --git a/module/api.mjs b/module/api.mjs index dc38c3d..33eab44 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -7,6 +7,7 @@ import { QueryStatus } from "./apps/QueryStatus.mjs"; // Utils import { attributeSorter } from "./utils/attributeSort.mjs"; import { DialogManager } from "./utils/DialogManager.mjs"; +import { localizer } from "./utils/localizer.mjs"; import { QueryManager } from "./utils/QueryManager.mjs"; import { toID } from "./utils/toID.mjs"; @@ -27,6 +28,7 @@ Object.defineProperty( }, utils: { attributeSorter, + localizer, toID, }, }), diff --git a/module/utils/localizer.mjs b/module/utils/localizer.mjs new file mode 100644 index 0000000..7988039 --- /dev/null +++ b/module/utils/localizer.mjs @@ -0,0 +1,32 @@ +const config = Object.preventExtensions({ + subKeyPattern: /@(?[a-zA-Z.]+)/gm, + maxDepth: 10, +}); + +export function localizer(key, args = {}, depth = 0) { + /** @type {string} */ + let localized = game.i18n.format(key, args); + const subkeys = localized.matchAll(config.subKeyPattern); + + // Short-cut to help prevent infinite recursion + if (depth > config.maxDepth) { + return localized; + }; + + /* + Helps prevent recursion on the same key so that we aren't doing excess work. + */ + const localizedSubkeys = new Map(); + for (const match of subkeys) { + const subkey = match.groups.key; + if (localizedSubkeys.has(subkey)) { continue }; + localizedSubkeys.set(subkey, localizer(subkey, args, depth + 1)); + }; + + return localized.replace( + config.subKeyPattern, + (_fullMatch, subkey) => { + return localizedSubkeys.get(subkey); + }, + ); +};