UNPKG

@react-form-builder/core

Version:

React JSON Schema Form Builder to create complex, validated, reusable forms with no deep React knowledge required

280 lines (279 loc) 8.87 kB
import { FluentResource as E } from "@fluent/bundle"; import { isLocalizedProperty as I } from "../../../stores/ComponentStore.js"; import { getEditableFormData as M } from "../../../utils/contexts/ComponentDataContext.js"; import { isString as p } from "../../../utils/isString.js"; import { isUndefined as f, isNull as C, isBoolean as v } from "../../../utils/tools.js"; import { getValidatorPropertyBlockType as $ } from "../../ui/PropertyBlockType.js"; import { globalDefaultLanguage as k } from "../default.js"; import { LocalizationError as V } from "../LocalizationError.js"; import { createFluentBundle as h } from "./createFluentBundle.js"; import { restoreDots as P, replaceDots as _, dotInternalValue as j } from "./dots.js"; import { FluentBundleCache as w, setupLocalizationBundles as F, populateFluentBundle as O } from "./fluentBundleSetup.js"; import { logFluentErrors as b } from "./fluentErrors.js"; import { getMessageWithFallback as D, resolveLocalizedMessageValue as T } from "./fluentMessageResolution.js"; import { fluentEncodeValue as q } from "./fluentResource.js"; import { isFluentVariable as A } from "./isFluentVariable.js"; import { createLocalizationDataProxy as R } from "./localizationDataProxy.js"; const B = "interpolate-tmp"; class se { #l = new w(); #e = k.fullCode; /** * Constructor. * @param locale the language full code, i.e. 'en-US'. */ constructor(e) { this.#e = e ?? this.#e; } /** * @inheritDoc */ set language(e) { this.#e = e; } /** * @inheritDoc */ get language() { return this.#e; } /** * @inheritDoc */ addMessages(e, t) { return this.#l.addMessages(e, t, { allowOverrides: !0 }); } /** * @inheritDoc */ localizeProperties(e, t, r, s, o = "component") { const n = {}, i = [], a = F(e, r.fullCode, i), u = this.#c( e, t, s, o, r.fullCode, a ); return Object.entries(s.props).forEach(([l, m]) => { I(m) && (n[l] = this.#m(l, u)); }), n; } /** * @inheritDoc */ getCompatibleId(e) { return e.replace(/\s/g, "_"); } /** * @inheritDoc */ localizeErrorMessage(e, t, r, s, o) { const n = $(o), a = `${this.getCompatibleId(s.key)}_${n}_message`, u = [], { defaultBundle: l, defaultItems: m, formBundle: d, formItems: g } = F(e, r.fullCode, u), c = D(a, d, l, g, m); return c ? this.#o(c.message, c.bundle, t) : void 0; } /** * @inheritDoc */ testLocalization(e, t, r, s) { const o = [], n = r.fullCode, i = O( { [t]: e }, o, h(n) ), u = i.getMessage(t)?.value ?? "", l = [], { fluentData: m, missingProperties: d } = this.#r(s), g = P(i.formatPattern(u, m, l)); return b(l, d, !0), l.length > 0 ? l.map((c) => new V("MessageFormatError", c.message)) : g; } /** * Formats a message pattern with form data. * @param message the message to format. * @param bundle the bundle to use for formatting. * @param formData the form data for variable substitution. * @param options formatting options. * @param options.logMissing when true, logs missing property names used in placeholders. * @returns the formatted result or undefined. */ #o(e, t, r, s) { const o = [], { fluentData: n, missingProperties: i } = this.#r(r), a = t?.formatPattern(e, n, o); return b(o, i, s?.logMissing), f(a) ? void 0 : P(a); } /** * Builds fluent data proxy for the given form data. * @param formData form data. * @returns fluent data proxy and missing property collector. */ #r(e) { const t = M(e), r = []; return { fluentData: R( this.#a(t), r ), missingProperties: r }; } /** * Returns a lazy getter for fluent interpolation data. * @param formData form data. * @returns getter for fluent data proxy. */ #u(e) { let t; return () => (t ??= this.#r(e).fluentData, t); } /** * Builds shared context for localizing all properties of one component. * @param form the form instance. * @param formData current form data. * @param componentStore the component store. * @param type localization type. * @param requestedFullCode requested language full code. * @param bundleSetup prepared fluent bundles and item maps. * @returns component localization context. */ #c(e, t, r, s, o, n) { const i = this.getCompatibleId(r.key); return { form: e, formData: t, componentStore: r, type: s, requestedFullCode: o, messageIdPrefix: `${i}_${s}_`, getFluentData: this.#u(t), ...n }; } /** * Returns the bundle used for interpolation when form bundles are unavailable. * @param context component localization context. * @returns bundle for interpolation. */ #s(e) { return e.formBundle ?? e.defaultBundle ?? h(e.requestedFullCode); } /** * Resolves one localized component property. * @param propertyKey component property key. * @param context shared localization context for the component. * @returns resolved property value. */ #m(e, t) { const { form: r, formData: s, componentStore: o, type: n, requestedFullCode: i, messageIdPrefix: a, formBundle: u, defaultBundle: l, formItems: m, defaultItems: d, getFluentData: g } = t, c = r.localization.getLocalization( i, o.key, e, n ); if (!f(c) && !p(c)) return this.#t(c, this.#s(t), g()); const y = `${a}${this.getCompatibleId(e)}`, z = this.#d(y, m, d); if (!f(z)) return this.#t(z, this.#s(t), g()); const L = D(y, u, l, m, d); return this.#f(L, e, r, o, s); } /** * Resolves the localized component property value. * @param localizedMessage localized message and source bundle. * @param propertyKey component property key. * @param form the form instance. * @param componentStore the component store. * @param formData current form data. * @returns the resolved value. */ #f(e, t, r, s, o) { return T( e, (n, i) => this.#o(n, i, o), () => { const n = s.props[t]?.value; if (!f(n)) return n; const i = r.componentTree?.findByKey(s.key)?.model?.defaultProps?.[t]; return f(i) ? s.type : i; } ); } #d(e, t, r) { const s = t?.[e], o = this.#n(s); if (!f(o)) return o; const n = r?.[e]; return this.#n(n); } #n(e) { if (f(e)) return; if (!p(e)) return e; const t = e.trim(); if (t.startsWith("{") || t.startsWith("[")) try { return JSON.parse(t); } catch { return; } } /** * Interpolates a single string through a temporary Fluent bundle. * @param value the string potentially containing Fluent placeholders. * @param bundle the Fluent bundle to use for formatting context. * @param fluentData the flattened form data for variable substitution. * @returns the interpolated string, or the original string if it contains no placeholders. */ #i(e, t, r) { if (!e.includes("{")) return e; const s = _(e), o = h(t.locales[0]), n = new E(`${B} = ${q(s)}`); o.addResource(n); const i = o.getMessage(B); if (!i?.value) return e; const a = [], u = o.formatPattern(i.value, r, a); return P(u); } /** * Recursively traverses data of any shape and interpolates Fluent placeholders in string keys and values. * @param data the data to interpolate (can be any shape: primitive, array, or object). * @param bundle the Fluent bundle to use for formatting context. * @param fluentData the flattened form data for variable substitution. * @returns the data with all string keys and values interpolated. */ #t(e, t, r) { if (p(e)) return this.#i(e, t, r); if (Array.isArray(e)) return e.map((s) => this.#t(s, t, r)); if (typeof e == "object" && !C(e)) { const s = {}; for (const [o, n] of Object.entries(e)) { const i = this.#i(o, t, r); s[i] = this.#t(n, t, r); } return s; } return e; } /** * Converts the form data to a Fluent compatible. **Internal use only.** * @param data the form data. * @param parentKey the parent property key. * @returns all the form data that is of the FluentVariable type. * Additionally, the keys of the returned object are converted to the snake case. */ #a = (e, t = "") => { const r = {}; for (const [s, o] of Object.entries(e)) { const n = t ? `${t}${j}${s}` : s; A(o) ? r[this.getCompatibleId(n)] = o : v(o) ? r[this.getCompatibleId(n)] = o ? "true" : "false" : typeof o == "object" && !C(o) && Object.assign(r, this.#a(o, n)); } return r; }; } export { se as FluentLocalizationEngine }; //# sourceMappingURL=FluentLocalizationEngine.js.map