@form-engine-ts/react 6.2.0 → 6.3.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 +3 -2
- package/dist/index.cjs +128 -38
- package/dist/index.d.cts +37 -4
- package/dist/index.d.ts +37 -4
- package/dist/index.js +128 -38
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -97,8 +97,9 @@ its `onConfirm` callback completes the locale removal.
|
|
|
97
97
|
|
|
98
98
|
`useTranslationComparison` provides a focused, controlled comparison model for form, page, field, and option text.
|
|
99
99
|
Its items expose source text, target text, status, canonical metadata, and path-based `updateTranslation`,
|
|
100
|
-
`translateSingle`, and `translateAll` actions. Comparison row slots receive locale labels, read-only state,
|
|
101
|
-
question and option indexes, plus async single-translation actions.
|
|
100
|
+
`translateSingle`, and `translateAll` actions. Comparison row slots receive `nodeKind`, locale labels, read-only state,
|
|
101
|
+
field type, question and option indexes, metadata, plus async single-translation actions. Use
|
|
102
|
+
`createTranslationMetadata` to add provider/model/hash fields while the canonical metadata fields are retained.
|
|
102
103
|
|
|
103
104
|
Wrap a builder or renderer in `FormEngineI18nProvider` to supply a UI locale and typed Core translator independently
|
|
104
105
|
from the schema's `defaultLocale` and `supportedLocales`:
|
package/dist/index.cjs
CHANGED
|
@@ -3325,7 +3325,7 @@ function asAsyncAdapter(adapter) {
|
|
|
3325
3325
|
)
|
|
3326
3326
|
};
|
|
3327
3327
|
}
|
|
3328
|
-
var validateLocalePipeline = (locale, schema, policy, customValidator, availableLocales) => {
|
|
3328
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator, availableLocales, sourceLocale) => {
|
|
3329
3329
|
const canonicalLocale = (0, import_core5.normalizeLocale)(locale);
|
|
3330
3330
|
if (canonicalLocale === null) {
|
|
3331
3331
|
return {
|
|
@@ -3337,20 +3337,17 @@ var validateLocalePipeline = (locale, schema, policy, customValidator, available
|
|
|
3337
3337
|
};
|
|
3338
3338
|
}
|
|
3339
3339
|
const currentLocales = (schema.supportedLocales ?? []).map((candidate) => (0, import_core5.normalizeLocale)(candidate) ?? candidate);
|
|
3340
|
-
const defaultLocale =
|
|
3341
|
-
if (
|
|
3342
|
-
const value = typeof candidate === "string" ? candidate : candidate.locale;
|
|
3343
|
-
return (0, import_core5.normalizeLocale)(value) === canonicalLocale;
|
|
3344
|
-
})) {
|
|
3340
|
+
const defaultLocale = (0, import_core5.normalizeLocale)(sourceLocale ?? schema.defaultLocale ?? "") ?? sourceLocale ?? schema.defaultLocale ?? "";
|
|
3341
|
+
if (canonicalLocale === defaultLocale) {
|
|
3345
3342
|
return {
|
|
3346
3343
|
valid: false,
|
|
3347
3344
|
error: {
|
|
3348
|
-
type: "
|
|
3349
|
-
message: `Locale "${canonicalLocale}" is
|
|
3345
|
+
type: "source_locale",
|
|
3346
|
+
message: `Locale "${canonicalLocale}" is the source locale and cannot be added as a translation.`
|
|
3350
3347
|
}
|
|
3351
3348
|
};
|
|
3352
3349
|
}
|
|
3353
|
-
if (
|
|
3350
|
+
if (currentLocales.includes(canonicalLocale)) {
|
|
3354
3351
|
return {
|
|
3355
3352
|
valid: false,
|
|
3356
3353
|
error: {
|
|
@@ -3359,6 +3356,18 @@ var validateLocalePipeline = (locale, schema, policy, customValidator, available
|
|
|
3359
3356
|
}
|
|
3360
3357
|
};
|
|
3361
3358
|
}
|
|
3359
|
+
if (availableLocales !== void 0 && !availableLocales.some((candidate) => {
|
|
3360
|
+
const value = typeof candidate === "string" ? candidate : candidate.locale;
|
|
3361
|
+
return (0, import_core5.normalizeLocale)(value) === canonicalLocale;
|
|
3362
|
+
})) {
|
|
3363
|
+
return {
|
|
3364
|
+
valid: false,
|
|
3365
|
+
error: {
|
|
3366
|
+
type: "locale_not_allowed",
|
|
3367
|
+
message: `Locale "${canonicalLocale}" is not available in the locale catalog.`
|
|
3368
|
+
}
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3362
3371
|
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.some((candidate) => (0, import_core5.normalizeLocale)(candidate) === canonicalLocale)) {
|
|
3363
3372
|
return {
|
|
3364
3373
|
valid: false,
|
|
@@ -3412,6 +3421,7 @@ function workspaceLocaleValidationError(validation, currentLocales, requestedLoc
|
|
|
3412
3421
|
if (error.type === "locale_already_exists") {
|
|
3413
3422
|
return { type: "locale_already_exists", locale: requestedLocale };
|
|
3414
3423
|
}
|
|
3424
|
+
if (error.type === "source_locale") return { type: "source_locale", locale: requestedLocale };
|
|
3415
3425
|
if (error.type === "invalid_locale_format") {
|
|
3416
3426
|
return { type: "invalid_locale_format", locale: requestedLocale };
|
|
3417
3427
|
}
|
|
@@ -3429,19 +3439,29 @@ function translationMetadata(sourceText, sourceLocale, mode) {
|
|
|
3429
3439
|
...mode === "manual" ? { editedAt: (/* @__PURE__ */ new Date()).toISOString() } : { translatedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
3430
3440
|
};
|
|
3431
3441
|
}
|
|
3432
|
-
function
|
|
3442
|
+
function createTranslationMetadata(slot, sourceLocale, translatedText, mode, factory) {
|
|
3443
|
+
return {
|
|
3444
|
+
...factory?.({ slot, translatedText, mode }) ?? {},
|
|
3445
|
+
...translationMetadata(slot.sourceText, sourceLocale, mode)
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
function setNodeMetadata(node, slot, text, sourceLocale, mode, metadataOverride) {
|
|
3433
3449
|
const localeMetadata = node.translationMetadata?.[slot.locale];
|
|
3434
|
-
const nextMetadata = text.trim().length === 0 ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== slot.property)) : {
|
|
3450
|
+
const nextMetadata = text.trim().length === 0 ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== slot.property)) : {
|
|
3451
|
+
...localeMetadata,
|
|
3452
|
+
[slot.property]: metadataOverride ?? translationMetadata(slot.sourceText, sourceLocale, mode)
|
|
3453
|
+
};
|
|
3435
3454
|
return { ...node, translationMetadata: { ...node.translationMetadata, [slot.locale]: nextMetadata } };
|
|
3436
3455
|
}
|
|
3437
|
-
function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manual") {
|
|
3456
|
+
function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manual", metadataOverride) {
|
|
3438
3457
|
if (slot.kind === "form") {
|
|
3439
3458
|
return setNodeMetadata(
|
|
3440
3459
|
{ ...schema, translations: updateTranslationMap(schema.translations, slot.locale, slot.property, text) },
|
|
3441
3460
|
slot,
|
|
3442
3461
|
text,
|
|
3443
3462
|
sourceLocale,
|
|
3444
|
-
mode
|
|
3463
|
+
mode,
|
|
3464
|
+
metadataOverride
|
|
3445
3465
|
);
|
|
3446
3466
|
}
|
|
3447
3467
|
if (slot.kind === "field") {
|
|
@@ -3450,7 +3470,14 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3450
3470
|
fields: schema.fields.map((field) => {
|
|
3451
3471
|
if (field.id !== slot.nodeId) return field;
|
|
3452
3472
|
const translations = updateTranslationMap(field.translations, slot.locale, slot.property, text);
|
|
3453
|
-
return setNodeMetadata(
|
|
3473
|
+
return setNodeMetadata(
|
|
3474
|
+
{ ...field, translations },
|
|
3475
|
+
slot,
|
|
3476
|
+
text,
|
|
3477
|
+
sourceLocale,
|
|
3478
|
+
mode,
|
|
3479
|
+
metadataOverride
|
|
3480
|
+
);
|
|
3454
3481
|
})
|
|
3455
3482
|
};
|
|
3456
3483
|
}
|
|
@@ -3466,7 +3493,7 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3466
3493
|
const translations = text.trim().length === 0 ? Object.fromEntries(
|
|
3467
3494
|
Object.entries(option.translations ?? {}).filter(([locale]) => locale !== slot.locale)
|
|
3468
3495
|
) : { ...option.translations, [slot.locale]: text };
|
|
3469
|
-
return setNodeMetadata({ ...option, translations }, slot, text, sourceLocale, mode);
|
|
3496
|
+
return setNodeMetadata({ ...option, translations }, slot, text, sourceLocale, mode, metadataOverride);
|
|
3470
3497
|
})
|
|
3471
3498
|
};
|
|
3472
3499
|
})
|
|
@@ -3478,7 +3505,7 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3478
3505
|
pages: schema.pages.map((page) => {
|
|
3479
3506
|
if (page.id !== slot.nodeId) return page;
|
|
3480
3507
|
const translations = updateTranslationMap(page.translations, slot.locale, slot.property, text);
|
|
3481
|
-
return setNodeMetadata({ ...page, translations }, slot, text, sourceLocale, mode);
|
|
3508
|
+
return setNodeMetadata({ ...page, translations }, slot, text, sourceLocale, mode, metadataOverride);
|
|
3482
3509
|
})
|
|
3483
3510
|
}
|
|
3484
3511
|
};
|
|
@@ -3504,7 +3531,8 @@ function useTranslationWorkspace({
|
|
|
3504
3531
|
onTranslationReport,
|
|
3505
3532
|
onTranslationError,
|
|
3506
3533
|
onTranslationChange,
|
|
3507
|
-
validateLocale
|
|
3534
|
+
validateLocale,
|
|
3535
|
+
createTranslationMetadata: metadataFactory
|
|
3508
3536
|
}) {
|
|
3509
3537
|
const [draftSchema, setDraftSchema] = (0, import_react5.useState)(schema);
|
|
3510
3538
|
const [selectedLocale, setSelectedLocale] = (0, import_react5.useState)((0, import_core5.normalizeLocale)(targetLocale ?? "") ?? targetLocale ?? "");
|
|
@@ -3518,20 +3546,31 @@ function useTranslationWorkspace({
|
|
|
3518
3546
|
const confirmRemoveLocaleRenderer = confirmRemoveLocale ?? workspaceSlots?.confirmRemoveLocale;
|
|
3519
3547
|
const targetLocales = (0, import_react5.useMemo)(() => {
|
|
3520
3548
|
const locales = (0, import_core5.collectSchemaLocales)(currentSchema).allUniqueLocales;
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3549
|
+
const normalizedSourceLocale = (0, import_core5.normalizeLocale)(sourceLocale) ?? sourceLocale;
|
|
3550
|
+
return [
|
|
3551
|
+
...new Set(
|
|
3552
|
+
[...currentSchema.supportedLocales ?? [], ...locales].map((locale) => (0, import_core5.normalizeLocale)(locale) ?? locale)
|
|
3553
|
+
)
|
|
3554
|
+
].filter((locale) => locale !== normalizedSourceLocale);
|
|
3524
3555
|
}, [currentSchema, sourceLocale]);
|
|
3525
3556
|
const activeLocale = selectedLocale.length > 0 ? selectedLocale : targetLocales[0] ?? "";
|
|
3526
3557
|
const localeOptions = (0, import_react5.useMemo)(() => {
|
|
3527
3558
|
if (availableLocales === void 0) {
|
|
3528
3559
|
return targetLocales.map((locale) => ({ locale, label: locale }));
|
|
3529
3560
|
}
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3561
|
+
const targetLocaleSet = new Set(targetLocales);
|
|
3562
|
+
const allowedLocales = policy?.allowedLocales?.map((locale) => (0, import_core5.normalizeLocale)(locale) ?? locale);
|
|
3563
|
+
const options = /* @__PURE__ */ new Map();
|
|
3564
|
+
for (const candidate of availableLocales) {
|
|
3565
|
+
const option = typeof candidate === "string" ? { locale: (0, import_core5.normalizeLocale)(candidate) ?? candidate, label: candidate } : { ...candidate, locale: (0, import_core5.normalizeLocale)(candidate.locale) ?? candidate.locale };
|
|
3566
|
+
const allowed = allowedLocales === void 0 || allowedLocales.includes(option.locale);
|
|
3567
|
+
if (allowed || targetLocaleSet.has(option.locale)) options.set(option.locale, option);
|
|
3568
|
+
}
|
|
3569
|
+
for (const locale of targetLocales) {
|
|
3570
|
+
if (!options.has(locale)) options.set(locale, { locale, label: locale });
|
|
3571
|
+
}
|
|
3572
|
+
return [...options.values()];
|
|
3573
|
+
}, [availableLocales, policy?.allowedLocales, targetLocales]);
|
|
3535
3574
|
const slots = (0, import_react5.useMemo)(
|
|
3536
3575
|
() => activeLocale.length === 0 ? [] : (0, import_core5.collectTranslationSlots)(currentSchema, activeLocale),
|
|
3537
3576
|
[activeLocale, currentSchema]
|
|
@@ -3565,8 +3604,8 @@ function useTranslationWorkspace({
|
|
|
3565
3604
|
const setTranslation = (0, import_react5.useCallback)(
|
|
3566
3605
|
(slot, text) => {
|
|
3567
3606
|
if (readOnly) return;
|
|
3568
|
-
const metadata =
|
|
3569
|
-
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "manual"));
|
|
3607
|
+
const metadata = createTranslationMetadata(slot, sourceLocale, text, "manual", metadataFactory);
|
|
3608
|
+
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "manual", metadata));
|
|
3570
3609
|
onTranslationChange?.({
|
|
3571
3610
|
slot,
|
|
3572
3611
|
...slot.existingText === void 0 ? {} : { previousText: slot.existingText },
|
|
@@ -3575,7 +3614,7 @@ function useTranslationWorkspace({
|
|
|
3575
3614
|
metadata
|
|
3576
3615
|
});
|
|
3577
3616
|
},
|
|
3578
|
-
[commit, currentSchema, onTranslationChange, readOnly, sourceLocale]
|
|
3617
|
+
[commit, currentSchema, metadataFactory, onTranslationChange, readOnly, sourceLocale]
|
|
3579
3618
|
);
|
|
3580
3619
|
const addLocale = (0, import_react5.useCallback)(
|
|
3581
3620
|
(locale) => {
|
|
@@ -3587,14 +3626,21 @@ function useTranslationWorkspace({
|
|
|
3587
3626
|
const normalized = (0, import_core5.normalizeLocale)(locale);
|
|
3588
3627
|
if (normalized === null) {
|
|
3589
3628
|
const workspaceError = workspaceLocaleValidationError(
|
|
3590
|
-
validateLocalePipeline(locale, currentSchema, policy, validateLocale, availableLocales),
|
|
3629
|
+
validateLocalePipeline(locale, currentSchema, policy, validateLocale, availableLocales, sourceLocale),
|
|
3591
3630
|
(0, import_core5.collectSchemaLocales)(currentSchema).allUniqueLocales.size,
|
|
3592
3631
|
locale
|
|
3593
3632
|
);
|
|
3594
3633
|
setError(workspaceError);
|
|
3595
3634
|
return { success: false, error: workspaceError };
|
|
3596
3635
|
}
|
|
3597
|
-
const validation = validateLocalePipeline(
|
|
3636
|
+
const validation = validateLocalePipeline(
|
|
3637
|
+
normalized,
|
|
3638
|
+
currentSchema,
|
|
3639
|
+
policy,
|
|
3640
|
+
validateLocale,
|
|
3641
|
+
availableLocales,
|
|
3642
|
+
sourceLocale
|
|
3643
|
+
);
|
|
3598
3644
|
if (!validation.valid) {
|
|
3599
3645
|
const workspaceError = workspaceLocaleValidationError(
|
|
3600
3646
|
validation,
|
|
@@ -3613,16 +3659,16 @@ function useTranslationWorkspace({
|
|
|
3613
3659
|
onLocaleAdded?.(normalized);
|
|
3614
3660
|
return { success: true };
|
|
3615
3661
|
},
|
|
3616
|
-
[availableLocales, commit, currentSchema, onLocaleAdded, policy, readOnly, validateLocale]
|
|
3662
|
+
[availableLocales, commit, currentSchema, onLocaleAdded, policy, readOnly, sourceLocale, validateLocale]
|
|
3617
3663
|
);
|
|
3618
3664
|
const isAddLocaleAllowed = (0, import_react5.useCallback)(
|
|
3619
3665
|
(locale) => {
|
|
3620
3666
|
if (readOnly) return false;
|
|
3621
3667
|
const normalized = (0, import_core5.normalizeLocale)(locale);
|
|
3622
3668
|
if (normalized === null) return false;
|
|
3623
|
-
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale, availableLocales).valid;
|
|
3669
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale, availableLocales, sourceLocale).valid;
|
|
3624
3670
|
},
|
|
3625
|
-
[availableLocales, currentSchema, policy, readOnly, validateLocale]
|
|
3671
|
+
[availableLocales, currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3626
3672
|
);
|
|
3627
3673
|
const removeLocale = (0, import_react5.useCallback)(
|
|
3628
3674
|
(locale) => {
|
|
@@ -3772,6 +3818,7 @@ function useTranslationWorkspace({
|
|
|
3772
3818
|
...options,
|
|
3773
3819
|
continueOnError: true,
|
|
3774
3820
|
signal: controller.signal,
|
|
3821
|
+
createMetadata: (slot, translatedText) => options.createMetadata?.(slot, translatedText) ?? createTranslationMetadata(slot, sourceLocale, translatedText, "automatic", metadataFactory),
|
|
3775
3822
|
onProgress: (nextProgress) => {
|
|
3776
3823
|
setProgress(nextProgress);
|
|
3777
3824
|
options.onProgress?.(nextProgress);
|
|
@@ -3844,6 +3891,7 @@ function useTranslationWorkspace({
|
|
|
3844
3891
|
readOnly,
|
|
3845
3892
|
slots,
|
|
3846
3893
|
sourceLocale,
|
|
3894
|
+
metadataFactory,
|
|
3847
3895
|
translationAdapter,
|
|
3848
3896
|
signal
|
|
3849
3897
|
]
|
|
@@ -3880,8 +3928,8 @@ function useTranslationWorkspace({
|
|
|
3880
3928
|
sourceLocale,
|
|
3881
3929
|
controller.signal
|
|
3882
3930
|
);
|
|
3883
|
-
const metadata =
|
|
3884
|
-
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic"));
|
|
3931
|
+
const metadata = createTranslationMetadata(slot, sourceLocale, text, "automatic", metadataFactory);
|
|
3932
|
+
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic", metadata));
|
|
3885
3933
|
onTranslationChange?.({
|
|
3886
3934
|
slot,
|
|
3887
3935
|
...slot.existingText === void 0 ? {} : { previousText: slot.existingText },
|
|
@@ -3904,6 +3952,7 @@ function useTranslationWorkspace({
|
|
|
3904
3952
|
cause
|
|
3905
3953
|
};
|
|
3906
3954
|
setError(workspaceError);
|
|
3955
|
+
onTranslationError?.({ targetLocale: slot.locale, error: workspaceError });
|
|
3907
3956
|
return { success: false, error: workspaceError };
|
|
3908
3957
|
} finally {
|
|
3909
3958
|
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
@@ -3914,7 +3963,17 @@ function useTranslationWorkspace({
|
|
|
3914
3963
|
setIsTranslating(false);
|
|
3915
3964
|
}
|
|
3916
3965
|
},
|
|
3917
|
-
[
|
|
3966
|
+
[
|
|
3967
|
+
commit,
|
|
3968
|
+
currentSchema,
|
|
3969
|
+
metadataFactory,
|
|
3970
|
+
onTranslationChange,
|
|
3971
|
+
onTranslationError,
|
|
3972
|
+
readOnly,
|
|
3973
|
+
signal,
|
|
3974
|
+
sourceLocale,
|
|
3975
|
+
translationAdapter
|
|
3976
|
+
]
|
|
3918
3977
|
);
|
|
3919
3978
|
return {
|
|
3920
3979
|
sourceLocale,
|
|
@@ -3969,6 +4028,7 @@ function optionParentId(slot) {
|
|
|
3969
4028
|
return /^fields\.([^.]+)\.options\./u.exec(slot.path)?.[1];
|
|
3970
4029
|
}
|
|
3971
4030
|
function getNodeTitle(schema, slot) {
|
|
4031
|
+
if (slot.kind === "form") return schema.title;
|
|
3972
4032
|
if (slot.kind === "field") return schema.fields.find((field) => field.id === slot.nodeId)?.title;
|
|
3973
4033
|
if (slot.kind === "page") return schema.pages?.find((page) => page.id === slot.nodeId)?.title;
|
|
3974
4034
|
if (slot.kind === "option") return schema.fields.find((field) => field.id === optionParentId(slot))?.title;
|
|
@@ -3986,6 +4046,7 @@ function comparisonItem(schema, slot, sourceLocale, hasAdapter) {
|
|
|
3986
4046
|
return {
|
|
3987
4047
|
id: path,
|
|
3988
4048
|
path,
|
|
4049
|
+
nodeId: slot.nodeId,
|
|
3989
4050
|
targetKind: slot.kind,
|
|
3990
4051
|
targetProperty: slot.property,
|
|
3991
4052
|
...nodeTitle === void 0 ? {} : { nodeTitle },
|
|
@@ -4019,23 +4080,45 @@ function useTranslationComparison({
|
|
|
4019
4080
|
targetLocale,
|
|
4020
4081
|
translationAdapter,
|
|
4021
4082
|
readOnly = false,
|
|
4083
|
+
availableLocales,
|
|
4084
|
+
policy,
|
|
4022
4085
|
onChange,
|
|
4023
4086
|
onTranslationChange,
|
|
4024
4087
|
onTranslationReport,
|
|
4025
4088
|
onTranslationError,
|
|
4026
|
-
signal
|
|
4089
|
+
signal,
|
|
4090
|
+
onLocaleAdded,
|
|
4091
|
+
onLocaleRemoved,
|
|
4092
|
+
onLocaleChange,
|
|
4093
|
+
beforeRemoveLocale,
|
|
4094
|
+
confirmRemoveLocale,
|
|
4095
|
+
onTranslationStart,
|
|
4096
|
+
onTranslationSuccess,
|
|
4097
|
+
validateLocale,
|
|
4098
|
+
createTranslationMetadata: createTranslationMetadata2
|
|
4027
4099
|
}) {
|
|
4028
4100
|
const workspace = useTranslationWorkspace({
|
|
4029
4101
|
schema,
|
|
4030
4102
|
sourceLocale,
|
|
4031
4103
|
targetLocale,
|
|
4104
|
+
...availableLocales === void 0 ? {} : { availableLocales },
|
|
4105
|
+
...policy === void 0 ? {} : { policy },
|
|
4032
4106
|
...translationAdapter === void 0 ? {} : { translationAdapter },
|
|
4033
4107
|
readOnly,
|
|
4034
4108
|
...onChange === void 0 ? {} : { onChange },
|
|
4035
4109
|
...onTranslationChange === void 0 ? {} : { onTranslationChange },
|
|
4036
4110
|
...onTranslationReport === void 0 ? {} : { onTranslationReport },
|
|
4037
4111
|
...onTranslationError === void 0 ? {} : { onTranslationError },
|
|
4038
|
-
...signal === void 0 ? {} : { signal }
|
|
4112
|
+
...signal === void 0 ? {} : { signal },
|
|
4113
|
+
...onLocaleAdded === void 0 ? {} : { onLocaleAdded },
|
|
4114
|
+
...onLocaleRemoved === void 0 ? {} : { onLocaleRemoved },
|
|
4115
|
+
...onLocaleChange === void 0 ? {} : { onLocaleChange },
|
|
4116
|
+
...beforeRemoveLocale === void 0 ? {} : { beforeRemoveLocale },
|
|
4117
|
+
...confirmRemoveLocale === void 0 ? {} : { confirmRemoveLocale },
|
|
4118
|
+
...onTranslationStart === void 0 ? {} : { onTranslationStart },
|
|
4119
|
+
...onTranslationSuccess === void 0 ? {} : { onTranslationSuccess },
|
|
4120
|
+
...validateLocale === void 0 ? {} : { validateLocale },
|
|
4121
|
+
...createTranslationMetadata2 === void 0 ? {} : { createTranslationMetadata: createTranslationMetadata2 }
|
|
4039
4122
|
});
|
|
4040
4123
|
const [report, setReport] = (0, import_react6.useState)();
|
|
4041
4124
|
const items = (0, import_react6.useMemo)(
|
|
@@ -4073,6 +4156,13 @@ function useTranslationComparison({
|
|
|
4073
4156
|
return {
|
|
4074
4157
|
sourceLocale: workspace.sourceLocale,
|
|
4075
4158
|
targetLocale: workspace.targetLocale,
|
|
4159
|
+
targetLocales: workspace.targetLocales,
|
|
4160
|
+
localeOptions: workspace.localeOptions,
|
|
4161
|
+
setTargetLocale: workspace.setTargetLocale,
|
|
4162
|
+
addLocale: workspace.addLocale,
|
|
4163
|
+
isAddLocaleAllowed: workspace.isAddLocaleAllowed,
|
|
4164
|
+
removeLocale: workspace.removeLocale,
|
|
4165
|
+
...workspace.removeLocaleConfirmation === void 0 ? {} : { removeLocaleConfirmation: workspace.removeLocaleConfirmation },
|
|
4076
4166
|
items,
|
|
4077
4167
|
summary: summaryFor(items),
|
|
4078
4168
|
isTranslating: workspace.isTranslating,
|
package/dist/index.d.cts
CHANGED
|
@@ -154,6 +154,11 @@ interface UseTranslationWorkspaceOptions {
|
|
|
154
154
|
readonly error: TranslationWorkspaceError;
|
|
155
155
|
}) => void;
|
|
156
156
|
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
157
|
+
readonly createTranslationMetadata?: (params: {
|
|
158
|
+
readonly slot: TranslationSlot;
|
|
159
|
+
readonly translatedText: string;
|
|
160
|
+
readonly mode: "manual" | "automatic";
|
|
161
|
+
}) => Readonly<Record<string, _form_engine_ts_core.JsonValue>>;
|
|
157
162
|
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
158
163
|
}
|
|
159
164
|
interface LocaleValidationContext {
|
|
@@ -166,7 +171,7 @@ type CustomLocaleValidator = (locale: string, context: LocaleValidationContext)
|
|
|
166
171
|
interface LocaleValidationResult {
|
|
167
172
|
readonly valid: boolean;
|
|
168
173
|
readonly error?: {
|
|
169
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
174
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "source_locale" | "custom_validation_failed";
|
|
170
175
|
readonly message: string;
|
|
171
176
|
};
|
|
172
177
|
}
|
|
@@ -184,6 +189,9 @@ type TranslationWorkspaceError = {
|
|
|
184
189
|
} | {
|
|
185
190
|
readonly type: "locale_already_exists";
|
|
186
191
|
readonly locale: string;
|
|
192
|
+
} | {
|
|
193
|
+
readonly type: "source_locale";
|
|
194
|
+
readonly locale: string;
|
|
187
195
|
} | {
|
|
188
196
|
readonly type: "invalid_locale_format";
|
|
189
197
|
readonly locale: string;
|
|
@@ -243,8 +251,8 @@ interface UseTranslationWorkspaceResult {
|
|
|
243
251
|
readonly progress?: TranslationProgress;
|
|
244
252
|
readonly error?: TranslationWorkspaceError;
|
|
245
253
|
}
|
|
246
|
-
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[]) => LocaleValidationResult;
|
|
247
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, signal, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationReport, onTranslationError, onTranslationChange, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
254
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[], sourceLocale?: string) => LocaleValidationResult;
|
|
255
|
+
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, signal, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationReport, onTranslationError, onTranslationChange, validateLocale, createTranslationMetadata: metadataFactory }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
248
256
|
|
|
249
257
|
interface SubmissionReceipt {
|
|
250
258
|
readonly formId: string;
|
|
@@ -529,6 +537,7 @@ interface TranslationSlotRowProps {
|
|
|
529
537
|
interface TranslationComparisonItem {
|
|
530
538
|
readonly id: string;
|
|
531
539
|
readonly path: string;
|
|
540
|
+
readonly nodeId?: string;
|
|
532
541
|
readonly targetKind: "form" | "page" | "field" | "option";
|
|
533
542
|
readonly targetProperty: "title" | "description" | "label" | "completionMessage";
|
|
534
543
|
readonly nodeTitle?: string;
|
|
@@ -558,6 +567,7 @@ interface TranslationComparisonHeaderProps {
|
|
|
558
567
|
}
|
|
559
568
|
interface TranslationComparisonItemRowProps {
|
|
560
569
|
readonly item: TranslationComparisonItem;
|
|
570
|
+
readonly nodeKind?: "form" | "page" | "field" | "option";
|
|
561
571
|
readonly questionIndex?: number;
|
|
562
572
|
readonly fieldType?: string;
|
|
563
573
|
readonly optionIndex?: number;
|
|
@@ -571,6 +581,8 @@ interface UseTranslationComparisonOptions {
|
|
|
571
581
|
readonly schema: FormSchema;
|
|
572
582
|
readonly sourceLocale?: string;
|
|
573
583
|
readonly targetLocale: string;
|
|
584
|
+
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
585
|
+
readonly policy?: FormPolicy;
|
|
574
586
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
575
587
|
readonly signal?: AbortSignal;
|
|
576
588
|
readonly readOnly?: boolean;
|
|
@@ -581,10 +593,31 @@ interface UseTranslationComparisonOptions {
|
|
|
581
593
|
readonly targetLocale: string;
|
|
582
594
|
readonly error: TranslationWorkspaceError;
|
|
583
595
|
}) => void;
|
|
596
|
+
readonly onLocaleAdded?: (locale: string) => void;
|
|
597
|
+
readonly onLocaleRemoved?: (locale: string) => void;
|
|
598
|
+
readonly onLocaleChange?: (locale: string) => void;
|
|
599
|
+
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
600
|
+
readonly slotCount: number;
|
|
601
|
+
}) => Promise<boolean> | boolean;
|
|
602
|
+
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
603
|
+
readonly onTranslationStart?: (params: {
|
|
604
|
+
readonly targetLocale: string;
|
|
605
|
+
readonly mode: "manual" | "automatic";
|
|
606
|
+
}) => void;
|
|
607
|
+
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
608
|
+
readonly validateLocale?: UseTranslationWorkspaceOptions["validateLocale"];
|
|
609
|
+
readonly createTranslationMetadata?: UseTranslationWorkspaceOptions["createTranslationMetadata"];
|
|
584
610
|
}
|
|
585
611
|
interface UseTranslationComparisonResult {
|
|
586
612
|
readonly sourceLocale: string;
|
|
587
613
|
readonly targetLocale: string;
|
|
614
|
+
readonly targetLocales: readonly string[];
|
|
615
|
+
readonly localeOptions: readonly LocaleOption[];
|
|
616
|
+
readonly setTargetLocale: (locale: string) => void;
|
|
617
|
+
readonly addLocale: UseTranslationWorkspaceResult["addLocale"];
|
|
618
|
+
readonly isAddLocaleAllowed: UseTranslationWorkspaceResult["isAddLocaleAllowed"];
|
|
619
|
+
readonly removeLocale: UseTranslationWorkspaceResult["removeLocale"];
|
|
620
|
+
readonly removeLocaleConfirmation?: ReactNode;
|
|
588
621
|
readonly items: readonly TranslationComparisonItem[];
|
|
589
622
|
readonly summary: TranslationComparisonSummary;
|
|
590
623
|
readonly isTranslating: boolean;
|
|
@@ -962,7 +995,7 @@ interface FieldState {
|
|
|
962
995
|
}
|
|
963
996
|
declare function useField(fieldId: string): FieldState;
|
|
964
997
|
|
|
965
|
-
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
998
|
+
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, availableLocales, policy, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, onTranslationStart, onTranslationSuccess, validateLocale, createTranslationMetadata }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
966
999
|
|
|
967
1000
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
968
1001
|
readonly ADD_FIELD: "builder.actions.addField";
|
package/dist/index.d.ts
CHANGED
|
@@ -154,6 +154,11 @@ interface UseTranslationWorkspaceOptions {
|
|
|
154
154
|
readonly error: TranslationWorkspaceError;
|
|
155
155
|
}) => void;
|
|
156
156
|
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
157
|
+
readonly createTranslationMetadata?: (params: {
|
|
158
|
+
readonly slot: TranslationSlot;
|
|
159
|
+
readonly translatedText: string;
|
|
160
|
+
readonly mode: "manual" | "automatic";
|
|
161
|
+
}) => Readonly<Record<string, _form_engine_ts_core.JsonValue>>;
|
|
157
162
|
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
158
163
|
}
|
|
159
164
|
interface LocaleValidationContext {
|
|
@@ -166,7 +171,7 @@ type CustomLocaleValidator = (locale: string, context: LocaleValidationContext)
|
|
|
166
171
|
interface LocaleValidationResult {
|
|
167
172
|
readonly valid: boolean;
|
|
168
173
|
readonly error?: {
|
|
169
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
174
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "source_locale" | "custom_validation_failed";
|
|
170
175
|
readonly message: string;
|
|
171
176
|
};
|
|
172
177
|
}
|
|
@@ -184,6 +189,9 @@ type TranslationWorkspaceError = {
|
|
|
184
189
|
} | {
|
|
185
190
|
readonly type: "locale_already_exists";
|
|
186
191
|
readonly locale: string;
|
|
192
|
+
} | {
|
|
193
|
+
readonly type: "source_locale";
|
|
194
|
+
readonly locale: string;
|
|
187
195
|
} | {
|
|
188
196
|
readonly type: "invalid_locale_format";
|
|
189
197
|
readonly locale: string;
|
|
@@ -243,8 +251,8 @@ interface UseTranslationWorkspaceResult {
|
|
|
243
251
|
readonly progress?: TranslationProgress;
|
|
244
252
|
readonly error?: TranslationWorkspaceError;
|
|
245
253
|
}
|
|
246
|
-
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[]) => LocaleValidationResult;
|
|
247
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, signal, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationReport, onTranslationError, onTranslationChange, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
254
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[], sourceLocale?: string) => LocaleValidationResult;
|
|
255
|
+
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, signal, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationReport, onTranslationError, onTranslationChange, validateLocale, createTranslationMetadata: metadataFactory }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
248
256
|
|
|
249
257
|
interface SubmissionReceipt {
|
|
250
258
|
readonly formId: string;
|
|
@@ -529,6 +537,7 @@ interface TranslationSlotRowProps {
|
|
|
529
537
|
interface TranslationComparisonItem {
|
|
530
538
|
readonly id: string;
|
|
531
539
|
readonly path: string;
|
|
540
|
+
readonly nodeId?: string;
|
|
532
541
|
readonly targetKind: "form" | "page" | "field" | "option";
|
|
533
542
|
readonly targetProperty: "title" | "description" | "label" | "completionMessage";
|
|
534
543
|
readonly nodeTitle?: string;
|
|
@@ -558,6 +567,7 @@ interface TranslationComparisonHeaderProps {
|
|
|
558
567
|
}
|
|
559
568
|
interface TranslationComparisonItemRowProps {
|
|
560
569
|
readonly item: TranslationComparisonItem;
|
|
570
|
+
readonly nodeKind?: "form" | "page" | "field" | "option";
|
|
561
571
|
readonly questionIndex?: number;
|
|
562
572
|
readonly fieldType?: string;
|
|
563
573
|
readonly optionIndex?: number;
|
|
@@ -571,6 +581,8 @@ interface UseTranslationComparisonOptions {
|
|
|
571
581
|
readonly schema: FormSchema;
|
|
572
582
|
readonly sourceLocale?: string;
|
|
573
583
|
readonly targetLocale: string;
|
|
584
|
+
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
585
|
+
readonly policy?: FormPolicy;
|
|
574
586
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
575
587
|
readonly signal?: AbortSignal;
|
|
576
588
|
readonly readOnly?: boolean;
|
|
@@ -581,10 +593,31 @@ interface UseTranslationComparisonOptions {
|
|
|
581
593
|
readonly targetLocale: string;
|
|
582
594
|
readonly error: TranslationWorkspaceError;
|
|
583
595
|
}) => void;
|
|
596
|
+
readonly onLocaleAdded?: (locale: string) => void;
|
|
597
|
+
readonly onLocaleRemoved?: (locale: string) => void;
|
|
598
|
+
readonly onLocaleChange?: (locale: string) => void;
|
|
599
|
+
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
600
|
+
readonly slotCount: number;
|
|
601
|
+
}) => Promise<boolean> | boolean;
|
|
602
|
+
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
603
|
+
readonly onTranslationStart?: (params: {
|
|
604
|
+
readonly targetLocale: string;
|
|
605
|
+
readonly mode: "manual" | "automatic";
|
|
606
|
+
}) => void;
|
|
607
|
+
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
608
|
+
readonly validateLocale?: UseTranslationWorkspaceOptions["validateLocale"];
|
|
609
|
+
readonly createTranslationMetadata?: UseTranslationWorkspaceOptions["createTranslationMetadata"];
|
|
584
610
|
}
|
|
585
611
|
interface UseTranslationComparisonResult {
|
|
586
612
|
readonly sourceLocale: string;
|
|
587
613
|
readonly targetLocale: string;
|
|
614
|
+
readonly targetLocales: readonly string[];
|
|
615
|
+
readonly localeOptions: readonly LocaleOption[];
|
|
616
|
+
readonly setTargetLocale: (locale: string) => void;
|
|
617
|
+
readonly addLocale: UseTranslationWorkspaceResult["addLocale"];
|
|
618
|
+
readonly isAddLocaleAllowed: UseTranslationWorkspaceResult["isAddLocaleAllowed"];
|
|
619
|
+
readonly removeLocale: UseTranslationWorkspaceResult["removeLocale"];
|
|
620
|
+
readonly removeLocaleConfirmation?: ReactNode;
|
|
588
621
|
readonly items: readonly TranslationComparisonItem[];
|
|
589
622
|
readonly summary: TranslationComparisonSummary;
|
|
590
623
|
readonly isTranslating: boolean;
|
|
@@ -962,7 +995,7 @@ interface FieldState {
|
|
|
962
995
|
}
|
|
963
996
|
declare function useField(fieldId: string): FieldState;
|
|
964
997
|
|
|
965
|
-
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
998
|
+
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, availableLocales, policy, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, onTranslationStart, onTranslationSuccess, validateLocale, createTranslationMetadata }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
966
999
|
|
|
967
1000
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
968
1001
|
readonly ADD_FIELD: "builder.actions.addField";
|
package/dist/index.js
CHANGED
|
@@ -3301,7 +3301,7 @@ function asAsyncAdapter(adapter) {
|
|
|
3301
3301
|
)
|
|
3302
3302
|
};
|
|
3303
3303
|
}
|
|
3304
|
-
var validateLocalePipeline = (locale, schema, policy, customValidator, availableLocales) => {
|
|
3304
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator, availableLocales, sourceLocale) => {
|
|
3305
3305
|
const canonicalLocale = normalizeLocale(locale);
|
|
3306
3306
|
if (canonicalLocale === null) {
|
|
3307
3307
|
return {
|
|
@@ -3313,20 +3313,17 @@ var validateLocalePipeline = (locale, schema, policy, customValidator, available
|
|
|
3313
3313
|
};
|
|
3314
3314
|
}
|
|
3315
3315
|
const currentLocales = (schema.supportedLocales ?? []).map((candidate) => normalizeLocale(candidate) ?? candidate);
|
|
3316
|
-
const defaultLocale = schema.defaultLocale
|
|
3317
|
-
if (
|
|
3318
|
-
const value = typeof candidate === "string" ? candidate : candidate.locale;
|
|
3319
|
-
return normalizeLocale(value) === canonicalLocale;
|
|
3320
|
-
})) {
|
|
3316
|
+
const defaultLocale = normalizeLocale(sourceLocale ?? schema.defaultLocale ?? "") ?? sourceLocale ?? schema.defaultLocale ?? "";
|
|
3317
|
+
if (canonicalLocale === defaultLocale) {
|
|
3321
3318
|
return {
|
|
3322
3319
|
valid: false,
|
|
3323
3320
|
error: {
|
|
3324
|
-
type: "
|
|
3325
|
-
message: `Locale "${canonicalLocale}" is
|
|
3321
|
+
type: "source_locale",
|
|
3322
|
+
message: `Locale "${canonicalLocale}" is the source locale and cannot be added as a translation.`
|
|
3326
3323
|
}
|
|
3327
3324
|
};
|
|
3328
3325
|
}
|
|
3329
|
-
if (
|
|
3326
|
+
if (currentLocales.includes(canonicalLocale)) {
|
|
3330
3327
|
return {
|
|
3331
3328
|
valid: false,
|
|
3332
3329
|
error: {
|
|
@@ -3335,6 +3332,18 @@ var validateLocalePipeline = (locale, schema, policy, customValidator, available
|
|
|
3335
3332
|
}
|
|
3336
3333
|
};
|
|
3337
3334
|
}
|
|
3335
|
+
if (availableLocales !== void 0 && !availableLocales.some((candidate) => {
|
|
3336
|
+
const value = typeof candidate === "string" ? candidate : candidate.locale;
|
|
3337
|
+
return normalizeLocale(value) === canonicalLocale;
|
|
3338
|
+
})) {
|
|
3339
|
+
return {
|
|
3340
|
+
valid: false,
|
|
3341
|
+
error: {
|
|
3342
|
+
type: "locale_not_allowed",
|
|
3343
|
+
message: `Locale "${canonicalLocale}" is not available in the locale catalog.`
|
|
3344
|
+
}
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3338
3347
|
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.some((candidate) => normalizeLocale(candidate) === canonicalLocale)) {
|
|
3339
3348
|
return {
|
|
3340
3349
|
valid: false,
|
|
@@ -3388,6 +3397,7 @@ function workspaceLocaleValidationError(validation, currentLocales, requestedLoc
|
|
|
3388
3397
|
if (error.type === "locale_already_exists") {
|
|
3389
3398
|
return { type: "locale_already_exists", locale: requestedLocale };
|
|
3390
3399
|
}
|
|
3400
|
+
if (error.type === "source_locale") return { type: "source_locale", locale: requestedLocale };
|
|
3391
3401
|
if (error.type === "invalid_locale_format") {
|
|
3392
3402
|
return { type: "invalid_locale_format", locale: requestedLocale };
|
|
3393
3403
|
}
|
|
@@ -3405,19 +3415,29 @@ function translationMetadata(sourceText, sourceLocale, mode) {
|
|
|
3405
3415
|
...mode === "manual" ? { editedAt: (/* @__PURE__ */ new Date()).toISOString() } : { translatedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
3406
3416
|
};
|
|
3407
3417
|
}
|
|
3408
|
-
function
|
|
3418
|
+
function createTranslationMetadata(slot, sourceLocale, translatedText, mode, factory) {
|
|
3419
|
+
return {
|
|
3420
|
+
...factory?.({ slot, translatedText, mode }) ?? {},
|
|
3421
|
+
...translationMetadata(slot.sourceText, sourceLocale, mode)
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
function setNodeMetadata(node, slot, text, sourceLocale, mode, metadataOverride) {
|
|
3409
3425
|
const localeMetadata = node.translationMetadata?.[slot.locale];
|
|
3410
|
-
const nextMetadata = text.trim().length === 0 ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== slot.property)) : {
|
|
3426
|
+
const nextMetadata = text.trim().length === 0 ? Object.fromEntries(Object.entries(localeMetadata ?? {}).filter(([key]) => key !== slot.property)) : {
|
|
3427
|
+
...localeMetadata,
|
|
3428
|
+
[slot.property]: metadataOverride ?? translationMetadata(slot.sourceText, sourceLocale, mode)
|
|
3429
|
+
};
|
|
3411
3430
|
return { ...node, translationMetadata: { ...node.translationMetadata, [slot.locale]: nextMetadata } };
|
|
3412
3431
|
}
|
|
3413
|
-
function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manual") {
|
|
3432
|
+
function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manual", metadataOverride) {
|
|
3414
3433
|
if (slot.kind === "form") {
|
|
3415
3434
|
return setNodeMetadata(
|
|
3416
3435
|
{ ...schema, translations: updateTranslationMap(schema.translations, slot.locale, slot.property, text) },
|
|
3417
3436
|
slot,
|
|
3418
3437
|
text,
|
|
3419
3438
|
sourceLocale,
|
|
3420
|
-
mode
|
|
3439
|
+
mode,
|
|
3440
|
+
metadataOverride
|
|
3421
3441
|
);
|
|
3422
3442
|
}
|
|
3423
3443
|
if (slot.kind === "field") {
|
|
@@ -3426,7 +3446,14 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3426
3446
|
fields: schema.fields.map((field) => {
|
|
3427
3447
|
if (field.id !== slot.nodeId) return field;
|
|
3428
3448
|
const translations = updateTranslationMap(field.translations, slot.locale, slot.property, text);
|
|
3429
|
-
return setNodeMetadata(
|
|
3449
|
+
return setNodeMetadata(
|
|
3450
|
+
{ ...field, translations },
|
|
3451
|
+
slot,
|
|
3452
|
+
text,
|
|
3453
|
+
sourceLocale,
|
|
3454
|
+
mode,
|
|
3455
|
+
metadataOverride
|
|
3456
|
+
);
|
|
3430
3457
|
})
|
|
3431
3458
|
};
|
|
3432
3459
|
}
|
|
@@ -3442,7 +3469,7 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3442
3469
|
const translations = text.trim().length === 0 ? Object.fromEntries(
|
|
3443
3470
|
Object.entries(option.translations ?? {}).filter(([locale]) => locale !== slot.locale)
|
|
3444
3471
|
) : { ...option.translations, [slot.locale]: text };
|
|
3445
|
-
return setNodeMetadata({ ...option, translations }, slot, text, sourceLocale, mode);
|
|
3472
|
+
return setNodeMetadata({ ...option, translations }, slot, text, sourceLocale, mode, metadataOverride);
|
|
3446
3473
|
})
|
|
3447
3474
|
};
|
|
3448
3475
|
})
|
|
@@ -3454,7 +3481,7 @@ function updateSchemaTranslation(schema, slot, text, sourceLocale, mode = "manua
|
|
|
3454
3481
|
pages: schema.pages.map((page) => {
|
|
3455
3482
|
if (page.id !== slot.nodeId) return page;
|
|
3456
3483
|
const translations = updateTranslationMap(page.translations, slot.locale, slot.property, text);
|
|
3457
|
-
return setNodeMetadata({ ...page, translations }, slot, text, sourceLocale, mode);
|
|
3484
|
+
return setNodeMetadata({ ...page, translations }, slot, text, sourceLocale, mode, metadataOverride);
|
|
3458
3485
|
})
|
|
3459
3486
|
}
|
|
3460
3487
|
};
|
|
@@ -3480,7 +3507,8 @@ function useTranslationWorkspace({
|
|
|
3480
3507
|
onTranslationReport,
|
|
3481
3508
|
onTranslationError,
|
|
3482
3509
|
onTranslationChange,
|
|
3483
|
-
validateLocale
|
|
3510
|
+
validateLocale,
|
|
3511
|
+
createTranslationMetadata: metadataFactory
|
|
3484
3512
|
}) {
|
|
3485
3513
|
const [draftSchema, setDraftSchema] = useState4(schema);
|
|
3486
3514
|
const [selectedLocale, setSelectedLocale] = useState4(normalizeLocale(targetLocale ?? "") ?? targetLocale ?? "");
|
|
@@ -3494,20 +3522,31 @@ function useTranslationWorkspace({
|
|
|
3494
3522
|
const confirmRemoveLocaleRenderer = confirmRemoveLocale ?? workspaceSlots?.confirmRemoveLocale;
|
|
3495
3523
|
const targetLocales = useMemo4(() => {
|
|
3496
3524
|
const locales = collectSchemaLocales2(currentSchema).allUniqueLocales;
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3525
|
+
const normalizedSourceLocale = normalizeLocale(sourceLocale) ?? sourceLocale;
|
|
3526
|
+
return [
|
|
3527
|
+
...new Set(
|
|
3528
|
+
[...currentSchema.supportedLocales ?? [], ...locales].map((locale) => normalizeLocale(locale) ?? locale)
|
|
3529
|
+
)
|
|
3530
|
+
].filter((locale) => locale !== normalizedSourceLocale);
|
|
3500
3531
|
}, [currentSchema, sourceLocale]);
|
|
3501
3532
|
const activeLocale = selectedLocale.length > 0 ? selectedLocale : targetLocales[0] ?? "";
|
|
3502
3533
|
const localeOptions = useMemo4(() => {
|
|
3503
3534
|
if (availableLocales === void 0) {
|
|
3504
3535
|
return targetLocales.map((locale) => ({ locale, label: locale }));
|
|
3505
3536
|
}
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3537
|
+
const targetLocaleSet = new Set(targetLocales);
|
|
3538
|
+
const allowedLocales = policy?.allowedLocales?.map((locale) => normalizeLocale(locale) ?? locale);
|
|
3539
|
+
const options = /* @__PURE__ */ new Map();
|
|
3540
|
+
for (const candidate of availableLocales) {
|
|
3541
|
+
const option = typeof candidate === "string" ? { locale: normalizeLocale(candidate) ?? candidate, label: candidate } : { ...candidate, locale: normalizeLocale(candidate.locale) ?? candidate.locale };
|
|
3542
|
+
const allowed = allowedLocales === void 0 || allowedLocales.includes(option.locale);
|
|
3543
|
+
if (allowed || targetLocaleSet.has(option.locale)) options.set(option.locale, option);
|
|
3544
|
+
}
|
|
3545
|
+
for (const locale of targetLocales) {
|
|
3546
|
+
if (!options.has(locale)) options.set(locale, { locale, label: locale });
|
|
3547
|
+
}
|
|
3548
|
+
return [...options.values()];
|
|
3549
|
+
}, [availableLocales, policy?.allowedLocales, targetLocales]);
|
|
3511
3550
|
const slots = useMemo4(
|
|
3512
3551
|
() => activeLocale.length === 0 ? [] : collectTranslationSlots(currentSchema, activeLocale),
|
|
3513
3552
|
[activeLocale, currentSchema]
|
|
@@ -3541,8 +3580,8 @@ function useTranslationWorkspace({
|
|
|
3541
3580
|
const setTranslation = useCallback3(
|
|
3542
3581
|
(slot, text) => {
|
|
3543
3582
|
if (readOnly) return;
|
|
3544
|
-
const metadata =
|
|
3545
|
-
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "manual"));
|
|
3583
|
+
const metadata = createTranslationMetadata(slot, sourceLocale, text, "manual", metadataFactory);
|
|
3584
|
+
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "manual", metadata));
|
|
3546
3585
|
onTranslationChange?.({
|
|
3547
3586
|
slot,
|
|
3548
3587
|
...slot.existingText === void 0 ? {} : { previousText: slot.existingText },
|
|
@@ -3551,7 +3590,7 @@ function useTranslationWorkspace({
|
|
|
3551
3590
|
metadata
|
|
3552
3591
|
});
|
|
3553
3592
|
},
|
|
3554
|
-
[commit, currentSchema, onTranslationChange, readOnly, sourceLocale]
|
|
3593
|
+
[commit, currentSchema, metadataFactory, onTranslationChange, readOnly, sourceLocale]
|
|
3555
3594
|
);
|
|
3556
3595
|
const addLocale = useCallback3(
|
|
3557
3596
|
(locale) => {
|
|
@@ -3563,14 +3602,21 @@ function useTranslationWorkspace({
|
|
|
3563
3602
|
const normalized = normalizeLocale(locale);
|
|
3564
3603
|
if (normalized === null) {
|
|
3565
3604
|
const workspaceError = workspaceLocaleValidationError(
|
|
3566
|
-
validateLocalePipeline(locale, currentSchema, policy, validateLocale, availableLocales),
|
|
3605
|
+
validateLocalePipeline(locale, currentSchema, policy, validateLocale, availableLocales, sourceLocale),
|
|
3567
3606
|
collectSchemaLocales2(currentSchema).allUniqueLocales.size,
|
|
3568
3607
|
locale
|
|
3569
3608
|
);
|
|
3570
3609
|
setError(workspaceError);
|
|
3571
3610
|
return { success: false, error: workspaceError };
|
|
3572
3611
|
}
|
|
3573
|
-
const validation = validateLocalePipeline(
|
|
3612
|
+
const validation = validateLocalePipeline(
|
|
3613
|
+
normalized,
|
|
3614
|
+
currentSchema,
|
|
3615
|
+
policy,
|
|
3616
|
+
validateLocale,
|
|
3617
|
+
availableLocales,
|
|
3618
|
+
sourceLocale
|
|
3619
|
+
);
|
|
3574
3620
|
if (!validation.valid) {
|
|
3575
3621
|
const workspaceError = workspaceLocaleValidationError(
|
|
3576
3622
|
validation,
|
|
@@ -3589,16 +3635,16 @@ function useTranslationWorkspace({
|
|
|
3589
3635
|
onLocaleAdded?.(normalized);
|
|
3590
3636
|
return { success: true };
|
|
3591
3637
|
},
|
|
3592
|
-
[availableLocales, commit, currentSchema, onLocaleAdded, policy, readOnly, validateLocale]
|
|
3638
|
+
[availableLocales, commit, currentSchema, onLocaleAdded, policy, readOnly, sourceLocale, validateLocale]
|
|
3593
3639
|
);
|
|
3594
3640
|
const isAddLocaleAllowed = useCallback3(
|
|
3595
3641
|
(locale) => {
|
|
3596
3642
|
if (readOnly) return false;
|
|
3597
3643
|
const normalized = normalizeLocale(locale);
|
|
3598
3644
|
if (normalized === null) return false;
|
|
3599
|
-
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale, availableLocales).valid;
|
|
3645
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale, availableLocales, sourceLocale).valid;
|
|
3600
3646
|
},
|
|
3601
|
-
[availableLocales, currentSchema, policy, readOnly, validateLocale]
|
|
3647
|
+
[availableLocales, currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3602
3648
|
);
|
|
3603
3649
|
const removeLocale = useCallback3(
|
|
3604
3650
|
(locale) => {
|
|
@@ -3748,6 +3794,7 @@ function useTranslationWorkspace({
|
|
|
3748
3794
|
...options,
|
|
3749
3795
|
continueOnError: true,
|
|
3750
3796
|
signal: controller.signal,
|
|
3797
|
+
createMetadata: (slot, translatedText) => options.createMetadata?.(slot, translatedText) ?? createTranslationMetadata(slot, sourceLocale, translatedText, "automatic", metadataFactory),
|
|
3751
3798
|
onProgress: (nextProgress) => {
|
|
3752
3799
|
setProgress(nextProgress);
|
|
3753
3800
|
options.onProgress?.(nextProgress);
|
|
@@ -3820,6 +3867,7 @@ function useTranslationWorkspace({
|
|
|
3820
3867
|
readOnly,
|
|
3821
3868
|
slots,
|
|
3822
3869
|
sourceLocale,
|
|
3870
|
+
metadataFactory,
|
|
3823
3871
|
translationAdapter,
|
|
3824
3872
|
signal
|
|
3825
3873
|
]
|
|
@@ -3856,8 +3904,8 @@ function useTranslationWorkspace({
|
|
|
3856
3904
|
sourceLocale,
|
|
3857
3905
|
controller.signal
|
|
3858
3906
|
);
|
|
3859
|
-
const metadata =
|
|
3860
|
-
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic"));
|
|
3907
|
+
const metadata = createTranslationMetadata(slot, sourceLocale, text, "automatic", metadataFactory);
|
|
3908
|
+
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic", metadata));
|
|
3861
3909
|
onTranslationChange?.({
|
|
3862
3910
|
slot,
|
|
3863
3911
|
...slot.existingText === void 0 ? {} : { previousText: slot.existingText },
|
|
@@ -3880,6 +3928,7 @@ function useTranslationWorkspace({
|
|
|
3880
3928
|
cause
|
|
3881
3929
|
};
|
|
3882
3930
|
setError(workspaceError);
|
|
3931
|
+
onTranslationError?.({ targetLocale: slot.locale, error: workspaceError });
|
|
3883
3932
|
return { success: false, error: workspaceError };
|
|
3884
3933
|
} finally {
|
|
3885
3934
|
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
@@ -3890,7 +3939,17 @@ function useTranslationWorkspace({
|
|
|
3890
3939
|
setIsTranslating(false);
|
|
3891
3940
|
}
|
|
3892
3941
|
},
|
|
3893
|
-
[
|
|
3942
|
+
[
|
|
3943
|
+
commit,
|
|
3944
|
+
currentSchema,
|
|
3945
|
+
metadataFactory,
|
|
3946
|
+
onTranslationChange,
|
|
3947
|
+
onTranslationError,
|
|
3948
|
+
readOnly,
|
|
3949
|
+
signal,
|
|
3950
|
+
sourceLocale,
|
|
3951
|
+
translationAdapter
|
|
3952
|
+
]
|
|
3894
3953
|
);
|
|
3895
3954
|
return {
|
|
3896
3955
|
sourceLocale,
|
|
@@ -3945,6 +4004,7 @@ function optionParentId(slot) {
|
|
|
3945
4004
|
return /^fields\.([^.]+)\.options\./u.exec(slot.path)?.[1];
|
|
3946
4005
|
}
|
|
3947
4006
|
function getNodeTitle(schema, slot) {
|
|
4007
|
+
if (slot.kind === "form") return schema.title;
|
|
3948
4008
|
if (slot.kind === "field") return schema.fields.find((field) => field.id === slot.nodeId)?.title;
|
|
3949
4009
|
if (slot.kind === "page") return schema.pages?.find((page) => page.id === slot.nodeId)?.title;
|
|
3950
4010
|
if (slot.kind === "option") return schema.fields.find((field) => field.id === optionParentId(slot))?.title;
|
|
@@ -3962,6 +4022,7 @@ function comparisonItem(schema, slot, sourceLocale, hasAdapter) {
|
|
|
3962
4022
|
return {
|
|
3963
4023
|
id: path,
|
|
3964
4024
|
path,
|
|
4025
|
+
nodeId: slot.nodeId,
|
|
3965
4026
|
targetKind: slot.kind,
|
|
3966
4027
|
targetProperty: slot.property,
|
|
3967
4028
|
...nodeTitle === void 0 ? {} : { nodeTitle },
|
|
@@ -3995,23 +4056,45 @@ function useTranslationComparison({
|
|
|
3995
4056
|
targetLocale,
|
|
3996
4057
|
translationAdapter,
|
|
3997
4058
|
readOnly = false,
|
|
4059
|
+
availableLocales,
|
|
4060
|
+
policy,
|
|
3998
4061
|
onChange,
|
|
3999
4062
|
onTranslationChange,
|
|
4000
4063
|
onTranslationReport,
|
|
4001
4064
|
onTranslationError,
|
|
4002
|
-
signal
|
|
4065
|
+
signal,
|
|
4066
|
+
onLocaleAdded,
|
|
4067
|
+
onLocaleRemoved,
|
|
4068
|
+
onLocaleChange,
|
|
4069
|
+
beforeRemoveLocale,
|
|
4070
|
+
confirmRemoveLocale,
|
|
4071
|
+
onTranslationStart,
|
|
4072
|
+
onTranslationSuccess,
|
|
4073
|
+
validateLocale,
|
|
4074
|
+
createTranslationMetadata: createTranslationMetadata2
|
|
4003
4075
|
}) {
|
|
4004
4076
|
const workspace = useTranslationWorkspace({
|
|
4005
4077
|
schema,
|
|
4006
4078
|
sourceLocale,
|
|
4007
4079
|
targetLocale,
|
|
4080
|
+
...availableLocales === void 0 ? {} : { availableLocales },
|
|
4081
|
+
...policy === void 0 ? {} : { policy },
|
|
4008
4082
|
...translationAdapter === void 0 ? {} : { translationAdapter },
|
|
4009
4083
|
readOnly,
|
|
4010
4084
|
...onChange === void 0 ? {} : { onChange },
|
|
4011
4085
|
...onTranslationChange === void 0 ? {} : { onTranslationChange },
|
|
4012
4086
|
...onTranslationReport === void 0 ? {} : { onTranslationReport },
|
|
4013
4087
|
...onTranslationError === void 0 ? {} : { onTranslationError },
|
|
4014
|
-
...signal === void 0 ? {} : { signal }
|
|
4088
|
+
...signal === void 0 ? {} : { signal },
|
|
4089
|
+
...onLocaleAdded === void 0 ? {} : { onLocaleAdded },
|
|
4090
|
+
...onLocaleRemoved === void 0 ? {} : { onLocaleRemoved },
|
|
4091
|
+
...onLocaleChange === void 0 ? {} : { onLocaleChange },
|
|
4092
|
+
...beforeRemoveLocale === void 0 ? {} : { beforeRemoveLocale },
|
|
4093
|
+
...confirmRemoveLocale === void 0 ? {} : { confirmRemoveLocale },
|
|
4094
|
+
...onTranslationStart === void 0 ? {} : { onTranslationStart },
|
|
4095
|
+
...onTranslationSuccess === void 0 ? {} : { onTranslationSuccess },
|
|
4096
|
+
...validateLocale === void 0 ? {} : { validateLocale },
|
|
4097
|
+
...createTranslationMetadata2 === void 0 ? {} : { createTranslationMetadata: createTranslationMetadata2 }
|
|
4015
4098
|
});
|
|
4016
4099
|
const [report, setReport] = useState5();
|
|
4017
4100
|
const items = useMemo5(
|
|
@@ -4049,6 +4132,13 @@ function useTranslationComparison({
|
|
|
4049
4132
|
return {
|
|
4050
4133
|
sourceLocale: workspace.sourceLocale,
|
|
4051
4134
|
targetLocale: workspace.targetLocale,
|
|
4135
|
+
targetLocales: workspace.targetLocales,
|
|
4136
|
+
localeOptions: workspace.localeOptions,
|
|
4137
|
+
setTargetLocale: workspace.setTargetLocale,
|
|
4138
|
+
addLocale: workspace.addLocale,
|
|
4139
|
+
isAddLocaleAllowed: workspace.isAddLocaleAllowed,
|
|
4140
|
+
removeLocale: workspace.removeLocale,
|
|
4141
|
+
...workspace.removeLocaleConfirmation === void 0 ? {} : { removeLocaleConfirmation: workspace.removeLocaleConfirmation },
|
|
4052
4142
|
items,
|
|
4053
4143
|
summary: summaryFor(items),
|
|
4054
4144
|
isTranslating: workspace.isTranslating,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typescript"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@form-engine-ts/core": "6.
|
|
46
|
-
"@form-engine-ts/privacy": "6.
|
|
45
|
+
"@form-engine-ts/core": "6.3.0",
|
|
46
|
+
"@form-engine-ts/privacy": "6.3.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|