diff --git a/langs/en-ca.json b/langs/en-ca.json index af3b8f9..751507c 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -7,6 +7,9 @@ "RipCrypt": { "sheet-names": { "HeroSummaryCardV1": "Hero Stat Card" + }, + "common": { + "empty": "---" } } } diff --git a/module/handlebarHelpers/_index.mjs b/module/handlebarHelpers/_index.mjs new file mode 100644 index 0000000..1603faf --- /dev/null +++ b/module/handlebarHelpers/_index.mjs @@ -0,0 +1,11 @@ +import { handlebarsLocalizer, localizer } from "../utils/Localizer.mjs"; +import { options } from "./options.mjs"; + +export default { + // #region Complex + "rc-i18n": handlebarsLocalizer, + "rc-options": options, + + // #region Simple + "rc-empty-state": (v) => v ?? localizer(`RipCrypt.common.empty`), +}; diff --git a/module/handlebarHelpers/options.mjs b/module/handlebarHelpers/options.mjs new file mode 100644 index 0000000..e88ba34 --- /dev/null +++ b/module/handlebarHelpers/options.mjs @@ -0,0 +1,36 @@ +import { localizer } from "../utils/Localizer.mjs"; + +/** + * @typedef {object} Option + * @property {string} [label] + * @property {string|number} value + * @property {boolean} [disabled] + */ + +/** + * @param {string | number} selected + * @param {Array`, + ); + }; +}; diff --git a/module/utils/Localizer.mjs b/module/utils/Localizer.mjs new file mode 100644 index 0000000..1a92058 --- /dev/null +++ b/module/utils/Localizer.mjs @@ -0,0 +1,37 @@ +import { localizerConfig } from "../config.mjs"; + +export function handlebarsLocalizer(key, ...args) { + let data = args[0]; + if (args.length === 1) { data = args[0].hash }; + if (key instanceof Handlebars.SafeString) { key = key.toString() }; + const localized = localizer(key, data); + return localized; +}; + +export function localizer(key, args = {}, depth = 0) { + /** @type {string} */ + let localized = game.i18n.format(key, args); + const subkeys = localized.matchAll(localizerConfig.subKeyPattern); + + // Short-cut to help prevent infinite recursion + if (depth > localizerConfig.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( + localizerConfig.subKeyPattern, + (_fullMatch, subkey) => { + return localizedSubkeys.get(subkey); + }, + ); +};