@form-engine-ts/core 4.6.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/index.cjs +214 -81
- package/dist/index.d.cts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +213 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,9 @@ Required locales cover every source text that exists on the form, its fields, op
|
|
|
52
52
|
safe maximum-length corrections while purging unregistered locale content. Pass
|
|
53
53
|
`{ policy: { allowedLocales, maxLocales } }` to `populateSchemaTranslations` to reject inadmissible targets before the
|
|
54
54
|
translation adapter runs.
|
|
55
|
+
`normalizeLocale(locale)` returns the canonical BCP 47 tag, accepts underscore-separated compatibility input such as
|
|
56
|
+
`ja_JP`, and returns `null` for invalid tags. Schema validation, sanitization, locale policy checks, and translation
|
|
57
|
+
slot lookup use this same normalization so equivalent locale spellings cannot bypass constraints.
|
|
55
58
|
|
|
56
59
|
Fields can use a `displayRule` with nested `all`/`any` condition groups and `show` or `hide` actions. Supported
|
|
57
60
|
operators include equality, containment, emptiness, and numeric comparisons; the legacy `displayCondition` and
|
|
@@ -63,7 +66,9 @@ and missing, translated, stale, or manual states for authoring tools. `populateS
|
|
|
63
66
|
and missing entries while preserving manual translations and reports skipped reasons.
|
|
64
67
|
|
|
65
68
|
Legacy translation metadata can be recognized with `isManualTranslationMetadata` and migrated with
|
|
66
|
-
`migrateSchemaTranslationMetadata`; pass a custom migrator when legacy fields need
|
|
69
|
+
`migrateSchemaTranslationMetadata`; pass a custom migrator (directly or as `{ migrator }`) when legacy fields need
|
|
70
|
+
application-specific conversion. The migrator receives `TranslationMigrationContext` with the locale, JSON path,
|
|
71
|
+
property, node kind, and node identifiers.
|
|
67
72
|
`PopulateTranslationsOptions` is a compatibility alias for `PopulateTranslationOptions`, which also accepts custom
|
|
68
73
|
manual-translation detection and metadata normalization callbacks. `removeLocaleFromSchema(schema, locale)` removes
|
|
69
74
|
the locale from registrations and all form, page, field, option translation values and metadata; the default locale
|
package/dist/index.cjs
CHANGED
|
@@ -57,6 +57,7 @@ __export(index_exports, {
|
|
|
57
57
|
matchesSubmissionFilter: () => matchesSubmissionFilter,
|
|
58
58
|
matchesSubmissionPageFilters: () => matchesSubmissionPageFilters,
|
|
59
59
|
migrateSchemaTranslationMetadata: () => migrateSchemaTranslationMetadata,
|
|
60
|
+
normalizeLocale: () => normalizeLocale,
|
|
60
61
|
normalizeSubmissionPageSize: () => normalizeSubmissionPageSize,
|
|
61
62
|
pipeResponsesToCsvStream: () => pipeResponsesToCsvStream,
|
|
62
63
|
populateSchemaTranslations: () => populateSchemaTranslations,
|
|
@@ -74,11 +75,27 @@ __export(index_exports, {
|
|
|
74
75
|
});
|
|
75
76
|
module.exports = __toCommonJS(index_exports);
|
|
76
77
|
|
|
78
|
+
// src/locale.ts
|
|
79
|
+
var normalizeLocale = (rawLocale) => {
|
|
80
|
+
if (!rawLocale || typeof rawLocale !== "string") return null;
|
|
81
|
+
const trimmed = rawLocale.trim().replace(/_/gu, "-");
|
|
82
|
+
if (trimmed.length === 0) return null;
|
|
83
|
+
try {
|
|
84
|
+
return Intl.getCanonicalLocales(trimmed)[0] ?? null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function canonicalLocaleOrRaw(rawLocale) {
|
|
90
|
+
return normalizeLocale(rawLocale) ?? rawLocale;
|
|
91
|
+
}
|
|
92
|
+
|
|
77
93
|
// src/policy.ts
|
|
78
94
|
function collectRecordKeys(value, path, pathsByLocale) {
|
|
79
|
-
for (const
|
|
95
|
+
for (const rawLocale of Object.keys(value ?? {})) {
|
|
96
|
+
const locale = canonicalLocaleOrRaw(rawLocale);
|
|
80
97
|
const paths = pathsByLocale.get(locale) ?? [];
|
|
81
|
-
paths.push(`${path}.${
|
|
98
|
+
paths.push(`${path}.${rawLocale}`);
|
|
82
99
|
pathsByLocale.set(locale, paths);
|
|
83
100
|
}
|
|
84
101
|
}
|
|
@@ -109,13 +126,15 @@ function collectSchemaLocales(schema) {
|
|
|
109
126
|
});
|
|
110
127
|
const translationLocales = new Set(pathsByLocale.keys());
|
|
111
128
|
const allUniqueLocales = /* @__PURE__ */ new Set([
|
|
112
|
-
...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
|
|
113
|
-
...schema.supportedLocales ?? [],
|
|
129
|
+
...schema.defaultLocale === void 0 ? [] : [canonicalLocaleOrRaw(schema.defaultLocale)],
|
|
130
|
+
...(schema.supportedLocales ?? []).map(canonicalLocaleOrRaw),
|
|
114
131
|
...translationLocales
|
|
115
132
|
]);
|
|
133
|
+
const defaultLocale = schema.defaultLocale === void 0 ? void 0 : canonicalLocaleOrRaw(schema.defaultLocale);
|
|
134
|
+
const supportedLocales = (schema.supportedLocales ?? []).map(canonicalLocaleOrRaw);
|
|
116
135
|
return {
|
|
117
|
-
...
|
|
118
|
-
supportedLocales
|
|
136
|
+
...defaultLocale === void 0 ? {} : { defaultLocale },
|
|
137
|
+
supportedLocales,
|
|
119
138
|
translationLocales,
|
|
120
139
|
allUniqueLocales,
|
|
121
140
|
translationLocalePaths: pathsByLocale
|
|
@@ -138,7 +157,12 @@ function displayRuleSourceIds(field) {
|
|
|
138
157
|
}
|
|
139
158
|
function registeredEntries(value, registeredLocales) {
|
|
140
159
|
if (value === void 0) return void 0;
|
|
141
|
-
const entries =
|
|
160
|
+
const entries = [];
|
|
161
|
+
for (const [rawLocale, entry] of Object.entries(value)) {
|
|
162
|
+
const locale = normalizeLocale(rawLocale);
|
|
163
|
+
if (locale === null || !registeredLocales.has(locale)) continue;
|
|
164
|
+
entries.push([locale, entry]);
|
|
165
|
+
}
|
|
142
166
|
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
143
167
|
}
|
|
144
168
|
function sanitizeNodeLocales(node, registeredLocales) {
|
|
@@ -176,6 +200,16 @@ function sanitizePageLocales(page, registeredLocales) {
|
|
|
176
200
|
const translations = registeredEntries(page.translations, registeredLocales);
|
|
177
201
|
return { ...base, ...translations === void 0 ? {} : { translations } };
|
|
178
202
|
}
|
|
203
|
+
function normalizedLocaleList(locales) {
|
|
204
|
+
return [
|
|
205
|
+
...new Set(
|
|
206
|
+
locales.flatMap((locale) => {
|
|
207
|
+
const normalized = normalizeLocale(locale);
|
|
208
|
+
return normalized === null ? [] : [normalized];
|
|
209
|
+
})
|
|
210
|
+
)
|
|
211
|
+
];
|
|
212
|
+
}
|
|
179
213
|
function sanitizeFieldConstraints(field, policy) {
|
|
180
214
|
const constraint = policy?.fieldConstraints?.[field.type];
|
|
181
215
|
if (constraint === void 0) return field;
|
|
@@ -290,10 +324,9 @@ function validateSchemaStructure(schema) {
|
|
|
290
324
|
}
|
|
291
325
|
function sanitizeSchema(schema, options = {}) {
|
|
292
326
|
const existingQuestionIds = new Set(schema.fields.map((field) => field.id));
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
]);
|
|
327
|
+
const defaultLocale = schema.defaultLocale === void 0 ? null : normalizeLocale(schema.defaultLocale);
|
|
328
|
+
const supportedLocales = schema.supportedLocales === void 0 ? void 0 : normalizedLocaleList(schema.supportedLocales);
|
|
329
|
+
const registeredLocales = /* @__PURE__ */ new Set([...defaultLocale === null ? [] : [defaultLocale], ...supportedLocales ?? []]);
|
|
297
330
|
const cyclic = cyclicQuestionIds(schema.fields);
|
|
298
331
|
const sanitizedFields = schema.fields.map((sourceField) => {
|
|
299
332
|
const field = sanitizeFieldConstraints(sanitizeFieldLocales(sourceField, registeredLocales), options.policy);
|
|
@@ -311,10 +344,17 @@ function sanitizeSchema(schema, options = {}) {
|
|
|
311
344
|
return sanitized;
|
|
312
345
|
});
|
|
313
346
|
const localizedSchema = sanitizeNodeLocales(schema, registeredLocales);
|
|
314
|
-
const {
|
|
347
|
+
const {
|
|
348
|
+
defaultLocale: _defaultLocale,
|
|
349
|
+
supportedLocales: _supportedLocales,
|
|
350
|
+
translations: _translations,
|
|
351
|
+
...schemaWithoutLocaleContent
|
|
352
|
+
} = localizedSchema;
|
|
315
353
|
const translations = registeredEntries(schema.translations, registeredLocales);
|
|
316
354
|
const base = {
|
|
317
355
|
...schemaWithoutLocaleContent,
|
|
356
|
+
...defaultLocale === null ? {} : { defaultLocale },
|
|
357
|
+
...supportedLocales === void 0 ? {} : { supportedLocales },
|
|
318
358
|
...translations === void 0 ? {} : { translations },
|
|
319
359
|
fields: sanitizedFields
|
|
320
360
|
};
|
|
@@ -367,6 +407,12 @@ function isRecord(value) {
|
|
|
367
407
|
function isNonEmptyString(value) {
|
|
368
408
|
return typeof value === "string" && value.trim().length > 0;
|
|
369
409
|
}
|
|
410
|
+
function localeRecordEntry(record, locale) {
|
|
411
|
+
for (const [candidate, value] of Object.entries(record ?? {})) {
|
|
412
|
+
if (canonicalLocaleOrRaw(candidate) === locale) return value;
|
|
413
|
+
}
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
370
416
|
function issue(issues, path, code, message) {
|
|
371
417
|
issues.push({ path, code, message });
|
|
372
418
|
}
|
|
@@ -729,51 +775,55 @@ function collectSchemaText(schema) {
|
|
|
729
775
|
return entries;
|
|
730
776
|
}
|
|
731
777
|
function addRequiredTranslationIssues(schema, locale, issues) {
|
|
732
|
-
if (!(schema.supportedLocales ?? []).
|
|
778
|
+
if (!(schema.supportedLocales ?? []).some((candidate) => canonicalLocaleOrRaw(candidate) === locale)) {
|
|
733
779
|
issue(issues, "supportedLocales", "required_locale_missing", `Required locale ${locale} is missing.`);
|
|
734
780
|
}
|
|
735
|
-
if (
|
|
781
|
+
if (schema.defaultLocale !== void 0 && canonicalLocaleOrRaw(schema.defaultLocale) === locale) return;
|
|
782
|
+
const formTranslation = localeRecordEntry(schema.translations, locale);
|
|
736
783
|
const required = [
|
|
737
|
-
{ path: `translations.${locale}.title`, value:
|
|
784
|
+
{ path: `translations.${locale}.title`, value: formTranslation?.title }
|
|
738
785
|
];
|
|
739
786
|
if (schema.description !== void 0)
|
|
740
|
-
required.push({ path: `translations.${locale}.description`, value:
|
|
787
|
+
required.push({ path: `translations.${locale}.description`, value: formTranslation?.description });
|
|
741
788
|
if (schema.completionMessage !== void 0) {
|
|
742
789
|
required.push({
|
|
743
790
|
path: `translations.${locale}.completionMessage`,
|
|
744
|
-
value:
|
|
791
|
+
value: formTranslation?.completionMessage
|
|
745
792
|
});
|
|
746
793
|
}
|
|
747
794
|
schema.fields.forEach((field, fieldIndex) => {
|
|
795
|
+
const fieldTranslation = localeRecordEntry(field.translations, locale);
|
|
748
796
|
required.push({
|
|
749
797
|
path: `fields[${fieldIndex}].translations.${locale}.title`,
|
|
750
|
-
value:
|
|
798
|
+
value: fieldTranslation?.title
|
|
751
799
|
});
|
|
752
800
|
if (field.description !== void 0) {
|
|
753
801
|
required.push({
|
|
754
802
|
path: `fields[${fieldIndex}].translations.${locale}.description`,
|
|
755
|
-
value:
|
|
803
|
+
value: fieldTranslation?.description
|
|
756
804
|
});
|
|
757
805
|
}
|
|
758
806
|
if (!("options" in field)) return;
|
|
759
807
|
field.options.forEach((option, optionIndex) => {
|
|
808
|
+
const optionTranslation = localeRecordEntry(option.translations, locale);
|
|
760
809
|
required.push({
|
|
761
810
|
path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`,
|
|
762
|
-
value:
|
|
811
|
+
value: optionTranslation
|
|
763
812
|
});
|
|
764
813
|
});
|
|
765
814
|
});
|
|
766
815
|
schema.pages?.forEach((page, pageIndex) => {
|
|
816
|
+
const pageTranslation = localeRecordEntry(page.translations, locale);
|
|
767
817
|
if (page.title !== void 0) {
|
|
768
818
|
required.push({
|
|
769
819
|
path: `pages[${pageIndex}].translations.${locale}.title`,
|
|
770
|
-
value:
|
|
820
|
+
value: pageTranslation?.title
|
|
771
821
|
});
|
|
772
822
|
}
|
|
773
823
|
if (page.description !== void 0) {
|
|
774
824
|
required.push({
|
|
775
825
|
path: `pages[${pageIndex}].translations.${locale}.description`,
|
|
776
|
-
value:
|
|
826
|
+
value: pageTranslation?.description
|
|
777
827
|
});
|
|
778
828
|
}
|
|
779
829
|
});
|
|
@@ -906,8 +956,8 @@ function validatePolicy(schema, policy, issues) {
|
|
|
906
956
|
}
|
|
907
957
|
const collectedLocales = collectSchemaLocales(schema);
|
|
908
958
|
const registeredLocales = /* @__PURE__ */ new Set([
|
|
909
|
-
...
|
|
910
|
-
...
|
|
959
|
+
...collectedLocales.defaultLocale === void 0 ? [] : [collectedLocales.defaultLocale],
|
|
960
|
+
...collectedLocales.supportedLocales
|
|
911
961
|
]);
|
|
912
962
|
for (const locale of collectedLocales.translationLocales) {
|
|
913
963
|
if (registeredLocales.has(locale)) continue;
|
|
@@ -921,23 +971,27 @@ function validatePolicy(schema, policy, issues) {
|
|
|
921
971
|
}
|
|
922
972
|
}
|
|
923
973
|
if (policy.allowedLocales !== void 0) {
|
|
974
|
+
const allowedLocales = new Set(policy.allowedLocales.map(canonicalLocaleOrRaw));
|
|
924
975
|
const pathsByLocale = /* @__PURE__ */ new Map();
|
|
925
|
-
if (
|
|
976
|
+
if (collectedLocales.defaultLocale !== void 0)
|
|
977
|
+
pathsByLocale.set(collectedLocales.defaultLocale, ["defaultLocale"]);
|
|
926
978
|
schema.supportedLocales?.forEach((locale, index) => {
|
|
927
|
-
|
|
979
|
+
const canonical = canonicalLocaleOrRaw(locale);
|
|
980
|
+
pathsByLocale.set(canonical, [...pathsByLocale.get(canonical) ?? [], `supportedLocales[${index}]`]);
|
|
928
981
|
});
|
|
929
982
|
for (const [locale, paths] of collectedLocales.translationLocalePaths) {
|
|
930
983
|
pathsByLocale.set(locale, [...pathsByLocale.get(locale) ?? [], ...paths]);
|
|
931
984
|
}
|
|
932
985
|
for (const [locale, paths] of pathsByLocale) {
|
|
933
|
-
if (!
|
|
986
|
+
if (!allowedLocales.has(locale)) {
|
|
934
987
|
for (const path of paths) {
|
|
935
988
|
issue(issues, path, "disallowed_locale", `Locale ${locale} is not allowed by the form policy.`);
|
|
936
989
|
}
|
|
937
990
|
}
|
|
938
991
|
}
|
|
939
|
-
for (const
|
|
940
|
-
|
|
992
|
+
for (const rawLocale of policy.requiredLocales ?? []) {
|
|
993
|
+
const locale = canonicalLocaleOrRaw(rawLocale);
|
|
994
|
+
if (!allowedLocales.has(locale)) {
|
|
941
995
|
issue(
|
|
942
996
|
issues,
|
|
943
997
|
"policy.requiredLocales",
|
|
@@ -950,7 +1004,8 @@ function validatePolicy(schema, policy, issues) {
|
|
|
950
1004
|
if (policy.maxLocales !== void 0 && collectedLocales.allUniqueLocales.size > policy.maxLocales) {
|
|
951
1005
|
issue(issues, "supportedLocales", "max_locales_exceeded", `At most ${policy.maxLocales} locales are allowed.`);
|
|
952
1006
|
}
|
|
953
|
-
for (const locale of policy.requiredLocales ?? [])
|
|
1007
|
+
for (const locale of policy.requiredLocales ?? [])
|
|
1008
|
+
addRequiredTranslationIssues(schema, canonicalLocaleOrRaw(locale), issues);
|
|
954
1009
|
if (policy.maxSchemaBytes !== void 0) {
|
|
955
1010
|
try {
|
|
956
1011
|
const byteLength = new TextEncoder().encode(JSON.stringify(schema)).byteLength;
|
|
@@ -985,8 +1040,12 @@ function validateFormSchema(input, options = {}) {
|
|
|
985
1040
|
);
|
|
986
1041
|
}
|
|
987
1042
|
}
|
|
988
|
-
if (input.defaultLocale !== void 0
|
|
989
|
-
|
|
1043
|
+
if (input.defaultLocale !== void 0) {
|
|
1044
|
+
if (!isNonEmptyString(input.defaultLocale)) {
|
|
1045
|
+
issue(issues, "defaultLocale", "invalid_locale", "Expected a non-empty default locale.");
|
|
1046
|
+
} else if (normalizeLocale(input.defaultLocale) === null) {
|
|
1047
|
+
issue(issues, "defaultLocale", "invalid_locale", "Expected a valid BCP 47 locale.");
|
|
1048
|
+
}
|
|
990
1049
|
}
|
|
991
1050
|
if (input.supportedLocales !== void 0) {
|
|
992
1051
|
if (!Array.isArray(input.supportedLocales) || input.supportedLocales.length === 0) {
|
|
@@ -996,10 +1055,15 @@ function validateFormSchema(input, options = {}) {
|
|
|
996
1055
|
input.supportedLocales.forEach((locale, index) => {
|
|
997
1056
|
if (!isNonEmptyString(locale)) {
|
|
998
1057
|
issue(issues, `supportedLocales[${index}]`, "invalid_locale", "Expected a non-empty locale.");
|
|
999
|
-
} else if (locales.has(locale)) {
|
|
1000
|
-
issue(issues, `supportedLocales[${index}]`, "duplicate_locale", "Locales must be unique.");
|
|
1001
1058
|
} else {
|
|
1002
|
-
|
|
1059
|
+
const normalized = normalizeLocale(locale);
|
|
1060
|
+
if (normalized === null) {
|
|
1061
|
+
issue(issues, `supportedLocales[${index}]`, "invalid_locale", "Expected a valid BCP 47 locale.");
|
|
1062
|
+
} else if (locales.has(normalized)) {
|
|
1063
|
+
issue(issues, `supportedLocales[${index}]`, "duplicate_locale", "Locales must be unique.");
|
|
1064
|
+
} else {
|
|
1065
|
+
locales.add(normalized);
|
|
1066
|
+
}
|
|
1003
1067
|
}
|
|
1004
1068
|
});
|
|
1005
1069
|
}
|
|
@@ -2268,8 +2332,15 @@ function withTranslationMetadata(node, locale, property, metadata) {
|
|
|
2268
2332
|
}
|
|
2269
2333
|
};
|
|
2270
2334
|
}
|
|
2271
|
-
function
|
|
2272
|
-
const
|
|
2335
|
+
function localeRecordEntry2(record, locale) {
|
|
2336
|
+
const normalizedLocale = normalizeLocale(locale) ?? locale;
|
|
2337
|
+
for (const [candidate, value] of Object.entries(record ?? {})) {
|
|
2338
|
+
if ((normalizeLocale(candidate) ?? candidate) === normalizedLocale) return value;
|
|
2339
|
+
}
|
|
2340
|
+
return void 0;
|
|
2341
|
+
}
|
|
2342
|
+
function createSlot(kind, nodeId, property, locale, sourceText, existingText, nodeMetadata, existingTranslationMetadata, options = {}, parentId) {
|
|
2343
|
+
const path = kind === "form" ? `form.${property}` : kind === "option" ? `fields.${parentId ?? ""}.options.${nodeId}.${property}` : `${kind}s.${nodeId}.${property}`;
|
|
2273
2344
|
const manual = options.isManualTranslation?.(existingTranslationMetadata, { path, locale }) ?? isManualTranslationMetadata(existingTranslationMetadata);
|
|
2274
2345
|
const normalizedMetadata = existingTranslationMetadata === void 0 ? void 0 : options.normalizeMetadata === void 0 ? existingTranslationMetadata : { ...options.normalizeMetadata(existingTranslationMetadata, sourceText) };
|
|
2275
2346
|
const status = getTranslationStatusWithManualOverride(sourceText, existingText, normalizedMetadata, manual);
|
|
@@ -2289,7 +2360,10 @@ function createSlot(kind, nodeId, property, locale, sourceText, existingText, no
|
|
|
2289
2360
|
};
|
|
2290
2361
|
}
|
|
2291
2362
|
function translationSlots(schema, locale, options = {}) {
|
|
2363
|
+
locale = normalizeLocale(locale) ?? locale;
|
|
2292
2364
|
const descriptors = [];
|
|
2365
|
+
const schemaTranslation = localeRecordEntry2(schema.translations, locale);
|
|
2366
|
+
const schemaTranslationMetadata = localeRecordEntry2(schema.translationMetadata, locale);
|
|
2293
2367
|
const addFormSlot = (property, sourceText) => {
|
|
2294
2368
|
const slot = createSlot(
|
|
2295
2369
|
"form",
|
|
@@ -2297,17 +2371,17 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2297
2371
|
property,
|
|
2298
2372
|
locale,
|
|
2299
2373
|
sourceText,
|
|
2300
|
-
|
|
2374
|
+
schemaTranslation?.[property],
|
|
2301
2375
|
schema.metadata,
|
|
2302
|
-
|
|
2376
|
+
schemaTranslationMetadata?.[property],
|
|
2303
2377
|
options
|
|
2304
2378
|
);
|
|
2305
2379
|
descriptors.push({
|
|
2306
2380
|
slot,
|
|
2307
|
-
manual: options.isManualTranslation?.(
|
|
2381
|
+
manual: options.isManualTranslation?.(schemaTranslationMetadata?.[property], {
|
|
2308
2382
|
path: slot.path ?? "",
|
|
2309
2383
|
locale
|
|
2310
|
-
}) ?? isManualTranslationMetadata(
|
|
2384
|
+
}) ?? isManualTranslationMetadata(schemaTranslationMetadata?.[property]),
|
|
2311
2385
|
apply: (current, value, metadata) => withTranslationMetadata(
|
|
2312
2386
|
{ ...current, translations: mergeLocalizedText(current.translations, locale, property, value) },
|
|
2313
2387
|
locale,
|
|
@@ -2320,6 +2394,8 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2320
2394
|
if (schema.description !== void 0) addFormSlot("description", schema.description);
|
|
2321
2395
|
if (schema.completionMessage !== void 0) addFormSlot("completionMessage", schema.completionMessage);
|
|
2322
2396
|
schema.fields.forEach((field, fieldIndex) => {
|
|
2397
|
+
const fieldTranslation = localeRecordEntry2(field.translations, locale);
|
|
2398
|
+
const fieldTranslationMetadata = localeRecordEntry2(field.translationMetadata, locale);
|
|
2323
2399
|
const addFieldSlot = (property, sourceText) => {
|
|
2324
2400
|
const slot = createSlot(
|
|
2325
2401
|
"field",
|
|
@@ -2327,17 +2403,17 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2327
2403
|
property,
|
|
2328
2404
|
locale,
|
|
2329
2405
|
sourceText,
|
|
2330
|
-
|
|
2406
|
+
fieldTranslation?.[property],
|
|
2331
2407
|
field.metadata,
|
|
2332
|
-
|
|
2408
|
+
fieldTranslationMetadata?.[property],
|
|
2333
2409
|
options
|
|
2334
2410
|
);
|
|
2335
2411
|
descriptors.push({
|
|
2336
2412
|
slot,
|
|
2337
|
-
manual: options.isManualTranslation?.(
|
|
2413
|
+
manual: options.isManualTranslation?.(fieldTranslationMetadata?.[property], {
|
|
2338
2414
|
path: slot.path ?? "",
|
|
2339
2415
|
locale
|
|
2340
|
-
}) ?? isManualTranslationMetadata(
|
|
2416
|
+
}) ?? isManualTranslationMetadata(fieldTranslationMetadata?.[property]),
|
|
2341
2417
|
apply: (current, value, metadata) => ({
|
|
2342
2418
|
...current,
|
|
2343
2419
|
fields: current.fields.map(
|
|
@@ -2358,23 +2434,26 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2358
2434
|
if (field.description !== void 0) addFieldSlot("description", field.description);
|
|
2359
2435
|
if ("options" in field) {
|
|
2360
2436
|
field.options.forEach((option, optionIndex) => {
|
|
2437
|
+
const optionTranslation = localeRecordEntry2(option.translations, locale);
|
|
2438
|
+
const optionTranslationMetadata = localeRecordEntry2(option.translationMetadata, locale);
|
|
2361
2439
|
const slot = createSlot(
|
|
2362
2440
|
"option",
|
|
2363
2441
|
option.id,
|
|
2364
2442
|
"label",
|
|
2365
2443
|
locale,
|
|
2366
2444
|
option.label,
|
|
2367
|
-
|
|
2445
|
+
optionTranslation,
|
|
2368
2446
|
option.metadata,
|
|
2369
|
-
|
|
2370
|
-
options
|
|
2447
|
+
optionTranslationMetadata?.label,
|
|
2448
|
+
options,
|
|
2449
|
+
field.id
|
|
2371
2450
|
);
|
|
2372
2451
|
descriptors.push({
|
|
2373
2452
|
slot,
|
|
2374
|
-
manual: options.isManualTranslation?.(
|
|
2453
|
+
manual: options.isManualTranslation?.(optionTranslationMetadata?.label, {
|
|
2375
2454
|
path: slot.path ?? "",
|
|
2376
2455
|
locale
|
|
2377
|
-
}) ?? isManualTranslationMetadata(
|
|
2456
|
+
}) ?? isManualTranslationMetadata(optionTranslationMetadata?.label),
|
|
2378
2457
|
apply: (current, value, metadata) => ({
|
|
2379
2458
|
...current,
|
|
2380
2459
|
fields: current.fields.map((candidate, candidateIndex) => {
|
|
@@ -2400,6 +2479,8 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2400
2479
|
}
|
|
2401
2480
|
});
|
|
2402
2481
|
schema.pages?.forEach((page, pageIndex) => {
|
|
2482
|
+
const pageTranslation = localeRecordEntry2(page.translations, locale);
|
|
2483
|
+
const pageTranslationMetadata = localeRecordEntry2(page.translationMetadata, locale);
|
|
2403
2484
|
const addPageSlot = (property, sourceText) => {
|
|
2404
2485
|
const slot = createSlot(
|
|
2405
2486
|
"page",
|
|
@@ -2407,17 +2488,17 @@ function translationSlots(schema, locale, options = {}) {
|
|
|
2407
2488
|
property,
|
|
2408
2489
|
locale,
|
|
2409
2490
|
sourceText,
|
|
2410
|
-
|
|
2491
|
+
pageTranslation?.[property],
|
|
2411
2492
|
page.metadata,
|
|
2412
|
-
|
|
2493
|
+
pageTranslationMetadata?.[property],
|
|
2413
2494
|
options
|
|
2414
2495
|
);
|
|
2415
2496
|
descriptors.push({
|
|
2416
2497
|
slot,
|
|
2417
|
-
manual: options.isManualTranslation?.(
|
|
2498
|
+
manual: options.isManualTranslation?.(pageTranslationMetadata?.[property], {
|
|
2418
2499
|
path: slot.path ?? "",
|
|
2419
2500
|
locale
|
|
2420
|
-
}) ?? isManualTranslationMetadata(
|
|
2501
|
+
}) ?? isManualTranslationMetadata(pageTranslationMetadata?.[property]),
|
|
2421
2502
|
apply: (current, value, metadata) => ({
|
|
2422
2503
|
...current,
|
|
2423
2504
|
...current.pages === void 0 ? {} : {
|
|
@@ -2456,10 +2537,9 @@ function removeLocalizedNodeLocale(node, locale) {
|
|
|
2456
2537
|
...translationMetadata === void 0 ? {} : { translationMetadata }
|
|
2457
2538
|
};
|
|
2458
2539
|
}
|
|
2459
|
-
function
|
|
2460
|
-
if (customMigrator !== void 0) return customMigrator(metadata, sourceText);
|
|
2540
|
+
function defaultTranslationMetadataMigrator(metadata, sourceText, defaultLocale) {
|
|
2461
2541
|
const record = metadata !== null && typeof metadata === "object" ? metadata : void 0;
|
|
2462
|
-
const sourceLocale = typeof record?.sourceLocale === "string" ? record.sourceLocale : defaultLocale
|
|
2542
|
+
const sourceLocale = typeof record?.sourceLocale === "string" ? record.sourceLocale : defaultLocale;
|
|
2463
2543
|
const translationSource = isManualTranslationMetadata(record) ? "manual" : "automatic";
|
|
2464
2544
|
return {
|
|
2465
2545
|
sourceLocale,
|
|
@@ -2469,22 +2549,33 @@ function migrateMetadata(metadata, sourceText, defaultLocale, customMigrator) {
|
|
|
2469
2549
|
...typeof record?.editedAt === "string" ? { editedAt: record.editedAt } : {}
|
|
2470
2550
|
};
|
|
2471
2551
|
}
|
|
2472
|
-
function
|
|
2552
|
+
function migrateMetadata(metadata, sourceText, context, migrator) {
|
|
2553
|
+
return migrator(metadata, sourceText, context);
|
|
2554
|
+
}
|
|
2555
|
+
function isTranslationMetadataProperty(property) {
|
|
2556
|
+
return property === "title" || property === "description" || property === "label" || property === "completionMessage";
|
|
2557
|
+
}
|
|
2558
|
+
function migrateNodeMetadata(node, sourceTexts, contextFor, migrator) {
|
|
2473
2559
|
if (node.translationMetadata === void 0) return node;
|
|
2474
2560
|
const translationMetadata = Object.fromEntries(
|
|
2475
2561
|
Object.entries(node.translationMetadata).map(([locale, properties]) => [
|
|
2476
2562
|
locale,
|
|
2477
2563
|
Object.fromEntries(
|
|
2478
|
-
Object.entries(properties).map(([property, metadata]) =>
|
|
2479
|
-
property,
|
|
2480
|
-
|
|
2481
|
-
|
|
2564
|
+
Object.entries(properties).map(([property, metadata]) => {
|
|
2565
|
+
if (!isTranslationMetadataProperty(property)) return [property, metadata];
|
|
2566
|
+
return [
|
|
2567
|
+
property,
|
|
2568
|
+
migrateMetadata(metadata, sourceTexts[property] ?? "", contextFor(locale, property), migrator)
|
|
2569
|
+
];
|
|
2570
|
+
})
|
|
2482
2571
|
)
|
|
2483
2572
|
])
|
|
2484
2573
|
);
|
|
2485
2574
|
return { ...node, translationMetadata };
|
|
2486
2575
|
}
|
|
2487
|
-
var migrateSchemaTranslationMetadata = (schema,
|
|
2576
|
+
var migrateSchemaTranslationMetadata = (schema, migratorOrOptions) => {
|
|
2577
|
+
const defaultLocale = schema.defaultLocale ?? "";
|
|
2578
|
+
const migrator = typeof migratorOrOptions === "function" ? migratorOrOptions : migratorOrOptions?.migrator ?? ((metadata, sourceText, context) => defaultTranslationMetadataMigrator(metadata, sourceText, context.defaultLocale));
|
|
2488
2579
|
const migratedSchema = migrateNodeMetadata(
|
|
2489
2580
|
schema,
|
|
2490
2581
|
{
|
|
@@ -2492,21 +2583,47 @@ var migrateSchemaTranslationMetadata = (schema, customMigrator) => {
|
|
|
2492
2583
|
...schema.description === void 0 ? {} : { description: schema.description },
|
|
2493
2584
|
...schema.completionMessage === void 0 ? {} : { completionMessage: schema.completionMessage }
|
|
2494
2585
|
},
|
|
2495
|
-
|
|
2496
|
-
|
|
2586
|
+
(locale, property) => ({
|
|
2587
|
+
locale,
|
|
2588
|
+
defaultLocale,
|
|
2589
|
+
path: `form.${property}`,
|
|
2590
|
+
property,
|
|
2591
|
+
nodeKind: "form"
|
|
2592
|
+
}),
|
|
2593
|
+
migrator
|
|
2497
2594
|
);
|
|
2498
2595
|
const fields = schema.fields.map((field) => {
|
|
2499
2596
|
const migratedField = migrateNodeMetadata(
|
|
2500
2597
|
field,
|
|
2501
2598
|
{ title: field.title, ...field.description === void 0 ? {} : { description: field.description } },
|
|
2502
|
-
|
|
2503
|
-
|
|
2599
|
+
(locale, property) => ({
|
|
2600
|
+
locale,
|
|
2601
|
+
defaultLocale,
|
|
2602
|
+
path: `fields.${field.id}.${property}`,
|
|
2603
|
+
property,
|
|
2604
|
+
nodeKind: "field",
|
|
2605
|
+
nodeId: field.id
|
|
2606
|
+
}),
|
|
2607
|
+
migrator
|
|
2504
2608
|
);
|
|
2505
2609
|
if (!("options" in migratedField)) return migratedField;
|
|
2506
2610
|
return {
|
|
2507
2611
|
...migratedField,
|
|
2508
2612
|
options: migratedField.options.map(
|
|
2509
|
-
(option) => migrateNodeMetadata(
|
|
2613
|
+
(option) => migrateNodeMetadata(
|
|
2614
|
+
option,
|
|
2615
|
+
{ label: option.label },
|
|
2616
|
+
(locale, property) => ({
|
|
2617
|
+
locale,
|
|
2618
|
+
defaultLocale,
|
|
2619
|
+
path: `fields.${field.id}.options.${option.id}.${property}`,
|
|
2620
|
+
property,
|
|
2621
|
+
nodeKind: "option",
|
|
2622
|
+
nodeId: option.id,
|
|
2623
|
+
parentId: field.id
|
|
2624
|
+
}),
|
|
2625
|
+
migrator
|
|
2626
|
+
)
|
|
2510
2627
|
)
|
|
2511
2628
|
};
|
|
2512
2629
|
});
|
|
@@ -2517,8 +2634,15 @@ var migrateSchemaTranslationMetadata = (schema, customMigrator) => {
|
|
|
2517
2634
|
...page.title === void 0 ? {} : { title: page.title },
|
|
2518
2635
|
...page.description === void 0 ? {} : { description: page.description }
|
|
2519
2636
|
},
|
|
2520
|
-
|
|
2521
|
-
|
|
2637
|
+
(locale, property) => ({
|
|
2638
|
+
locale,
|
|
2639
|
+
defaultLocale,
|
|
2640
|
+
path: `pages.${page.id}.${property}`,
|
|
2641
|
+
property,
|
|
2642
|
+
nodeKind: "page",
|
|
2643
|
+
nodeId: page.id
|
|
2644
|
+
}),
|
|
2645
|
+
migrator
|
|
2522
2646
|
)
|
|
2523
2647
|
);
|
|
2524
2648
|
return {
|
|
@@ -2561,8 +2685,11 @@ function collectTranslationSlots(schema, locale) {
|
|
|
2561
2685
|
return translationSlots(schema, locale).map((descriptor) => descriptor.slot);
|
|
2562
2686
|
}
|
|
2563
2687
|
function resolveLocalizedSchema(schema, targetLocale) {
|
|
2564
|
-
if (targetLocale === void 0 || targetLocale.length === 0
|
|
2565
|
-
const
|
|
2688
|
+
if (targetLocale === void 0 || targetLocale.length === 0) return schema;
|
|
2689
|
+
const normalizedTargetLocale = normalizeLocale(targetLocale) ?? targetLocale;
|
|
2690
|
+
const defaultLocale = schema.defaultLocale === void 0 ? void 0 : normalizeLocale(schema.defaultLocale);
|
|
2691
|
+
if (normalizedTargetLocale === defaultLocale || targetLocale === schema.defaultLocale) return schema;
|
|
2692
|
+
const formTranslation = localeRecordEntry2(schema.translations, normalizedTargetLocale);
|
|
2566
2693
|
const completionMessage = formTranslation?.completionMessage ?? schema.completionMessage;
|
|
2567
2694
|
return {
|
|
2568
2695
|
...schema,
|
|
@@ -2570,7 +2697,7 @@ function resolveLocalizedSchema(schema, targetLocale) {
|
|
|
2570
2697
|
...(formTranslation?.description ?? schema.description) === void 0 ? {} : { description: formTranslation?.description ?? schema.description },
|
|
2571
2698
|
...completionMessage === void 0 ? {} : { completionMessage },
|
|
2572
2699
|
fields: schema.fields.map((field) => {
|
|
2573
|
-
const translation = field.translations
|
|
2700
|
+
const translation = localeRecordEntry2(field.translations, normalizedTargetLocale);
|
|
2574
2701
|
const localized = {
|
|
2575
2702
|
...field,
|
|
2576
2703
|
title: translation?.title ?? field.title,
|
|
@@ -2581,13 +2708,13 @@ function resolveLocalizedSchema(schema, targetLocale) {
|
|
|
2581
2708
|
...localized,
|
|
2582
2709
|
options: field.options.map((option) => ({
|
|
2583
2710
|
...option,
|
|
2584
|
-
label: option.translations
|
|
2711
|
+
label: localeRecordEntry2(option.translations, normalizedTargetLocale) ?? option.label
|
|
2585
2712
|
}))
|
|
2586
2713
|
};
|
|
2587
2714
|
}),
|
|
2588
2715
|
...schema.pages === void 0 ? {} : {
|
|
2589
2716
|
pages: schema.pages.map((page) => {
|
|
2590
|
-
const translation = page.translations
|
|
2717
|
+
const translation = localeRecordEntry2(page.translations, normalizedTargetLocale);
|
|
2591
2718
|
const title = translation?.title ?? page.title;
|
|
2592
2719
|
const description = translation?.description ?? page.description;
|
|
2593
2720
|
return {
|
|
@@ -2601,8 +2728,13 @@ function resolveLocalizedSchema(schema, targetLocale) {
|
|
|
2601
2728
|
}
|
|
2602
2729
|
async function populateSchemaTranslations(schema, targetLocales, adapter, options = {}) {
|
|
2603
2730
|
assertValidFormSchema(schema);
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2731
|
+
const defaultLocale = schema.defaultLocale === void 0 ? void 0 : normalizeLocale(schema.defaultLocale) ?? schema.defaultLocale;
|
|
2732
|
+
const locales = [
|
|
2733
|
+
...new Set(
|
|
2734
|
+
targetLocales.map((locale) => normalizeLocale(locale) ?? locale.trim()).filter((locale) => locale.length > 0 && locale !== defaultLocale)
|
|
2735
|
+
)
|
|
2736
|
+
];
|
|
2737
|
+
const allowedLocales = options.policy?.allowedLocales?.map((locale) => normalizeLocale(locale) ?? locale);
|
|
2606
2738
|
const collectedLocales = collectSchemaLocales(schema);
|
|
2607
2739
|
const disallowedLocale = [...collectedLocales.allUniqueLocales, ...locales].find(
|
|
2608
2740
|
(locale) => allowedLocales !== void 0 && !allowedLocales.includes(locale)
|
|
@@ -2644,7 +2776,7 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2644
2776
|
adapter,
|
|
2645
2777
|
selected.map((descriptor) => descriptor.slot.sourceText),
|
|
2646
2778
|
locale,
|
|
2647
|
-
|
|
2779
|
+
defaultLocale
|
|
2648
2780
|
);
|
|
2649
2781
|
if (translated.length !== selected.length) {
|
|
2650
2782
|
throw new Error(`Translation adapter returned ${translated.length} texts for ${selected.length} inputs.`);
|
|
@@ -2653,7 +2785,7 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2653
2785
|
const translatedText = translated[index];
|
|
2654
2786
|
if (translatedText === void 0) throw new Error("Translation adapter returned an unexpected result.");
|
|
2655
2787
|
const metadata = options.createMetadata?.(descriptor.slot, translatedText) ?? {
|
|
2656
|
-
sourceLocale:
|
|
2788
|
+
sourceLocale: defaultLocale ?? "",
|
|
2657
2789
|
sourceTextHash: computeSourceTextHash(descriptor.slot.sourceText),
|
|
2658
2790
|
translationSource: "automatic",
|
|
2659
2791
|
translatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2664,8 +2796,8 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2664
2796
|
}
|
|
2665
2797
|
const supportedLocales = [
|
|
2666
2798
|
.../* @__PURE__ */ new Set([
|
|
2667
|
-
...
|
|
2668
|
-
...schema.supportedLocales ?? [],
|
|
2799
|
+
...defaultLocale === void 0 ? [] : [defaultLocale],
|
|
2800
|
+
...(schema.supportedLocales ?? []).map((locale) => normalizeLocale(locale) ?? locale),
|
|
2669
2801
|
...locales
|
|
2670
2802
|
])
|
|
2671
2803
|
];
|
|
@@ -3001,6 +3133,7 @@ function assertVersionMutable(status) {
|
|
|
3001
3133
|
matchesSubmissionFilter,
|
|
3002
3134
|
matchesSubmissionPageFilters,
|
|
3003
3135
|
migrateSchemaTranslationMetadata,
|
|
3136
|
+
normalizeLocale,
|
|
3004
3137
|
normalizeSubmissionPageSize,
|
|
3005
3138
|
pipeResponsesToCsvStream,
|
|
3006
3139
|
populateSchemaTranslations,
|