@form-engine-ts/react 6.1.0 → 6.2.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 +7 -4
- package/dist/index.cjs +112 -13
- package/dist/index.d.cts +147 -114
- package/dist/index.d.ts +147 -114
- package/dist/index.js +125 -26
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -89,13 +89,16 @@ underscore-separated values such as `EN_us` are accepted as compatibility input.
|
|
|
89
89
|
Use `isAddLocaleAllowed` to disable locale controls before submission. Removing a locale also clears its
|
|
90
90
|
localized values and metadata; the default locale remains protected.
|
|
91
91
|
|
|
92
|
-
`useTranslationWorkspace`
|
|
93
|
-
`
|
|
94
|
-
|
|
92
|
+
`useTranslationWorkspace` accepts either a synchronous or asynchronous translation adapter, forwards an optional
|
|
93
|
+
`AbortSignal`, and exposes `cancelTranslation`, progress, and typed partial-failure/cancellation errors. It can notify
|
|
94
|
+
`onTranslationStart`, `onTranslationSuccess`, `onTranslationReport`, `onTranslationError`, and `onTranslationChange`
|
|
95
|
+
with typed lifecycle payloads. The MUI workspace accepts a `confirmRemoveLocale` slot for an async confirmation UI;
|
|
96
|
+
its `onConfirm` callback completes the locale removal.
|
|
95
97
|
|
|
96
98
|
`useTranslationComparison` provides a focused, controlled comparison model for form, page, field, and option text.
|
|
97
99
|
Its items expose source text, target text, status, canonical metadata, and path-based `updateTranslation`,
|
|
98
|
-
`translateSingle`, and `translateAll` actions.
|
|
100
|
+
`translateSingle`, and `translateAll` actions. Comparison row slots receive locale labels, read-only state, field type,
|
|
101
|
+
question and option indexes, plus async single-translation actions.
|
|
99
102
|
|
|
100
103
|
Wrap a builder or renderer in `FormEngineI18nProvider` to supply a UI locale and typed Core translator independently
|
|
101
104
|
from the schema's `defaultLocale` and `supportedLocales`:
|
package/dist/index.cjs
CHANGED
|
@@ -3489,6 +3489,7 @@ function useTranslationWorkspace({
|
|
|
3489
3489
|
sourceLocale = schema.defaultLocale ?? "en",
|
|
3490
3490
|
targetLocale,
|
|
3491
3491
|
translationAdapter,
|
|
3492
|
+
signal,
|
|
3492
3493
|
readOnly = false,
|
|
3493
3494
|
policy,
|
|
3494
3495
|
availableLocales,
|
|
@@ -3500,6 +3501,7 @@ function useTranslationWorkspace({
|
|
|
3500
3501
|
slots: workspaceSlots,
|
|
3501
3502
|
onTranslationStart,
|
|
3502
3503
|
onTranslationSuccess,
|
|
3504
|
+
onTranslationReport,
|
|
3503
3505
|
onTranslationError,
|
|
3504
3506
|
onTranslationChange,
|
|
3505
3507
|
validateLocale
|
|
@@ -3507,8 +3509,10 @@ function useTranslationWorkspace({
|
|
|
3507
3509
|
const [draftSchema, setDraftSchema] = (0, import_react5.useState)(schema);
|
|
3508
3510
|
const [selectedLocale, setSelectedLocale] = (0, import_react5.useState)((0, import_core5.normalizeLocale)(targetLocale ?? "") ?? targetLocale ?? "");
|
|
3509
3511
|
const [isTranslating, setIsTranslating] = (0, import_react5.useState)(false);
|
|
3512
|
+
const [progress, setProgress] = (0, import_react5.useState)();
|
|
3510
3513
|
const [error, setError] = (0, import_react5.useState)();
|
|
3511
3514
|
const [pendingRemoval, setPendingRemoval] = (0, import_react5.useState)();
|
|
3515
|
+
const activeAbortController = (0, import_react5.useRef)(void 0);
|
|
3512
3516
|
const pendingRemovalResolver = (0, import_react5.useRef)(void 0);
|
|
3513
3517
|
const currentSchema = onChange === void 0 ? draftSchema : schema;
|
|
3514
3518
|
const confirmRemoveLocaleRenderer = confirmRemoveLocale ?? workspaceSlots?.confirmRemoveLocale;
|
|
@@ -3748,7 +3752,15 @@ function useTranslationWorkspace({
|
|
|
3748
3752
|
}
|
|
3749
3753
|
setIsTranslating(true);
|
|
3750
3754
|
setError(void 0);
|
|
3755
|
+
setProgress(void 0);
|
|
3751
3756
|
onTranslationStart?.({ targetLocale: activeLocale, mode: "automatic" });
|
|
3757
|
+
const controller = new AbortController();
|
|
3758
|
+
activeAbortController.current = controller;
|
|
3759
|
+
const operationSignal = options.signal ?? signal;
|
|
3760
|
+
const abortListener = operationSignal === void 0 ? void 0 : () => controller.abort(operationSignal.reason);
|
|
3761
|
+
if (operationSignal?.aborted) controller.abort(operationSignal.reason);
|
|
3762
|
+
else if (operationSignal !== void 0 && abortListener !== void 0)
|
|
3763
|
+
operationSignal.addEventListener("abort", abortListener, { once: true });
|
|
3752
3764
|
try {
|
|
3753
3765
|
const populated = await (0, import_core5.populateSchemaTranslations)(
|
|
3754
3766
|
currentSchema,
|
|
@@ -3757,13 +3769,36 @@ function useTranslationWorkspace({
|
|
|
3757
3769
|
{
|
|
3758
3770
|
overwrite: "stale-and-missing",
|
|
3759
3771
|
preserveManualTranslations: true,
|
|
3760
|
-
...options
|
|
3772
|
+
...options,
|
|
3773
|
+
continueOnError: true,
|
|
3774
|
+
signal: controller.signal,
|
|
3775
|
+
onProgress: (nextProgress) => {
|
|
3776
|
+
setProgress(nextProgress);
|
|
3777
|
+
options.onProgress?.(nextProgress);
|
|
3778
|
+
}
|
|
3761
3779
|
}
|
|
3762
3780
|
);
|
|
3763
3781
|
commit(populated.schema);
|
|
3782
|
+
onTranslationReport?.(populated.report);
|
|
3764
3783
|
const missingSlotsCount = (0, import_core5.collectTranslationSlots)(populated.schema, activeLocale).filter(
|
|
3765
3784
|
(slot) => slot.status === "missing"
|
|
3766
3785
|
).length;
|
|
3786
|
+
if (populated.report.cancelled === true) {
|
|
3787
|
+
const workspaceError = { type: "cancelled" };
|
|
3788
|
+
setError(workspaceError);
|
|
3789
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3790
|
+
return { success: false, report: populated.report, error: workspaceError };
|
|
3791
|
+
}
|
|
3792
|
+
if ((populated.report.failed ?? 0) > 0) {
|
|
3793
|
+
const workspaceError = {
|
|
3794
|
+
type: "partial_failure",
|
|
3795
|
+
succeeded: populated.report.succeeded ?? populated.report.updatedSlots.length,
|
|
3796
|
+
failed: populated.report.failed ?? 0
|
|
3797
|
+
};
|
|
3798
|
+
setError(workspaceError);
|
|
3799
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3800
|
+
return { success: false, report: populated.report, error: workspaceError };
|
|
3801
|
+
}
|
|
3767
3802
|
onTranslationSuccess?.({
|
|
3768
3803
|
sourceLocale,
|
|
3769
3804
|
targetLocale: activeLocale,
|
|
@@ -3774,6 +3809,12 @@ function useTranslationWorkspace({
|
|
|
3774
3809
|
});
|
|
3775
3810
|
return { success: true, report: populated.report };
|
|
3776
3811
|
} catch (cause) {
|
|
3812
|
+
if (controller.signal.aborted || cause instanceof DOMException && cause.name === "AbortError") {
|
|
3813
|
+
const workspaceError2 = { type: "cancelled" };
|
|
3814
|
+
setError(workspaceError2);
|
|
3815
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError2 });
|
|
3816
|
+
return { success: false, error: workspaceError2 };
|
|
3817
|
+
}
|
|
3777
3818
|
const workspaceError = {
|
|
3778
3819
|
type: "translation_failed",
|
|
3779
3820
|
message: cause instanceof Error ? cause.message : String(cause),
|
|
@@ -3783,6 +3824,11 @@ function useTranslationWorkspace({
|
|
|
3783
3824
|
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3784
3825
|
return { success: false, error: workspaceError };
|
|
3785
3826
|
} finally {
|
|
3827
|
+
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
3828
|
+
const listener = abortListener;
|
|
3829
|
+
operationSignal.removeEventListener("abort", listener);
|
|
3830
|
+
}
|
|
3831
|
+
if (activeAbortController.current === controller) activeAbortController.current = void 0;
|
|
3786
3832
|
setIsTranslating(false);
|
|
3787
3833
|
}
|
|
3788
3834
|
},
|
|
@@ -3794,14 +3840,19 @@ function useTranslationWorkspace({
|
|
|
3794
3840
|
onTranslationError,
|
|
3795
3841
|
onTranslationStart,
|
|
3796
3842
|
onTranslationSuccess,
|
|
3843
|
+
onTranslationReport,
|
|
3797
3844
|
readOnly,
|
|
3798
3845
|
slots,
|
|
3799
3846
|
sourceLocale,
|
|
3800
|
-
translationAdapter
|
|
3847
|
+
translationAdapter,
|
|
3848
|
+
signal
|
|
3801
3849
|
]
|
|
3802
3850
|
);
|
|
3851
|
+
const cancelTranslation = (0, import_react5.useCallback)(() => {
|
|
3852
|
+
activeAbortController.current?.abort(new DOMException("The translation was cancelled.", "AbortError"));
|
|
3853
|
+
}, []);
|
|
3803
3854
|
const translateSlot = (0, import_react5.useCallback)(
|
|
3804
|
-
async (slot) => {
|
|
3855
|
+
async (slot, options = {}) => {
|
|
3805
3856
|
if (translationAdapter === void 0) {
|
|
3806
3857
|
const workspaceError = { type: "adapter_not_configured" };
|
|
3807
3858
|
setError(workspaceError);
|
|
@@ -3814,8 +3865,21 @@ function useTranslationWorkspace({
|
|
|
3814
3865
|
}
|
|
3815
3866
|
setIsTranslating(true);
|
|
3816
3867
|
setError(void 0);
|
|
3868
|
+
setProgress({ total: 1, completed: 0, succeeded: 0, failed: 0, percentage: 0 });
|
|
3869
|
+
const controller = new AbortController();
|
|
3870
|
+
activeAbortController.current = controller;
|
|
3871
|
+
const operationSignal = options.signal ?? signal;
|
|
3872
|
+
const abortListener = operationSignal === void 0 ? void 0 : () => controller.abort(operationSignal.reason);
|
|
3873
|
+
if (operationSignal?.aborted) controller.abort(operationSignal.reason);
|
|
3874
|
+
else if (operationSignal !== void 0 && abortListener !== void 0)
|
|
3875
|
+
operationSignal.addEventListener("abort", abortListener, { once: true });
|
|
3817
3876
|
try {
|
|
3818
|
-
const text = await asAsyncAdapter(translationAdapter).translateText(
|
|
3877
|
+
const text = await asAsyncAdapter(translationAdapter).translateText(
|
|
3878
|
+
slot.sourceText,
|
|
3879
|
+
slot.locale,
|
|
3880
|
+
sourceLocale,
|
|
3881
|
+
controller.signal
|
|
3882
|
+
);
|
|
3819
3883
|
const metadata = translationMetadata(slot.sourceText, sourceLocale, "automatic");
|
|
3820
3884
|
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic"));
|
|
3821
3885
|
onTranslationChange?.({
|
|
@@ -3825,8 +3889,15 @@ function useTranslationWorkspace({
|
|
|
3825
3889
|
mode: "automatic",
|
|
3826
3890
|
metadata
|
|
3827
3891
|
});
|
|
3892
|
+
setProgress({ total: 1, completed: 1, succeeded: 1, failed: 0, percentage: 100 });
|
|
3828
3893
|
return { success: true };
|
|
3829
3894
|
} catch (cause) {
|
|
3895
|
+
if (controller.signal.aborted || cause instanceof DOMException && cause.name === "AbortError") {
|
|
3896
|
+
const workspaceError2 = { type: "cancelled" };
|
|
3897
|
+
setError(workspaceError2);
|
|
3898
|
+
onTranslationError?.({ targetLocale: slot.locale, error: workspaceError2 });
|
|
3899
|
+
return { success: false, error: workspaceError2 };
|
|
3900
|
+
}
|
|
3830
3901
|
const workspaceError = {
|
|
3831
3902
|
type: "translation_failed",
|
|
3832
3903
|
message: cause instanceof Error ? cause.message : String(cause),
|
|
@@ -3835,10 +3906,15 @@ function useTranslationWorkspace({
|
|
|
3835
3906
|
setError(workspaceError);
|
|
3836
3907
|
return { success: false, error: workspaceError };
|
|
3837
3908
|
} finally {
|
|
3909
|
+
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
3910
|
+
const listener = abortListener;
|
|
3911
|
+
operationSignal.removeEventListener("abort", listener);
|
|
3912
|
+
}
|
|
3913
|
+
if (activeAbortController.current === controller) activeAbortController.current = void 0;
|
|
3838
3914
|
setIsTranslating(false);
|
|
3839
3915
|
}
|
|
3840
3916
|
},
|
|
3841
|
-
[commit, currentSchema, onTranslationChange, readOnly, sourceLocale, translationAdapter]
|
|
3917
|
+
[commit, currentSchema, onTranslationChange, onTranslationError, readOnly, signal, sourceLocale, translationAdapter]
|
|
3842
3918
|
);
|
|
3843
3919
|
return {
|
|
3844
3920
|
sourceLocale,
|
|
@@ -3859,7 +3935,9 @@ function useTranslationWorkspace({
|
|
|
3859
3935
|
setTranslation,
|
|
3860
3936
|
translateAll,
|
|
3861
3937
|
translateSlot,
|
|
3938
|
+
cancelTranslation,
|
|
3862
3939
|
isTranslating,
|
|
3940
|
+
...progress === void 0 ? {} : { progress },
|
|
3863
3941
|
...error === void 0 ? {} : { error }
|
|
3864
3942
|
};
|
|
3865
3943
|
}
|
|
@@ -3869,6 +3947,7 @@ function canonicalMetadata(metadata, sourceText, sourceLocale, translatedText) {
|
|
|
3869
3947
|
if (translatedText === void 0 || translatedText.trim().length === 0) return void 0;
|
|
3870
3948
|
if (metadata !== void 0 && typeof metadata.sourceLocale === "string" && typeof metadata.sourceTextHash === "string" && (metadata.translationSource === "automatic" || metadata.translationSource === "manual")) {
|
|
3871
3949
|
return {
|
|
3950
|
+
...metadata,
|
|
3872
3951
|
sourceLocale: metadata.sourceLocale,
|
|
3873
3952
|
sourceTextHash: metadata.sourceTextHash,
|
|
3874
3953
|
translationSource: metadata.translationSource,
|
|
@@ -3876,7 +3955,10 @@ function canonicalMetadata(metadata, sourceText, sourceLocale, translatedText) {
|
|
|
3876
3955
|
...typeof metadata.editedAt === "string" ? { editedAt: metadata.editedAt } : {}
|
|
3877
3956
|
};
|
|
3878
3957
|
}
|
|
3958
|
+
const canonicalKeys = /* @__PURE__ */ new Set(["sourceLocale", "sourceTextHash", "translationSource", "translatedAt", "editedAt"]);
|
|
3959
|
+
const extensions = Object.fromEntries(Object.entries(metadata ?? {}).filter(([key]) => !canonicalKeys.has(key)));
|
|
3879
3960
|
return {
|
|
3961
|
+
...extensions,
|
|
3880
3962
|
sourceLocale: typeof metadata?.sourceLocale === "string" ? metadata.sourceLocale : sourceLocale,
|
|
3881
3963
|
sourceTextHash: (0, import_core6.computeSourceTextHash)(sourceText),
|
|
3882
3964
|
translationSource: metadata?.isManual === true || metadata?.isManuallyEdited === true || metadata?.translationSource === "manual" ? "manual" : "automatic"
|
|
@@ -3938,7 +4020,10 @@ function useTranslationComparison({
|
|
|
3938
4020
|
translationAdapter,
|
|
3939
4021
|
readOnly = false,
|
|
3940
4022
|
onChange,
|
|
3941
|
-
onTranslationChange
|
|
4023
|
+
onTranslationChange,
|
|
4024
|
+
onTranslationReport,
|
|
4025
|
+
onTranslationError,
|
|
4026
|
+
signal
|
|
3942
4027
|
}) {
|
|
3943
4028
|
const workspace = useTranslationWorkspace({
|
|
3944
4029
|
schema,
|
|
@@ -3947,8 +4032,12 @@ function useTranslationComparison({
|
|
|
3947
4032
|
...translationAdapter === void 0 ? {} : { translationAdapter },
|
|
3948
4033
|
readOnly,
|
|
3949
4034
|
...onChange === void 0 ? {} : { onChange },
|
|
3950
|
-
...onTranslationChange === void 0 ? {} : { onTranslationChange }
|
|
4035
|
+
...onTranslationChange === void 0 ? {} : { onTranslationChange },
|
|
4036
|
+
...onTranslationReport === void 0 ? {} : { onTranslationReport },
|
|
4037
|
+
...onTranslationError === void 0 ? {} : { onTranslationError },
|
|
4038
|
+
...signal === void 0 ? {} : { signal }
|
|
3951
4039
|
});
|
|
4040
|
+
const [report, setReport] = (0, import_react6.useState)();
|
|
3952
4041
|
const items = (0, import_react6.useMemo)(
|
|
3953
4042
|
() => workspace.slots.map((slot) => comparisonItem(schema, slot, sourceLocale, translationAdapter !== void 0)),
|
|
3954
4043
|
[schema, sourceLocale, translationAdapter, workspace.slots]
|
|
@@ -3970,11 +4059,17 @@ function useTranslationComparison({
|
|
|
3970
4059
|
},
|
|
3971
4060
|
[itemByPath, workspace]
|
|
3972
4061
|
);
|
|
3973
|
-
const translateAll = (0, import_react6.useCallback)(
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
4062
|
+
const translateAll = (0, import_react6.useCallback)(
|
|
4063
|
+
async (options) => {
|
|
4064
|
+
const result = await workspace.translateAll(options);
|
|
4065
|
+
if (result.report !== void 0) {
|
|
4066
|
+
setReport(result.report);
|
|
4067
|
+
return result.report;
|
|
4068
|
+
}
|
|
4069
|
+
throw new Error(result.error?.type ?? "Translation failed.");
|
|
4070
|
+
},
|
|
4071
|
+
[workspace]
|
|
4072
|
+
);
|
|
3978
4073
|
return {
|
|
3979
4074
|
sourceLocale: workspace.sourceLocale,
|
|
3980
4075
|
targetLocale: workspace.targetLocale,
|
|
@@ -3983,7 +4078,11 @@ function useTranslationComparison({
|
|
|
3983
4078
|
isTranslating: workspace.isTranslating,
|
|
3984
4079
|
updateTranslation,
|
|
3985
4080
|
translateSingle,
|
|
3986
|
-
translateAll
|
|
4081
|
+
translateAll,
|
|
4082
|
+
cancelTranslation: workspace.cancelTranslation,
|
|
4083
|
+
...workspace.progress === void 0 ? {} : { progress: workspace.progress },
|
|
4084
|
+
...workspace.error === void 0 ? {} : { error: workspace.error },
|
|
4085
|
+
...report === void 0 ? {} : { report }
|
|
3987
4086
|
};
|
|
3988
4087
|
}
|
|
3989
4088
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _form_engine_ts_core from '@form-engine-ts/core';
|
|
2
|
-
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy,
|
|
2
|
+
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy, TranslationAdapter, AsyncTranslationAdapter, LocaleOption, TranslationReport, TranslationSlot, PopulateTranslationOptions, TranslationProgress, Question, FieldOption, ValidationIssue, ValidationError, FormValues, TranslationStatus, CanonicalTranslationMetadata, FormValue, AnswerValidationResult, FormEngineTranslator, FormEngineMessages, TranslationMissingKeyEvent, FieldType } from '@form-engine-ts/core';
|
|
3
3
|
export { FormSubmissionError, FormSubmissionSerializedError, QuestionType } from '@form-engine-ts/core';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ReactNode, ComponentType, KeyboardEvent, MouseEvent, CSSProperties } from 'react';
|
|
@@ -125,6 +125,127 @@ interface UseFormBuilderResult extends FormBuilderResult {
|
|
|
125
125
|
}
|
|
126
126
|
declare function useFormBuilder({ schema, onChange, policy, idFactory, factories, fieldEditorMode, activeFieldId: controlledActiveFieldId, defaultActiveFieldId, onActiveFieldChange }: FormBuilderOptions): FormBuilderResult;
|
|
127
127
|
|
|
128
|
+
interface UseTranslationWorkspaceOptions {
|
|
129
|
+
readonly schema: FormSchema;
|
|
130
|
+
readonly onChange?: (schema: FormSchema) => void;
|
|
131
|
+
readonly sourceLocale?: string;
|
|
132
|
+
readonly targetLocale?: string;
|
|
133
|
+
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
134
|
+
readonly signal?: AbortSignal;
|
|
135
|
+
readonly readOnly?: boolean;
|
|
136
|
+
readonly policy?: FormPolicy;
|
|
137
|
+
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
138
|
+
readonly onLocaleAdded?: (locale: string) => void;
|
|
139
|
+
readonly onLocaleRemoved?: (locale: string) => void;
|
|
140
|
+
readonly onLocaleChange?: (targetLocale: string) => void;
|
|
141
|
+
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
142
|
+
readonly slotCount: number;
|
|
143
|
+
}) => Promise<boolean> | boolean;
|
|
144
|
+
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
145
|
+
readonly slots?: Pick<TranslationWorkspaceSlots, "confirmRemoveLocale">;
|
|
146
|
+
readonly onTranslationStart?: (params: {
|
|
147
|
+
readonly targetLocale: string;
|
|
148
|
+
readonly mode: "manual" | "automatic";
|
|
149
|
+
}) => void;
|
|
150
|
+
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
151
|
+
readonly onTranslationReport?: (report: TranslationReport) => void;
|
|
152
|
+
readonly onTranslationError?: (params: {
|
|
153
|
+
readonly targetLocale: string;
|
|
154
|
+
readonly error: TranslationWorkspaceError;
|
|
155
|
+
}) => void;
|
|
156
|
+
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
157
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
158
|
+
}
|
|
159
|
+
interface LocaleValidationContext {
|
|
160
|
+
readonly locale: string;
|
|
161
|
+
readonly defaultLocale: string;
|
|
162
|
+
readonly currentLocales: readonly string[];
|
|
163
|
+
readonly policy?: FormPolicy;
|
|
164
|
+
}
|
|
165
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
166
|
+
interface LocaleValidationResult {
|
|
167
|
+
readonly valid: boolean;
|
|
168
|
+
readonly error?: {
|
|
169
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
170
|
+
readonly message: string;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
interface TranslationSummary {
|
|
174
|
+
readonly totalSlots: number;
|
|
175
|
+
readonly translatedCount: number;
|
|
176
|
+
readonly missingCount: number;
|
|
177
|
+
readonly staleCount: number;
|
|
178
|
+
readonly manualCount: number;
|
|
179
|
+
readonly completionPercentage: number;
|
|
180
|
+
}
|
|
181
|
+
type TranslationWorkspaceError = {
|
|
182
|
+
readonly type: "locale_not_allowed";
|
|
183
|
+
readonly locale: string;
|
|
184
|
+
} | {
|
|
185
|
+
readonly type: "locale_already_exists";
|
|
186
|
+
readonly locale: string;
|
|
187
|
+
} | {
|
|
188
|
+
readonly type: "invalid_locale_format";
|
|
189
|
+
readonly locale: string;
|
|
190
|
+
} | {
|
|
191
|
+
readonly type: "max_locales_exceeded";
|
|
192
|
+
readonly max: number;
|
|
193
|
+
readonly current: number;
|
|
194
|
+
} | {
|
|
195
|
+
readonly type: "read_only_mode";
|
|
196
|
+
} | {
|
|
197
|
+
readonly type: "adapter_not_configured";
|
|
198
|
+
} | {
|
|
199
|
+
readonly type: "target_locale_missing";
|
|
200
|
+
} | {
|
|
201
|
+
readonly type: "translation_failed";
|
|
202
|
+
readonly message: string;
|
|
203
|
+
readonly cause?: unknown;
|
|
204
|
+
} | {
|
|
205
|
+
readonly type: "partial_failure";
|
|
206
|
+
readonly succeeded: number;
|
|
207
|
+
readonly failed: number;
|
|
208
|
+
} | {
|
|
209
|
+
readonly type: "cancelled";
|
|
210
|
+
} | {
|
|
211
|
+
readonly type: "custom_validation_failed";
|
|
212
|
+
readonly message: string;
|
|
213
|
+
};
|
|
214
|
+
interface UseTranslationWorkspaceResult {
|
|
215
|
+
readonly sourceLocale: string;
|
|
216
|
+
readonly targetLocale: string;
|
|
217
|
+
readonly targetLocales: readonly string[];
|
|
218
|
+
readonly localeOptions: readonly LocaleOption[];
|
|
219
|
+
readonly setTargetLocale: (locale: string) => void;
|
|
220
|
+
readonly slots: readonly TranslationSlot[];
|
|
221
|
+
readonly summary: TranslationSummary;
|
|
222
|
+
readonly addLocale: (locale: string) => {
|
|
223
|
+
readonly success: boolean;
|
|
224
|
+
readonly error?: TranslationWorkspaceError;
|
|
225
|
+
};
|
|
226
|
+
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
227
|
+
readonly removeLocale: (locale: string) => boolean | Promise<boolean>;
|
|
228
|
+
readonly removeLocaleConfirmation?: ReactNode;
|
|
229
|
+
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
230
|
+
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<{
|
|
231
|
+
readonly success: boolean;
|
|
232
|
+
readonly report?: TranslationReport;
|
|
233
|
+
readonly error?: TranslationWorkspaceError;
|
|
234
|
+
}>;
|
|
235
|
+
readonly translateSlot: (slot: TranslationSlot, options?: {
|
|
236
|
+
readonly signal?: AbortSignal;
|
|
237
|
+
}) => Promise<{
|
|
238
|
+
readonly success: boolean;
|
|
239
|
+
readonly error?: TranslationWorkspaceError;
|
|
240
|
+
}>;
|
|
241
|
+
readonly cancelTranslation: () => void;
|
|
242
|
+
readonly isTranslating: boolean;
|
|
243
|
+
readonly progress?: TranslationProgress;
|
|
244
|
+
readonly error?: TranslationWorkspaceError;
|
|
245
|
+
}
|
|
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;
|
|
248
|
+
|
|
128
249
|
interface SubmissionReceipt {
|
|
129
250
|
readonly formId: string;
|
|
130
251
|
readonly formVersion: number;
|
|
@@ -396,6 +517,8 @@ interface TranslationWorkspaceHeaderProps {
|
|
|
396
517
|
readonly onTranslateAll: () => void;
|
|
397
518
|
readonly isTranslating: boolean;
|
|
398
519
|
readonly readOnly: boolean;
|
|
520
|
+
readonly progress?: TranslationProgress;
|
|
521
|
+
readonly onCancel?: () => void;
|
|
399
522
|
}
|
|
400
523
|
interface TranslationSlotRowProps {
|
|
401
524
|
readonly slot: _form_engine_ts_core.TranslationSlot;
|
|
@@ -429,21 +552,35 @@ interface TranslationComparisonHeaderProps {
|
|
|
429
552
|
readonly onTranslateAll: () => void;
|
|
430
553
|
readonly isTranslating: boolean;
|
|
431
554
|
readonly readOnly: boolean;
|
|
555
|
+
readonly report?: TranslationReport;
|
|
556
|
+
readonly progress?: TranslationProgress;
|
|
557
|
+
readonly onCancel?: () => void;
|
|
432
558
|
}
|
|
433
559
|
interface TranslationComparisonItemRowProps {
|
|
434
560
|
readonly item: TranslationComparisonItem;
|
|
561
|
+
readonly questionIndex?: number;
|
|
562
|
+
readonly fieldType?: string;
|
|
563
|
+
readonly optionIndex?: number;
|
|
564
|
+
readonly sourceLocaleLabel?: string;
|
|
565
|
+
readonly targetLocaleLabel?: string;
|
|
435
566
|
readonly readOnly: boolean;
|
|
436
567
|
readonly onChange: (text: string) => void;
|
|
437
|
-
readonly onTranslate: () => void
|
|
568
|
+
readonly onTranslate: () => Promise<void>;
|
|
438
569
|
}
|
|
439
570
|
interface UseTranslationComparisonOptions {
|
|
440
571
|
readonly schema: FormSchema;
|
|
441
572
|
readonly sourceLocale?: string;
|
|
442
573
|
readonly targetLocale: string;
|
|
443
|
-
readonly translationAdapter?: TranslationAdapter;
|
|
574
|
+
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
575
|
+
readonly signal?: AbortSignal;
|
|
444
576
|
readonly readOnly?: boolean;
|
|
445
577
|
readonly onChange?: (nextSchema: FormSchema) => void;
|
|
446
578
|
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
579
|
+
readonly onTranslationReport?: (report: TranslationReport) => void;
|
|
580
|
+
readonly onTranslationError?: (params: {
|
|
581
|
+
readonly targetLocale: string;
|
|
582
|
+
readonly error: TranslationWorkspaceError;
|
|
583
|
+
}) => void;
|
|
447
584
|
}
|
|
448
585
|
interface UseTranslationComparisonResult {
|
|
449
586
|
readonly sourceLocale: string;
|
|
@@ -453,7 +590,11 @@ interface UseTranslationComparisonResult {
|
|
|
453
590
|
readonly isTranslating: boolean;
|
|
454
591
|
readonly updateTranslation: (path: string, text: string) => void;
|
|
455
592
|
readonly translateSingle: (path: string) => Promise<void>;
|
|
456
|
-
readonly translateAll: () => Promise<TranslationReport>;
|
|
593
|
+
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<TranslationReport>;
|
|
594
|
+
readonly cancelTranslation: () => void;
|
|
595
|
+
readonly progress?: TranslationProgress;
|
|
596
|
+
readonly error?: TranslationWorkspaceError;
|
|
597
|
+
readonly report?: TranslationReport;
|
|
457
598
|
}
|
|
458
599
|
interface LocaleSelectorProps {
|
|
459
600
|
readonly targetLocale: string;
|
|
@@ -496,6 +637,7 @@ interface ConfirmRemoveLocaleSlotProps {
|
|
|
496
637
|
interface TranslationWorkspaceSlots {
|
|
497
638
|
readonly renderHeader?: (props: TranslationWorkspaceHeaderProps) => ReactNode;
|
|
498
639
|
readonly renderSlotRow?: (props: TranslationSlotRowProps) => ReactNode;
|
|
640
|
+
readonly renderItemRow?: (props: TranslationComparisonItemRowProps) => ReactNode;
|
|
499
641
|
readonly renderLocaleSelector?: (props: LocaleSelectorProps) => ReactNode;
|
|
500
642
|
readonly renderStatusBadge?: (props: {
|
|
501
643
|
readonly status: _form_engine_ts_core.TranslationStatus;
|
|
@@ -820,116 +962,7 @@ interface FieldState {
|
|
|
820
962
|
}
|
|
821
963
|
declare function useField(fieldId: string): FieldState;
|
|
822
964
|
|
|
823
|
-
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
824
|
-
|
|
825
|
-
interface UseTranslationWorkspaceOptions {
|
|
826
|
-
readonly schema: FormSchema;
|
|
827
|
-
readonly onChange?: (schema: FormSchema) => void;
|
|
828
|
-
readonly sourceLocale?: string;
|
|
829
|
-
readonly targetLocale?: string;
|
|
830
|
-
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
831
|
-
readonly readOnly?: boolean;
|
|
832
|
-
readonly policy?: FormPolicy;
|
|
833
|
-
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
834
|
-
readonly onLocaleAdded?: (locale: string) => void;
|
|
835
|
-
readonly onLocaleRemoved?: (locale: string) => void;
|
|
836
|
-
readonly onLocaleChange?: (targetLocale: string) => void;
|
|
837
|
-
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
838
|
-
readonly slotCount: number;
|
|
839
|
-
}) => Promise<boolean> | boolean;
|
|
840
|
-
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
841
|
-
readonly slots?: Pick<TranslationWorkspaceSlots, "confirmRemoveLocale">;
|
|
842
|
-
readonly onTranslationStart?: (params: {
|
|
843
|
-
readonly targetLocale: string;
|
|
844
|
-
readonly mode: "manual" | "automatic";
|
|
845
|
-
}) => void;
|
|
846
|
-
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
847
|
-
readonly onTranslationError?: (params: {
|
|
848
|
-
readonly targetLocale: string;
|
|
849
|
-
readonly error: TranslationWorkspaceError;
|
|
850
|
-
}) => void;
|
|
851
|
-
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
852
|
-
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
853
|
-
}
|
|
854
|
-
interface LocaleValidationContext {
|
|
855
|
-
readonly locale: string;
|
|
856
|
-
readonly defaultLocale: string;
|
|
857
|
-
readonly currentLocales: readonly string[];
|
|
858
|
-
readonly policy?: FormPolicy;
|
|
859
|
-
}
|
|
860
|
-
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
861
|
-
interface LocaleValidationResult {
|
|
862
|
-
readonly valid: boolean;
|
|
863
|
-
readonly error?: {
|
|
864
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
865
|
-
readonly message: string;
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
interface TranslationSummary {
|
|
869
|
-
readonly totalSlots: number;
|
|
870
|
-
readonly translatedCount: number;
|
|
871
|
-
readonly missingCount: number;
|
|
872
|
-
readonly staleCount: number;
|
|
873
|
-
readonly manualCount: number;
|
|
874
|
-
readonly completionPercentage: number;
|
|
875
|
-
}
|
|
876
|
-
type TranslationWorkspaceError = {
|
|
877
|
-
readonly type: "locale_not_allowed";
|
|
878
|
-
readonly locale: string;
|
|
879
|
-
} | {
|
|
880
|
-
readonly type: "locale_already_exists";
|
|
881
|
-
readonly locale: string;
|
|
882
|
-
} | {
|
|
883
|
-
readonly type: "invalid_locale_format";
|
|
884
|
-
readonly locale: string;
|
|
885
|
-
} | {
|
|
886
|
-
readonly type: "max_locales_exceeded";
|
|
887
|
-
readonly max: number;
|
|
888
|
-
readonly current: number;
|
|
889
|
-
} | {
|
|
890
|
-
readonly type: "read_only_mode";
|
|
891
|
-
} | {
|
|
892
|
-
readonly type: "adapter_not_configured";
|
|
893
|
-
} | {
|
|
894
|
-
readonly type: "target_locale_missing";
|
|
895
|
-
} | {
|
|
896
|
-
readonly type: "translation_failed";
|
|
897
|
-
readonly message: string;
|
|
898
|
-
readonly cause?: unknown;
|
|
899
|
-
} | {
|
|
900
|
-
readonly type: "custom_validation_failed";
|
|
901
|
-
readonly message: string;
|
|
902
|
-
};
|
|
903
|
-
interface UseTranslationWorkspaceResult {
|
|
904
|
-
readonly sourceLocale: string;
|
|
905
|
-
readonly targetLocale: string;
|
|
906
|
-
readonly targetLocales: readonly string[];
|
|
907
|
-
readonly localeOptions: readonly LocaleOption[];
|
|
908
|
-
readonly setTargetLocale: (locale: string) => void;
|
|
909
|
-
readonly slots: readonly TranslationSlot[];
|
|
910
|
-
readonly summary: TranslationSummary;
|
|
911
|
-
readonly addLocale: (locale: string) => {
|
|
912
|
-
readonly success: boolean;
|
|
913
|
-
readonly error?: TranslationWorkspaceError;
|
|
914
|
-
};
|
|
915
|
-
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
916
|
-
readonly removeLocale: (locale: string) => boolean | Promise<boolean>;
|
|
917
|
-
readonly removeLocaleConfirmation?: ReactNode;
|
|
918
|
-
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
919
|
-
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<{
|
|
920
|
-
readonly success: boolean;
|
|
921
|
-
readonly report?: TranslationReport;
|
|
922
|
-
readonly error?: TranslationWorkspaceError;
|
|
923
|
-
}>;
|
|
924
|
-
readonly translateSlot: (slot: TranslationSlot) => Promise<{
|
|
925
|
-
readonly success: boolean;
|
|
926
|
-
readonly error?: TranslationWorkspaceError;
|
|
927
|
-
}>;
|
|
928
|
-
readonly isTranslating: boolean;
|
|
929
|
-
readonly error?: TranslationWorkspaceError;
|
|
930
|
-
}
|
|
931
|
-
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[]) => LocaleValidationResult;
|
|
932
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationError, onTranslationChange, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
965
|
+
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
933
966
|
|
|
934
967
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
935
968
|
readonly ADD_FIELD: "builder.actions.addField";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _form_engine_ts_core from '@form-engine-ts/core';
|
|
2
|
-
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy,
|
|
2
|
+
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy, TranslationAdapter, AsyncTranslationAdapter, LocaleOption, TranslationReport, TranslationSlot, PopulateTranslationOptions, TranslationProgress, Question, FieldOption, ValidationIssue, ValidationError, FormValues, TranslationStatus, CanonicalTranslationMetadata, FormValue, AnswerValidationResult, FormEngineTranslator, FormEngineMessages, TranslationMissingKeyEvent, FieldType } from '@form-engine-ts/core';
|
|
3
3
|
export { FormSubmissionError, FormSubmissionSerializedError, QuestionType } from '@form-engine-ts/core';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ReactNode, ComponentType, KeyboardEvent, MouseEvent, CSSProperties } from 'react';
|
|
@@ -125,6 +125,127 @@ interface UseFormBuilderResult extends FormBuilderResult {
|
|
|
125
125
|
}
|
|
126
126
|
declare function useFormBuilder({ schema, onChange, policy, idFactory, factories, fieldEditorMode, activeFieldId: controlledActiveFieldId, defaultActiveFieldId, onActiveFieldChange }: FormBuilderOptions): FormBuilderResult;
|
|
127
127
|
|
|
128
|
+
interface UseTranslationWorkspaceOptions {
|
|
129
|
+
readonly schema: FormSchema;
|
|
130
|
+
readonly onChange?: (schema: FormSchema) => void;
|
|
131
|
+
readonly sourceLocale?: string;
|
|
132
|
+
readonly targetLocale?: string;
|
|
133
|
+
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
134
|
+
readonly signal?: AbortSignal;
|
|
135
|
+
readonly readOnly?: boolean;
|
|
136
|
+
readonly policy?: FormPolicy;
|
|
137
|
+
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
138
|
+
readonly onLocaleAdded?: (locale: string) => void;
|
|
139
|
+
readonly onLocaleRemoved?: (locale: string) => void;
|
|
140
|
+
readonly onLocaleChange?: (targetLocale: string) => void;
|
|
141
|
+
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
142
|
+
readonly slotCount: number;
|
|
143
|
+
}) => Promise<boolean> | boolean;
|
|
144
|
+
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
145
|
+
readonly slots?: Pick<TranslationWorkspaceSlots, "confirmRemoveLocale">;
|
|
146
|
+
readonly onTranslationStart?: (params: {
|
|
147
|
+
readonly targetLocale: string;
|
|
148
|
+
readonly mode: "manual" | "automatic";
|
|
149
|
+
}) => void;
|
|
150
|
+
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
151
|
+
readonly onTranslationReport?: (report: TranslationReport) => void;
|
|
152
|
+
readonly onTranslationError?: (params: {
|
|
153
|
+
readonly targetLocale: string;
|
|
154
|
+
readonly error: TranslationWorkspaceError;
|
|
155
|
+
}) => void;
|
|
156
|
+
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
157
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
158
|
+
}
|
|
159
|
+
interface LocaleValidationContext {
|
|
160
|
+
readonly locale: string;
|
|
161
|
+
readonly defaultLocale: string;
|
|
162
|
+
readonly currentLocales: readonly string[];
|
|
163
|
+
readonly policy?: FormPolicy;
|
|
164
|
+
}
|
|
165
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
166
|
+
interface LocaleValidationResult {
|
|
167
|
+
readonly valid: boolean;
|
|
168
|
+
readonly error?: {
|
|
169
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
170
|
+
readonly message: string;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
interface TranslationSummary {
|
|
174
|
+
readonly totalSlots: number;
|
|
175
|
+
readonly translatedCount: number;
|
|
176
|
+
readonly missingCount: number;
|
|
177
|
+
readonly staleCount: number;
|
|
178
|
+
readonly manualCount: number;
|
|
179
|
+
readonly completionPercentage: number;
|
|
180
|
+
}
|
|
181
|
+
type TranslationWorkspaceError = {
|
|
182
|
+
readonly type: "locale_not_allowed";
|
|
183
|
+
readonly locale: string;
|
|
184
|
+
} | {
|
|
185
|
+
readonly type: "locale_already_exists";
|
|
186
|
+
readonly locale: string;
|
|
187
|
+
} | {
|
|
188
|
+
readonly type: "invalid_locale_format";
|
|
189
|
+
readonly locale: string;
|
|
190
|
+
} | {
|
|
191
|
+
readonly type: "max_locales_exceeded";
|
|
192
|
+
readonly max: number;
|
|
193
|
+
readonly current: number;
|
|
194
|
+
} | {
|
|
195
|
+
readonly type: "read_only_mode";
|
|
196
|
+
} | {
|
|
197
|
+
readonly type: "adapter_not_configured";
|
|
198
|
+
} | {
|
|
199
|
+
readonly type: "target_locale_missing";
|
|
200
|
+
} | {
|
|
201
|
+
readonly type: "translation_failed";
|
|
202
|
+
readonly message: string;
|
|
203
|
+
readonly cause?: unknown;
|
|
204
|
+
} | {
|
|
205
|
+
readonly type: "partial_failure";
|
|
206
|
+
readonly succeeded: number;
|
|
207
|
+
readonly failed: number;
|
|
208
|
+
} | {
|
|
209
|
+
readonly type: "cancelled";
|
|
210
|
+
} | {
|
|
211
|
+
readonly type: "custom_validation_failed";
|
|
212
|
+
readonly message: string;
|
|
213
|
+
};
|
|
214
|
+
interface UseTranslationWorkspaceResult {
|
|
215
|
+
readonly sourceLocale: string;
|
|
216
|
+
readonly targetLocale: string;
|
|
217
|
+
readonly targetLocales: readonly string[];
|
|
218
|
+
readonly localeOptions: readonly LocaleOption[];
|
|
219
|
+
readonly setTargetLocale: (locale: string) => void;
|
|
220
|
+
readonly slots: readonly TranslationSlot[];
|
|
221
|
+
readonly summary: TranslationSummary;
|
|
222
|
+
readonly addLocale: (locale: string) => {
|
|
223
|
+
readonly success: boolean;
|
|
224
|
+
readonly error?: TranslationWorkspaceError;
|
|
225
|
+
};
|
|
226
|
+
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
227
|
+
readonly removeLocale: (locale: string) => boolean | Promise<boolean>;
|
|
228
|
+
readonly removeLocaleConfirmation?: ReactNode;
|
|
229
|
+
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
230
|
+
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<{
|
|
231
|
+
readonly success: boolean;
|
|
232
|
+
readonly report?: TranslationReport;
|
|
233
|
+
readonly error?: TranslationWorkspaceError;
|
|
234
|
+
}>;
|
|
235
|
+
readonly translateSlot: (slot: TranslationSlot, options?: {
|
|
236
|
+
readonly signal?: AbortSignal;
|
|
237
|
+
}) => Promise<{
|
|
238
|
+
readonly success: boolean;
|
|
239
|
+
readonly error?: TranslationWorkspaceError;
|
|
240
|
+
}>;
|
|
241
|
+
readonly cancelTranslation: () => void;
|
|
242
|
+
readonly isTranslating: boolean;
|
|
243
|
+
readonly progress?: TranslationProgress;
|
|
244
|
+
readonly error?: TranslationWorkspaceError;
|
|
245
|
+
}
|
|
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;
|
|
248
|
+
|
|
128
249
|
interface SubmissionReceipt {
|
|
129
250
|
readonly formId: string;
|
|
130
251
|
readonly formVersion: number;
|
|
@@ -396,6 +517,8 @@ interface TranslationWorkspaceHeaderProps {
|
|
|
396
517
|
readonly onTranslateAll: () => void;
|
|
397
518
|
readonly isTranslating: boolean;
|
|
398
519
|
readonly readOnly: boolean;
|
|
520
|
+
readonly progress?: TranslationProgress;
|
|
521
|
+
readonly onCancel?: () => void;
|
|
399
522
|
}
|
|
400
523
|
interface TranslationSlotRowProps {
|
|
401
524
|
readonly slot: _form_engine_ts_core.TranslationSlot;
|
|
@@ -429,21 +552,35 @@ interface TranslationComparisonHeaderProps {
|
|
|
429
552
|
readonly onTranslateAll: () => void;
|
|
430
553
|
readonly isTranslating: boolean;
|
|
431
554
|
readonly readOnly: boolean;
|
|
555
|
+
readonly report?: TranslationReport;
|
|
556
|
+
readonly progress?: TranslationProgress;
|
|
557
|
+
readonly onCancel?: () => void;
|
|
432
558
|
}
|
|
433
559
|
interface TranslationComparisonItemRowProps {
|
|
434
560
|
readonly item: TranslationComparisonItem;
|
|
561
|
+
readonly questionIndex?: number;
|
|
562
|
+
readonly fieldType?: string;
|
|
563
|
+
readonly optionIndex?: number;
|
|
564
|
+
readonly sourceLocaleLabel?: string;
|
|
565
|
+
readonly targetLocaleLabel?: string;
|
|
435
566
|
readonly readOnly: boolean;
|
|
436
567
|
readonly onChange: (text: string) => void;
|
|
437
|
-
readonly onTranslate: () => void
|
|
568
|
+
readonly onTranslate: () => Promise<void>;
|
|
438
569
|
}
|
|
439
570
|
interface UseTranslationComparisonOptions {
|
|
440
571
|
readonly schema: FormSchema;
|
|
441
572
|
readonly sourceLocale?: string;
|
|
442
573
|
readonly targetLocale: string;
|
|
443
|
-
readonly translationAdapter?: TranslationAdapter;
|
|
574
|
+
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
575
|
+
readonly signal?: AbortSignal;
|
|
444
576
|
readonly readOnly?: boolean;
|
|
445
577
|
readonly onChange?: (nextSchema: FormSchema) => void;
|
|
446
578
|
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
579
|
+
readonly onTranslationReport?: (report: TranslationReport) => void;
|
|
580
|
+
readonly onTranslationError?: (params: {
|
|
581
|
+
readonly targetLocale: string;
|
|
582
|
+
readonly error: TranslationWorkspaceError;
|
|
583
|
+
}) => void;
|
|
447
584
|
}
|
|
448
585
|
interface UseTranslationComparisonResult {
|
|
449
586
|
readonly sourceLocale: string;
|
|
@@ -453,7 +590,11 @@ interface UseTranslationComparisonResult {
|
|
|
453
590
|
readonly isTranslating: boolean;
|
|
454
591
|
readonly updateTranslation: (path: string, text: string) => void;
|
|
455
592
|
readonly translateSingle: (path: string) => Promise<void>;
|
|
456
|
-
readonly translateAll: () => Promise<TranslationReport>;
|
|
593
|
+
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<TranslationReport>;
|
|
594
|
+
readonly cancelTranslation: () => void;
|
|
595
|
+
readonly progress?: TranslationProgress;
|
|
596
|
+
readonly error?: TranslationWorkspaceError;
|
|
597
|
+
readonly report?: TranslationReport;
|
|
457
598
|
}
|
|
458
599
|
interface LocaleSelectorProps {
|
|
459
600
|
readonly targetLocale: string;
|
|
@@ -496,6 +637,7 @@ interface ConfirmRemoveLocaleSlotProps {
|
|
|
496
637
|
interface TranslationWorkspaceSlots {
|
|
497
638
|
readonly renderHeader?: (props: TranslationWorkspaceHeaderProps) => ReactNode;
|
|
498
639
|
readonly renderSlotRow?: (props: TranslationSlotRowProps) => ReactNode;
|
|
640
|
+
readonly renderItemRow?: (props: TranslationComparisonItemRowProps) => ReactNode;
|
|
499
641
|
readonly renderLocaleSelector?: (props: LocaleSelectorProps) => ReactNode;
|
|
500
642
|
readonly renderStatusBadge?: (props: {
|
|
501
643
|
readonly status: _form_engine_ts_core.TranslationStatus;
|
|
@@ -820,116 +962,7 @@ interface FieldState {
|
|
|
820
962
|
}
|
|
821
963
|
declare function useField(fieldId: string): FieldState;
|
|
822
964
|
|
|
823
|
-
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
824
|
-
|
|
825
|
-
interface UseTranslationWorkspaceOptions {
|
|
826
|
-
readonly schema: FormSchema;
|
|
827
|
-
readonly onChange?: (schema: FormSchema) => void;
|
|
828
|
-
readonly sourceLocale?: string;
|
|
829
|
-
readonly targetLocale?: string;
|
|
830
|
-
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
831
|
-
readonly readOnly?: boolean;
|
|
832
|
-
readonly policy?: FormPolicy;
|
|
833
|
-
readonly availableLocales?: readonly (string | LocaleOption)[];
|
|
834
|
-
readonly onLocaleAdded?: (locale: string) => void;
|
|
835
|
-
readonly onLocaleRemoved?: (locale: string) => void;
|
|
836
|
-
readonly onLocaleChange?: (targetLocale: string) => void;
|
|
837
|
-
readonly beforeRemoveLocale?: (locale: string, context: {
|
|
838
|
-
readonly slotCount: number;
|
|
839
|
-
}) => Promise<boolean> | boolean;
|
|
840
|
-
readonly confirmRemoveLocale?: (props: ConfirmRemoveLocaleSlotProps) => ReactNode;
|
|
841
|
-
readonly slots?: Pick<TranslationWorkspaceSlots, "confirmRemoveLocale">;
|
|
842
|
-
readonly onTranslationStart?: (params: {
|
|
843
|
-
readonly targetLocale: string;
|
|
844
|
-
readonly mode: "manual" | "automatic";
|
|
845
|
-
}) => void;
|
|
846
|
-
readonly onTranslationSuccess?: (payload: TranslationEventPayload) => void;
|
|
847
|
-
readonly onTranslationError?: (params: {
|
|
848
|
-
readonly targetLocale: string;
|
|
849
|
-
readonly error: TranslationWorkspaceError;
|
|
850
|
-
}) => void;
|
|
851
|
-
readonly onTranslationChange?: (event: TranslationSlotChangeEvent) => void;
|
|
852
|
-
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
853
|
-
}
|
|
854
|
-
interface LocaleValidationContext {
|
|
855
|
-
readonly locale: string;
|
|
856
|
-
readonly defaultLocale: string;
|
|
857
|
-
readonly currentLocales: readonly string[];
|
|
858
|
-
readonly policy?: FormPolicy;
|
|
859
|
-
}
|
|
860
|
-
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
861
|
-
interface LocaleValidationResult {
|
|
862
|
-
readonly valid: boolean;
|
|
863
|
-
readonly error?: {
|
|
864
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
865
|
-
readonly message: string;
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
interface TranslationSummary {
|
|
869
|
-
readonly totalSlots: number;
|
|
870
|
-
readonly translatedCount: number;
|
|
871
|
-
readonly missingCount: number;
|
|
872
|
-
readonly staleCount: number;
|
|
873
|
-
readonly manualCount: number;
|
|
874
|
-
readonly completionPercentage: number;
|
|
875
|
-
}
|
|
876
|
-
type TranslationWorkspaceError = {
|
|
877
|
-
readonly type: "locale_not_allowed";
|
|
878
|
-
readonly locale: string;
|
|
879
|
-
} | {
|
|
880
|
-
readonly type: "locale_already_exists";
|
|
881
|
-
readonly locale: string;
|
|
882
|
-
} | {
|
|
883
|
-
readonly type: "invalid_locale_format";
|
|
884
|
-
readonly locale: string;
|
|
885
|
-
} | {
|
|
886
|
-
readonly type: "max_locales_exceeded";
|
|
887
|
-
readonly max: number;
|
|
888
|
-
readonly current: number;
|
|
889
|
-
} | {
|
|
890
|
-
readonly type: "read_only_mode";
|
|
891
|
-
} | {
|
|
892
|
-
readonly type: "adapter_not_configured";
|
|
893
|
-
} | {
|
|
894
|
-
readonly type: "target_locale_missing";
|
|
895
|
-
} | {
|
|
896
|
-
readonly type: "translation_failed";
|
|
897
|
-
readonly message: string;
|
|
898
|
-
readonly cause?: unknown;
|
|
899
|
-
} | {
|
|
900
|
-
readonly type: "custom_validation_failed";
|
|
901
|
-
readonly message: string;
|
|
902
|
-
};
|
|
903
|
-
interface UseTranslationWorkspaceResult {
|
|
904
|
-
readonly sourceLocale: string;
|
|
905
|
-
readonly targetLocale: string;
|
|
906
|
-
readonly targetLocales: readonly string[];
|
|
907
|
-
readonly localeOptions: readonly LocaleOption[];
|
|
908
|
-
readonly setTargetLocale: (locale: string) => void;
|
|
909
|
-
readonly slots: readonly TranslationSlot[];
|
|
910
|
-
readonly summary: TranslationSummary;
|
|
911
|
-
readonly addLocale: (locale: string) => {
|
|
912
|
-
readonly success: boolean;
|
|
913
|
-
readonly error?: TranslationWorkspaceError;
|
|
914
|
-
};
|
|
915
|
-
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
916
|
-
readonly removeLocale: (locale: string) => boolean | Promise<boolean>;
|
|
917
|
-
readonly removeLocaleConfirmation?: ReactNode;
|
|
918
|
-
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
919
|
-
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<{
|
|
920
|
-
readonly success: boolean;
|
|
921
|
-
readonly report?: TranslationReport;
|
|
922
|
-
readonly error?: TranslationWorkspaceError;
|
|
923
|
-
}>;
|
|
924
|
-
readonly translateSlot: (slot: TranslationSlot) => Promise<{
|
|
925
|
-
readonly success: boolean;
|
|
926
|
-
readonly error?: TranslationWorkspaceError;
|
|
927
|
-
}>;
|
|
928
|
-
readonly isTranslating: boolean;
|
|
929
|
-
readonly error?: TranslationWorkspaceError;
|
|
930
|
-
}
|
|
931
|
-
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator, availableLocales?: readonly (string | LocaleOption)[]) => LocaleValidationResult;
|
|
932
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, availableLocales, onLocaleAdded, onLocaleRemoved, onLocaleChange, beforeRemoveLocale, confirmRemoveLocale, slots: workspaceSlots, onTranslationStart, onTranslationSuccess, onTranslationError, onTranslationChange, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
965
|
+
declare function useTranslationComparison({ schema, sourceLocale, targetLocale, translationAdapter, readOnly, onChange, onTranslationChange, onTranslationReport, onTranslationError, signal }: UseTranslationComparisonOptions): UseTranslationComparisonResult;
|
|
933
966
|
|
|
934
967
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
935
968
|
readonly ADD_FIELD: "builder.actions.addField";
|
package/dist/index.js
CHANGED
|
@@ -3274,7 +3274,7 @@ function useField(fieldId) {
|
|
|
3274
3274
|
|
|
3275
3275
|
// src/hooks/useTranslationComparison.ts
|
|
3276
3276
|
import { computeSourceTextHash as computeSourceTextHash2 } from "@form-engine-ts/core";
|
|
3277
|
-
import { useCallback as useCallback4, useMemo as useMemo5 } from "react";
|
|
3277
|
+
import { useCallback as useCallback4, useMemo as useMemo5, useState as useState5 } from "react";
|
|
3278
3278
|
|
|
3279
3279
|
// src/hooks/useTranslationWorkspace.ts
|
|
3280
3280
|
import {
|
|
@@ -3465,6 +3465,7 @@ function useTranslationWorkspace({
|
|
|
3465
3465
|
sourceLocale = schema.defaultLocale ?? "en",
|
|
3466
3466
|
targetLocale,
|
|
3467
3467
|
translationAdapter,
|
|
3468
|
+
signal,
|
|
3468
3469
|
readOnly = false,
|
|
3469
3470
|
policy,
|
|
3470
3471
|
availableLocales,
|
|
@@ -3476,6 +3477,7 @@ function useTranslationWorkspace({
|
|
|
3476
3477
|
slots: workspaceSlots,
|
|
3477
3478
|
onTranslationStart,
|
|
3478
3479
|
onTranslationSuccess,
|
|
3480
|
+
onTranslationReport,
|
|
3479
3481
|
onTranslationError,
|
|
3480
3482
|
onTranslationChange,
|
|
3481
3483
|
validateLocale
|
|
@@ -3483,8 +3485,10 @@ function useTranslationWorkspace({
|
|
|
3483
3485
|
const [draftSchema, setDraftSchema] = useState4(schema);
|
|
3484
3486
|
const [selectedLocale, setSelectedLocale] = useState4(normalizeLocale(targetLocale ?? "") ?? targetLocale ?? "");
|
|
3485
3487
|
const [isTranslating, setIsTranslating] = useState4(false);
|
|
3488
|
+
const [progress, setProgress] = useState4();
|
|
3486
3489
|
const [error, setError] = useState4();
|
|
3487
3490
|
const [pendingRemoval, setPendingRemoval] = useState4();
|
|
3491
|
+
const activeAbortController = useRef2(void 0);
|
|
3488
3492
|
const pendingRemovalResolver = useRef2(void 0);
|
|
3489
3493
|
const currentSchema = onChange === void 0 ? draftSchema : schema;
|
|
3490
3494
|
const confirmRemoveLocaleRenderer = confirmRemoveLocale ?? workspaceSlots?.confirmRemoveLocale;
|
|
@@ -3724,7 +3728,15 @@ function useTranslationWorkspace({
|
|
|
3724
3728
|
}
|
|
3725
3729
|
setIsTranslating(true);
|
|
3726
3730
|
setError(void 0);
|
|
3731
|
+
setProgress(void 0);
|
|
3727
3732
|
onTranslationStart?.({ targetLocale: activeLocale, mode: "automatic" });
|
|
3733
|
+
const controller = new AbortController();
|
|
3734
|
+
activeAbortController.current = controller;
|
|
3735
|
+
const operationSignal = options.signal ?? signal;
|
|
3736
|
+
const abortListener = operationSignal === void 0 ? void 0 : () => controller.abort(operationSignal.reason);
|
|
3737
|
+
if (operationSignal?.aborted) controller.abort(operationSignal.reason);
|
|
3738
|
+
else if (operationSignal !== void 0 && abortListener !== void 0)
|
|
3739
|
+
operationSignal.addEventListener("abort", abortListener, { once: true });
|
|
3728
3740
|
try {
|
|
3729
3741
|
const populated = await populateSchemaTranslations2(
|
|
3730
3742
|
currentSchema,
|
|
@@ -3733,13 +3745,36 @@ function useTranslationWorkspace({
|
|
|
3733
3745
|
{
|
|
3734
3746
|
overwrite: "stale-and-missing",
|
|
3735
3747
|
preserveManualTranslations: true,
|
|
3736
|
-
...options
|
|
3748
|
+
...options,
|
|
3749
|
+
continueOnError: true,
|
|
3750
|
+
signal: controller.signal,
|
|
3751
|
+
onProgress: (nextProgress) => {
|
|
3752
|
+
setProgress(nextProgress);
|
|
3753
|
+
options.onProgress?.(nextProgress);
|
|
3754
|
+
}
|
|
3737
3755
|
}
|
|
3738
3756
|
);
|
|
3739
3757
|
commit(populated.schema);
|
|
3758
|
+
onTranslationReport?.(populated.report);
|
|
3740
3759
|
const missingSlotsCount = collectTranslationSlots(populated.schema, activeLocale).filter(
|
|
3741
3760
|
(slot) => slot.status === "missing"
|
|
3742
3761
|
).length;
|
|
3762
|
+
if (populated.report.cancelled === true) {
|
|
3763
|
+
const workspaceError = { type: "cancelled" };
|
|
3764
|
+
setError(workspaceError);
|
|
3765
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3766
|
+
return { success: false, report: populated.report, error: workspaceError };
|
|
3767
|
+
}
|
|
3768
|
+
if ((populated.report.failed ?? 0) > 0) {
|
|
3769
|
+
const workspaceError = {
|
|
3770
|
+
type: "partial_failure",
|
|
3771
|
+
succeeded: populated.report.succeeded ?? populated.report.updatedSlots.length,
|
|
3772
|
+
failed: populated.report.failed ?? 0
|
|
3773
|
+
};
|
|
3774
|
+
setError(workspaceError);
|
|
3775
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3776
|
+
return { success: false, report: populated.report, error: workspaceError };
|
|
3777
|
+
}
|
|
3743
3778
|
onTranslationSuccess?.({
|
|
3744
3779
|
sourceLocale,
|
|
3745
3780
|
targetLocale: activeLocale,
|
|
@@ -3750,6 +3785,12 @@ function useTranslationWorkspace({
|
|
|
3750
3785
|
});
|
|
3751
3786
|
return { success: true, report: populated.report };
|
|
3752
3787
|
} catch (cause) {
|
|
3788
|
+
if (controller.signal.aborted || cause instanceof DOMException && cause.name === "AbortError") {
|
|
3789
|
+
const workspaceError2 = { type: "cancelled" };
|
|
3790
|
+
setError(workspaceError2);
|
|
3791
|
+
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError2 });
|
|
3792
|
+
return { success: false, error: workspaceError2 };
|
|
3793
|
+
}
|
|
3753
3794
|
const workspaceError = {
|
|
3754
3795
|
type: "translation_failed",
|
|
3755
3796
|
message: cause instanceof Error ? cause.message : String(cause),
|
|
@@ -3759,6 +3800,11 @@ function useTranslationWorkspace({
|
|
|
3759
3800
|
onTranslationError?.({ targetLocale: activeLocale, error: workspaceError });
|
|
3760
3801
|
return { success: false, error: workspaceError };
|
|
3761
3802
|
} finally {
|
|
3803
|
+
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
3804
|
+
const listener = abortListener;
|
|
3805
|
+
operationSignal.removeEventListener("abort", listener);
|
|
3806
|
+
}
|
|
3807
|
+
if (activeAbortController.current === controller) activeAbortController.current = void 0;
|
|
3762
3808
|
setIsTranslating(false);
|
|
3763
3809
|
}
|
|
3764
3810
|
},
|
|
@@ -3770,14 +3816,19 @@ function useTranslationWorkspace({
|
|
|
3770
3816
|
onTranslationError,
|
|
3771
3817
|
onTranslationStart,
|
|
3772
3818
|
onTranslationSuccess,
|
|
3819
|
+
onTranslationReport,
|
|
3773
3820
|
readOnly,
|
|
3774
3821
|
slots,
|
|
3775
3822
|
sourceLocale,
|
|
3776
|
-
translationAdapter
|
|
3823
|
+
translationAdapter,
|
|
3824
|
+
signal
|
|
3777
3825
|
]
|
|
3778
3826
|
);
|
|
3827
|
+
const cancelTranslation = useCallback3(() => {
|
|
3828
|
+
activeAbortController.current?.abort(new DOMException("The translation was cancelled.", "AbortError"));
|
|
3829
|
+
}, []);
|
|
3779
3830
|
const translateSlot = useCallback3(
|
|
3780
|
-
async (slot) => {
|
|
3831
|
+
async (slot, options = {}) => {
|
|
3781
3832
|
if (translationAdapter === void 0) {
|
|
3782
3833
|
const workspaceError = { type: "adapter_not_configured" };
|
|
3783
3834
|
setError(workspaceError);
|
|
@@ -3790,8 +3841,21 @@ function useTranslationWorkspace({
|
|
|
3790
3841
|
}
|
|
3791
3842
|
setIsTranslating(true);
|
|
3792
3843
|
setError(void 0);
|
|
3844
|
+
setProgress({ total: 1, completed: 0, succeeded: 0, failed: 0, percentage: 0 });
|
|
3845
|
+
const controller = new AbortController();
|
|
3846
|
+
activeAbortController.current = controller;
|
|
3847
|
+
const operationSignal = options.signal ?? signal;
|
|
3848
|
+
const abortListener = operationSignal === void 0 ? void 0 : () => controller.abort(operationSignal.reason);
|
|
3849
|
+
if (operationSignal?.aborted) controller.abort(operationSignal.reason);
|
|
3850
|
+
else if (operationSignal !== void 0 && abortListener !== void 0)
|
|
3851
|
+
operationSignal.addEventListener("abort", abortListener, { once: true });
|
|
3793
3852
|
try {
|
|
3794
|
-
const text = await asAsyncAdapter(translationAdapter).translateText(
|
|
3853
|
+
const text = await asAsyncAdapter(translationAdapter).translateText(
|
|
3854
|
+
slot.sourceText,
|
|
3855
|
+
slot.locale,
|
|
3856
|
+
sourceLocale,
|
|
3857
|
+
controller.signal
|
|
3858
|
+
);
|
|
3795
3859
|
const metadata = translationMetadata(slot.sourceText, sourceLocale, "automatic");
|
|
3796
3860
|
commit(updateSchemaTranslation(currentSchema, slot, text, sourceLocale, "automatic"));
|
|
3797
3861
|
onTranslationChange?.({
|
|
@@ -3801,8 +3865,15 @@ function useTranslationWorkspace({
|
|
|
3801
3865
|
mode: "automatic",
|
|
3802
3866
|
metadata
|
|
3803
3867
|
});
|
|
3868
|
+
setProgress({ total: 1, completed: 1, succeeded: 1, failed: 0, percentage: 100 });
|
|
3804
3869
|
return { success: true };
|
|
3805
3870
|
} catch (cause) {
|
|
3871
|
+
if (controller.signal.aborted || cause instanceof DOMException && cause.name === "AbortError") {
|
|
3872
|
+
const workspaceError2 = { type: "cancelled" };
|
|
3873
|
+
setError(workspaceError2);
|
|
3874
|
+
onTranslationError?.({ targetLocale: slot.locale, error: workspaceError2 });
|
|
3875
|
+
return { success: false, error: workspaceError2 };
|
|
3876
|
+
}
|
|
3806
3877
|
const workspaceError = {
|
|
3807
3878
|
type: "translation_failed",
|
|
3808
3879
|
message: cause instanceof Error ? cause.message : String(cause),
|
|
@@ -3811,10 +3882,15 @@ function useTranslationWorkspace({
|
|
|
3811
3882
|
setError(workspaceError);
|
|
3812
3883
|
return { success: false, error: workspaceError };
|
|
3813
3884
|
} finally {
|
|
3885
|
+
if (operationSignal !== void 0 && abortListener !== void 0) {
|
|
3886
|
+
const listener = abortListener;
|
|
3887
|
+
operationSignal.removeEventListener("abort", listener);
|
|
3888
|
+
}
|
|
3889
|
+
if (activeAbortController.current === controller) activeAbortController.current = void 0;
|
|
3814
3890
|
setIsTranslating(false);
|
|
3815
3891
|
}
|
|
3816
3892
|
},
|
|
3817
|
-
[commit, currentSchema, onTranslationChange, readOnly, sourceLocale, translationAdapter]
|
|
3893
|
+
[commit, currentSchema, onTranslationChange, onTranslationError, readOnly, signal, sourceLocale, translationAdapter]
|
|
3818
3894
|
);
|
|
3819
3895
|
return {
|
|
3820
3896
|
sourceLocale,
|
|
@@ -3835,7 +3911,9 @@ function useTranslationWorkspace({
|
|
|
3835
3911
|
setTranslation,
|
|
3836
3912
|
translateAll,
|
|
3837
3913
|
translateSlot,
|
|
3914
|
+
cancelTranslation,
|
|
3838
3915
|
isTranslating,
|
|
3916
|
+
...progress === void 0 ? {} : { progress },
|
|
3839
3917
|
...error === void 0 ? {} : { error }
|
|
3840
3918
|
};
|
|
3841
3919
|
}
|
|
@@ -3845,6 +3923,7 @@ function canonicalMetadata(metadata, sourceText, sourceLocale, translatedText) {
|
|
|
3845
3923
|
if (translatedText === void 0 || translatedText.trim().length === 0) return void 0;
|
|
3846
3924
|
if (metadata !== void 0 && typeof metadata.sourceLocale === "string" && typeof metadata.sourceTextHash === "string" && (metadata.translationSource === "automatic" || metadata.translationSource === "manual")) {
|
|
3847
3925
|
return {
|
|
3926
|
+
...metadata,
|
|
3848
3927
|
sourceLocale: metadata.sourceLocale,
|
|
3849
3928
|
sourceTextHash: metadata.sourceTextHash,
|
|
3850
3929
|
translationSource: metadata.translationSource,
|
|
@@ -3852,7 +3931,10 @@ function canonicalMetadata(metadata, sourceText, sourceLocale, translatedText) {
|
|
|
3852
3931
|
...typeof metadata.editedAt === "string" ? { editedAt: metadata.editedAt } : {}
|
|
3853
3932
|
};
|
|
3854
3933
|
}
|
|
3934
|
+
const canonicalKeys = /* @__PURE__ */ new Set(["sourceLocale", "sourceTextHash", "translationSource", "translatedAt", "editedAt"]);
|
|
3935
|
+
const extensions = Object.fromEntries(Object.entries(metadata ?? {}).filter(([key]) => !canonicalKeys.has(key)));
|
|
3855
3936
|
return {
|
|
3937
|
+
...extensions,
|
|
3856
3938
|
sourceLocale: typeof metadata?.sourceLocale === "string" ? metadata.sourceLocale : sourceLocale,
|
|
3857
3939
|
sourceTextHash: computeSourceTextHash2(sourceText),
|
|
3858
3940
|
translationSource: metadata?.isManual === true || metadata?.isManuallyEdited === true || metadata?.translationSource === "manual" ? "manual" : "automatic"
|
|
@@ -3914,7 +3996,10 @@ function useTranslationComparison({
|
|
|
3914
3996
|
translationAdapter,
|
|
3915
3997
|
readOnly = false,
|
|
3916
3998
|
onChange,
|
|
3917
|
-
onTranslationChange
|
|
3999
|
+
onTranslationChange,
|
|
4000
|
+
onTranslationReport,
|
|
4001
|
+
onTranslationError,
|
|
4002
|
+
signal
|
|
3918
4003
|
}) {
|
|
3919
4004
|
const workspace = useTranslationWorkspace({
|
|
3920
4005
|
schema,
|
|
@@ -3923,8 +4008,12 @@ function useTranslationComparison({
|
|
|
3923
4008
|
...translationAdapter === void 0 ? {} : { translationAdapter },
|
|
3924
4009
|
readOnly,
|
|
3925
4010
|
...onChange === void 0 ? {} : { onChange },
|
|
3926
|
-
...onTranslationChange === void 0 ? {} : { onTranslationChange }
|
|
4011
|
+
...onTranslationChange === void 0 ? {} : { onTranslationChange },
|
|
4012
|
+
...onTranslationReport === void 0 ? {} : { onTranslationReport },
|
|
4013
|
+
...onTranslationError === void 0 ? {} : { onTranslationError },
|
|
4014
|
+
...signal === void 0 ? {} : { signal }
|
|
3927
4015
|
});
|
|
4016
|
+
const [report, setReport] = useState5();
|
|
3928
4017
|
const items = useMemo5(
|
|
3929
4018
|
() => workspace.slots.map((slot) => comparisonItem(schema, slot, sourceLocale, translationAdapter !== void 0)),
|
|
3930
4019
|
[schema, sourceLocale, translationAdapter, workspace.slots]
|
|
@@ -3946,11 +4035,17 @@ function useTranslationComparison({
|
|
|
3946
4035
|
},
|
|
3947
4036
|
[itemByPath, workspace]
|
|
3948
4037
|
);
|
|
3949
|
-
const translateAll = useCallback4(
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
4038
|
+
const translateAll = useCallback4(
|
|
4039
|
+
async (options) => {
|
|
4040
|
+
const result = await workspace.translateAll(options);
|
|
4041
|
+
if (result.report !== void 0) {
|
|
4042
|
+
setReport(result.report);
|
|
4043
|
+
return result.report;
|
|
4044
|
+
}
|
|
4045
|
+
throw new Error(result.error?.type ?? "Translation failed.");
|
|
4046
|
+
},
|
|
4047
|
+
[workspace]
|
|
4048
|
+
);
|
|
3954
4049
|
return {
|
|
3955
4050
|
sourceLocale: workspace.sourceLocale,
|
|
3956
4051
|
targetLocale: workspace.targetLocale,
|
|
@@ -3959,12 +4054,16 @@ function useTranslationComparison({
|
|
|
3959
4054
|
isTranslating: workspace.isTranslating,
|
|
3960
4055
|
updateTranslation,
|
|
3961
4056
|
translateSingle,
|
|
3962
|
-
translateAll
|
|
4057
|
+
translateAll,
|
|
4058
|
+
cancelTranslation: workspace.cancelTranslation,
|
|
4059
|
+
...workspace.progress === void 0 ? {} : { progress: workspace.progress },
|
|
4060
|
+
...workspace.error === void 0 ? {} : { error: workspace.error },
|
|
4061
|
+
...report === void 0 ? {} : { report }
|
|
3963
4062
|
};
|
|
3964
4063
|
}
|
|
3965
4064
|
|
|
3966
4065
|
// src/receipt.ts
|
|
3967
|
-
import { useEffect as useEffect2, useMemo as useMemo6, useState as
|
|
4066
|
+
import { useEffect as useEffect2, useMemo as useMemo6, useState as useState6 } from "react";
|
|
3968
4067
|
function submissionReceiptQueryKey(formId, formVersion) {
|
|
3969
4068
|
return `${formId}:v${formVersion}`;
|
|
3970
4069
|
}
|
|
@@ -4042,7 +4141,7 @@ function useSubmissionReceipts(store, queries) {
|
|
|
4042
4141
|
(entry) => Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "number" && Number.isSafeInteger(entry[1]) ? [{ formId: entry[0], formVersion: entry[1] }] : []
|
|
4043
4142
|
);
|
|
4044
4143
|
}, [querySignature]);
|
|
4045
|
-
const [state, setState] =
|
|
4144
|
+
const [state, setState] = useState6({
|
|
4046
4145
|
receipts: /* @__PURE__ */ new Map(),
|
|
4047
4146
|
isLoading: stableQueries.length > 0,
|
|
4048
4147
|
error: null
|
|
@@ -4095,7 +4194,7 @@ import {
|
|
|
4095
4194
|
useId,
|
|
4096
4195
|
useMemo as useMemo7,
|
|
4097
4196
|
useRef as useRef3,
|
|
4098
|
-
useState as
|
|
4197
|
+
useState as useState7
|
|
4099
4198
|
} from "react";
|
|
4100
4199
|
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4101
4200
|
var isChoiceFieldType = (type) => type === "radio" || type === "checkbox" || type === "multi-select" || type === "select";
|
|
@@ -4612,15 +4711,15 @@ function ContextFormRenderer({
|
|
|
4612
4711
|
const prefix = useId().replace(/:/g, "");
|
|
4613
4712
|
const formRef = useRef3(null);
|
|
4614
4713
|
const loadedDraftKey = useRef3(null);
|
|
4615
|
-
const [draftRestored, setDraftRestored] =
|
|
4616
|
-
const [currentPageIndex, setCurrentPageIndex] =
|
|
4617
|
-
const [focusFieldId, setFocusFieldId] =
|
|
4618
|
-
const [confirmation, setConfirmation] =
|
|
4619
|
-
const [guardMessage, setGuardMessage] =
|
|
4620
|
-
const [guardsPending, setGuardsPending] =
|
|
4621
|
-
const [receipt, setReceipt] =
|
|
4622
|
-
const [completionData, setCompletionData] =
|
|
4623
|
-
const [receiptLoaded, setReceiptLoaded] =
|
|
4714
|
+
const [draftRestored, setDraftRestored] = useState7(false);
|
|
4715
|
+
const [currentPageIndex, setCurrentPageIndex] = useState7(0);
|
|
4716
|
+
const [focusFieldId, setFocusFieldId] = useState7(null);
|
|
4717
|
+
const [confirmation, setConfirmation] = useState7(null);
|
|
4718
|
+
const [guardMessage, setGuardMessage] = useState7(null);
|
|
4719
|
+
const [guardsPending, setGuardsPending] = useState7(false);
|
|
4720
|
+
const [receipt, setReceipt] = useState7(null);
|
|
4721
|
+
const [completionData, setCompletionData] = useState7(null);
|
|
4722
|
+
const [receiptLoaded, setReceiptLoaded] = useState7(receiptStore === void 0);
|
|
4624
4723
|
const rendererSubmissionInFlight = useRef3(false);
|
|
4625
4724
|
const fallbackAttemptId = useRef3(null);
|
|
4626
4725
|
const completionRef = useRef3(null);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.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/
|
|
46
|
-
"@form-engine-ts/
|
|
45
|
+
"@form-engine-ts/core": "6.2.0",
|
|
46
|
+
"@form-engine-ts/privacy": "6.2.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|