@parhelia/localization 0.1.10745

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.
Files changed (70) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +29 -0
  3. package/dist/core/src/editor/ui/DragPreview.d.ts +15 -0
  4. package/dist/core/src/editor/ui/DragPreview.d.ts.map +1 -0
  5. package/dist/core/src/editor/ui/DragPreview.js +32 -0
  6. package/dist/core/src/editor/ui/PerfectTree.d.ts +79 -0
  7. package/dist/core/src/editor/ui/PerfectTree.d.ts.map +1 -0
  8. package/dist/core/src/editor/ui/PerfectTree.js +857 -0
  9. package/dist/localization/src/LocalizeItemCommand.d.ts +8 -0
  10. package/dist/localization/src/LocalizeItemCommand.d.ts.map +1 -0
  11. package/dist/localization/src/LocalizeItemCommand.js +44 -0
  12. package/dist/localization/src/LocalizeItemDialog.d.ts +4 -0
  13. package/dist/localization/src/LocalizeItemDialog.d.ts.map +1 -0
  14. package/dist/localization/src/LocalizeItemDialog.js +126 -0
  15. package/dist/localization/src/LocalizeItemUtils.d.ts +17 -0
  16. package/dist/localization/src/LocalizeItemUtils.d.ts.map +1 -0
  17. package/dist/localization/src/LocalizeItemUtils.js +93 -0
  18. package/dist/localization/src/api/discovery.d.ts +36 -0
  19. package/dist/localization/src/api/discovery.d.ts.map +1 -0
  20. package/dist/localization/src/api/discovery.js +29 -0
  21. package/dist/localization/src/constants.d.ts +15 -0
  22. package/dist/localization/src/constants.d.ts.map +1 -0
  23. package/dist/localization/src/constants.js +21 -0
  24. package/dist/localization/src/hooks/useTranslationWizard.d.ts +6 -0
  25. package/dist/localization/src/hooks/useTranslationWizard.d.ts.map +1 -0
  26. package/dist/localization/src/hooks/useTranslationWizard.js +78 -0
  27. package/dist/localization/src/index.d.ts +69 -0
  28. package/dist/localization/src/index.d.ts.map +1 -0
  29. package/dist/localization/src/index.js +152 -0
  30. package/dist/localization/src/services/translationService.d.ts +102 -0
  31. package/dist/localization/src/services/translationService.d.ts.map +1 -0
  32. package/dist/localization/src/services/translationService.js +37 -0
  33. package/dist/localization/src/setup/LocalizationSetupStep.d.ts +3 -0
  34. package/dist/localization/src/setup/LocalizationSetupStep.d.ts.map +1 -0
  35. package/dist/localization/src/setup/LocalizationSetupStep.js +108 -0
  36. package/dist/localization/src/sidebar/TranslationSidebar.d.ts +2 -0
  37. package/dist/localization/src/sidebar/TranslationSidebar.d.ts.map +1 -0
  38. package/dist/localization/src/sidebar/TranslationSidebar.js +93 -0
  39. package/dist/localization/src/steps/MetadataInputStep.d.ts +4 -0
  40. package/dist/localization/src/steps/MetadataInputStep.d.ts.map +1 -0
  41. package/dist/localization/src/steps/MetadataInputStep.js +38 -0
  42. package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts +3 -0
  43. package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts.map +1 -0
  44. package/dist/localization/src/steps/ServiceLanguageSelectionStep.js +91 -0
  45. package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts +3 -0
  46. package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts.map +1 -0
  47. package/dist/localization/src/steps/SubitemDiscoveryStep.js +391 -0
  48. package/dist/localization/src/steps/index.d.ts +5 -0
  49. package/dist/localization/src/steps/index.d.ts.map +1 -0
  50. package/dist/localization/src/steps/index.js +4 -0
  51. package/dist/localization/src/steps/types.d.ts +68 -0
  52. package/dist/localization/src/steps/types.d.ts.map +1 -0
  53. package/dist/localization/src/steps/types.js +1 -0
  54. package/dist/localization/src/translation-center/BatchTranslationView.d.ts +7 -0
  55. package/dist/localization/src/translation-center/BatchTranslationView.d.ts.map +1 -0
  56. package/dist/localization/src/translation-center/BatchTranslationView.js +487 -0
  57. package/dist/localization/src/translation-center/RecentTranslations.d.ts +2 -0
  58. package/dist/localization/src/translation-center/RecentTranslations.d.ts.map +1 -0
  59. package/dist/localization/src/translation-center/RecentTranslations.js +199 -0
  60. package/dist/localization/src/translation-center/TranslationManagement.d.ts +2 -0
  61. package/dist/localization/src/translation-center/TranslationManagement.d.ts.map +1 -0
  62. package/dist/localization/src/translation-center/TranslationManagement.js +25 -0
  63. package/dist/localization/src/types.d.ts +18 -0
  64. package/dist/localization/src/types.d.ts.map +1 -0
  65. package/dist/localization/src/types.js +1 -0
  66. package/dist/localization/src/utils/createVersions.d.ts +14 -0
  67. package/dist/localization/src/utils/createVersions.d.ts.map +1 -0
  68. package/dist/localization/src/utils/createVersions.js +26 -0
  69. package/package.json +47 -0
  70. package/styles.css +1 -0
@@ -0,0 +1,8 @@
1
+ import { Command, CommandContext, CommandData, FullItem } from "@parhelia/core";
2
+ export type LocalizeItemCommandData = CommandData & {
3
+ items: FullItem[];
4
+ };
5
+ export type LocalizeItemCommandContext = CommandContext<LocalizeItemCommandData>;
6
+ export type LocalizeItemCommand = Command<LocalizeItemCommandData>;
7
+ export declare const localizeItemCommand: LocalizeItemCommand;
8
+ //# sourceMappingURL=LocalizeItemCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../../../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAMhF,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,cAAc,CAAC,uBAAuB,CAAC,CAAC;AACjF,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,eAAO,MAAM,mBAAmB,EAAE,mBAgDjC,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Globe } from "lucide-react";
3
+ import { LocalizeItemDialog } from "./LocalizeItemDialog";
4
+ export const localizeItemCommand = {
5
+ id: "localizeItem",
6
+ label: "Localize",
7
+ icon: _jsx(Globe, { strokeWidth: 1 }),
8
+ disabled: (context) => {
9
+ if (!context.data?.items || context.data.items.length === 0) {
10
+ return true;
11
+ }
12
+ // Check if multi-item is disabled and multiple items are selected
13
+ const multiItemEnabled = context.editContext?.configuration?.localization?.multiItem !== false;
14
+ if (!multiItemEnabled && context.data.items.length > 1) {
15
+ return true; // Disable command when multiple items selected in single-item mode
16
+ }
17
+ return false;
18
+ },
19
+ execute: async (context) => {
20
+ const items = context.data?.items;
21
+ if (!items || !items?.length) {
22
+ return;
23
+ }
24
+ const result = await context.openDialog(LocalizeItemDialog, {
25
+ items: items,
26
+ editContext: context.editContext,
27
+ });
28
+ // If translation was started (result contains batchId), navigate to the batch view
29
+ if (result?.batchId) {
30
+ const translationManagementEnabled = context.editContext?.configuration?.localization?.translationManagement !== false;
31
+ if (translationManagementEnabled) {
32
+ // Synchronous URL update to avoid race with view URL sync
33
+ const params = new URLSearchParams(window.location.search);
34
+ params.set("view", "translation-management");
35
+ params.set("batchId", result.batchId);
36
+ const newUrl = `${window.location.pathname}?${params.toString()}`;
37
+ window.history.pushState(null, "", newUrl);
38
+ // Now switch the view; URL already contains batchId
39
+ context.editContext.switchView("translation-management");
40
+ }
41
+ }
42
+ return result;
43
+ },
44
+ };
@@ -0,0 +1,4 @@
1
+ import { DialogProps } from "@parhelia/core";
2
+ import { TranslationDialogResult, LocalizeItemDialogProps } from "./steps/types";
3
+ export declare function LocalizeItemDialog(props: LocalizeItemDialogProps & DialogProps<TranslationDialogResult>): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=LocalizeItemDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../../../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAYvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA4PtE"}
@@ -0,0 +1,126 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, Button, cn } from "@parhelia/core";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { ChevronRight } from "lucide-react";
5
+ import { useTranslationWizard } from "./hooks/useTranslationWizard";
6
+ import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
7
+ // Note: DialogButtons is an internal component that might need to be added to core exports
8
+ // For now, we'll implement it inline
9
+ const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "flex justify-end gap-2 pt-4 border-t mt-auto px-6 pb-6", ...props, children: children }));
10
+ export function LocalizeItemDialog(props) {
11
+ const editContext = props.editContext;
12
+ const configuration = editContext.configuration.translationWizard;
13
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
14
+ const [completedByStepId, setCompletedByStepId] = useState({});
15
+ const [beforeNextCallback, setBeforeNextCallback] = useState(null);
16
+ const { wizardData, setWizardData } = useTranslationWizard(props);
17
+ // Keep a ref to the latest wizard data to avoid stale closure issues
18
+ const latestWizardDataRef = useRef(wizardData);
19
+ latestWizardDataRef.current = wizardData;
20
+ // Clear footer actions when step changes to avoid stale actions from previous steps
21
+ const [footerActions, setFooterActions] = useState([]);
22
+ useEffect(() => {
23
+ setFooterActions([]);
24
+ }, [currentStepIndex]);
25
+ // Provide stable callbacks to steps to avoid update loops
26
+ const lastActionsSigRef = useRef("");
27
+ const provideFooterActions = useCallback((actions) => {
28
+ const normalized = (actions || []).map(a => ({ key: a.key, label: a.label, disabled: !!a.disabled, signature: a.signature || "" }));
29
+ const signature = JSON.stringify(normalized);
30
+ if (signature === lastActionsSigRef.current)
31
+ return;
32
+ lastActionsSigRef.current = signature;
33
+ setFooterActions(actions || []);
34
+ }, []);
35
+ const requestCloseCb = useCallback((result) => {
36
+ props.onClose?.(result);
37
+ }, [props.onClose]);
38
+ // Filter steps based on skip conditions
39
+ const activeSteps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
40
+ const currentStep = activeSteps[currentStepIndex];
41
+ const isLastStep = currentStepIndex === activeSteps.length - 1;
42
+ const canProceed = !!(currentStep && completedByStepId[currentStep.id]);
43
+ const handleNext = async () => {
44
+ if (beforeNextCallback && typeof beforeNextCallback === 'function') {
45
+ const canProceed = await beforeNextCallback();
46
+ if (!canProceed)
47
+ return;
48
+ }
49
+ if (currentStep?.beforeNext) {
50
+ const canProceed = await currentStep.beforeNext(wizardData, editContext);
51
+ if (!canProceed)
52
+ return;
53
+ }
54
+ if (isLastStep) {
55
+ await handleFinish();
56
+ }
57
+ else {
58
+ setCurrentStepIndex(currentStepIndex + 1);
59
+ }
60
+ };
61
+ const handlePrevious = () => {
62
+ if (currentStepIndex > 0) {
63
+ // Clear the callback when moving to previous step
64
+ setBeforeNextCallback(null);
65
+ setCurrentStepIndex(currentStepIndex - 1);
66
+ }
67
+ };
68
+ const handleFinish = useCallback(async () => {
69
+ try {
70
+ // Use the ref to get the absolute latest wizard data
71
+ const currentWizardData = latestWizardDataRef.current;
72
+ const result = {
73
+ translationProvider: currentWizardData.translationProvider,
74
+ targetLanguages: currentWizardData.targetLanguages,
75
+ includeSubitems: currentWizardData.includeSubitems,
76
+ discoveredItems: currentWizardData.discoveredItems,
77
+ };
78
+ if (currentWizardData.translationProvider === "Create Versions") {
79
+ props.onClose?.(result);
80
+ }
81
+ else {
82
+ try {
83
+ const batchId = generateBatchId();
84
+ const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
85
+ console.log("Translation completed successfully:", translationResult);
86
+ // Include batchId in result for navigation
87
+ const resultWithBatch = { ...result, batchId };
88
+ props.onClose?.(resultWithBatch);
89
+ }
90
+ catch (error) {
91
+ console.error("Translation failed:", error);
92
+ // Still close the dialog to show results
93
+ props.onClose?.(result);
94
+ }
95
+ }
96
+ }
97
+ catch (error) {
98
+ console.error("Error in handleFinish:", error);
99
+ props.onClose?.(null);
100
+ }
101
+ }, [editContext, props]);
102
+ const handleStepCompleted = useCallback((completed) => {
103
+ if (!currentStep)
104
+ return;
105
+ setCompletedByStepId(prev => ({ ...prev, [currentStep.id]: completed }));
106
+ }, [currentStep?.id]);
107
+ const StepComponent = currentStep?.component;
108
+ return (_jsx(Dialog, { open: true, onOpenChange: (open) => {
109
+ // Only close when explicitly requested (e.g., close button clicked)
110
+ if (!open) {
111
+ props.onClose?.(null);
112
+ }
113
+ }, children: _jsxs(DialogContent, { className: "w-[90vw] max-w-5xl h-[85vh] max-h-[900px] min-h-[700px] overflow-hidden flex flex-col", "data-testid": "translation-wizard-dialog", onPointerDownOutside: (e) => e.preventDefault(), onEscapeKeyDown: (e) => e.preventDefault(), "aria-describedby": "translation-wizard-description", style: {
114
+ width: 'min(90vw, 1280px)',
115
+ height: 'min(85vh, 900px)',
116
+ minHeight: '700px'
117
+ }, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { "data-testid": "translation-wizard-title", children: "Translation Wizard" }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description || "Configure and start translation for your content." })] }), _jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [_jsx("div", { className: "border-b border-gray-300 bg-white px-4", "data-testid": "translation-wizard-step-navigation", children: _jsx("div", { className: "flex items-center gap-3 py-2", children: activeSteps.map((step, index) => (_jsxs("div", { className: "flex items-center gap-2", "data-testid": `step-indicator-${step.id}`, children: [_jsx("div", { className: cn("flex h-8 w-8 items-center justify-center rounded-full border text-sm font-medium", currentStepIndex === index &&
118
+ "border-blue-500 text-blue-500 bg-blue-50", currentStepIndex < index && "border-gray-300 text-gray-400", currentStepIndex > index &&
119
+ "bg-blue-500 text-white border-blue-500"), "data-testid": `step-indicator-circle-${step.id}`, children: currentStepIndex > index ? "✓" : index + 1 }), _jsx("span", { className: cn("text-sm", currentStepIndex === index
120
+ ? "text-blue-600 font-medium"
121
+ : currentStepIndex > index
122
+ ? "text-blue-600"
123
+ : "text-gray-400"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRight, { className: "w-4 h-4 text-gray-400 mx-2" }))] }, step.id))) }) }), _jsx("div", { className: "flex-1 overflow-y-auto min-h-0", "data-testid": "translation-wizard-step-content", children: StepComponent && (_jsx("div", { className: "h-full", children: _jsx(StepComponent, { data: wizardData, setData: setWizardData, editContext: editContext, onStepCompleted: handleStepCompleted, setBeforeNextCallback: (cb) => {
124
+ setBeforeNextCallback((prev) => (prev === cb ? prev : cb));
125
+ }, setFooterActions: provideFooterActions, requestClose: requestCloseCb }) })) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [footerActions.map((a) => (_jsx(Button, { onClick: a.onClick, disabled: !!a.disabled, variant: "default", size: "default", "data-testid": `translation-wizard-footer-action-${a.key}`, children: a.label }, a.key))), currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", "data-testid": "translation-wizard-previous-button", children: "Previous" })), _jsx(Button, { onClick: handleNext, disabled: !canProceed, variant: "default", size: "default", "data-testid": "translation-wizard-next-button", children: isLastStep ? "Start Translation" : "Next" }), _jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", "data-testid": "translation-wizard-cancel-button", children: "Cancel" })] })] })] }) }));
126
+ }
@@ -0,0 +1,17 @@
1
+ import { TranslationStatus } from "./types";
2
+ import { FullItem } from "@parhelia/core";
3
+ import { TranslationWizardData } from "./steps/types";
4
+ import { EditContextType } from "@parhelia/core";
5
+ export declare function generateBatchId(): string;
6
+ export type StartedTranslation = {
7
+ itemId: string;
8
+ targetLanguage: string;
9
+ jobId?: string;
10
+ };
11
+ export type TranslationResult = {
12
+ started: StartedTranslation[];
13
+ batchId?: string;
14
+ };
15
+ export declare function performDefaultTranslation(wizardData: TranslationWizardData, editContext: EditContextType, predefinedBatchId?: string): Promise<TranslationResult>;
16
+ export declare const defaultTranslateAll: (languageCodes: string[], translationStatus: TranslationStatus[], sessionId: string, item: FullItem, translationProvider: string) => Promise<TranslationResult>;
17
+ //# sourceMappingURL=LocalizeItemUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizeItemUtils.d.ts","sourceRoot":"","sources":["../../../src/LocalizeItemUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIjD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,qBAAqB,EACjC,WAAW,EAAE,eAAe,EAC5B,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAqE5B;AAGD,eAAO,MAAM,mBAAmB,GAAU,eAAe,MAAM,EAAE,EAAE,mBAAmB,iBAAiB,EAAE,EAAE,WAAW,MAAM,EAAE,MAAM,QAAQ,EAAE,qBAAqB,MAAM,+BA0BxK,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { requestBatchTranslation } from "./services/translationService";
2
+ // Shared utility for generating unique batch IDs
3
+ export function generateBatchId() {
4
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}-${performance.now().toString(36).substring(2, 8)}`;
5
+ }
6
+ // Unified translation function that handles both single and batch translations
7
+ // Now always uses batch logic for traceability in recentTranslation view
8
+ export async function performDefaultTranslation(wizardData, editContext, predefinedBatchId) {
9
+ const batchId = predefinedBatchId || generateBatchId();
10
+ const itemsToTranslate = wizardData.includeSubitems
11
+ ? wizardData.discoveredItems
12
+ : wizardData.items;
13
+ // Prepare all translation requests
14
+ const translationRequests = [];
15
+ for (const item of itemsToTranslate) {
16
+ for (const language of wizardData.targetLanguages) {
17
+ const languageInfo = wizardData.languageData.get(language);
18
+ if (!languageInfo?.translationStatus)
19
+ continue;
20
+ const status = languageInfo.translationStatus;
21
+ if (status.sourceLanguage === language)
22
+ continue;
23
+ const translationItem = {
24
+ itemId: item.descriptor.id,
25
+ targetLanguage: language,
26
+ sourceLanguage: status.sourceLanguage || "en",
27
+ };
28
+ // Get metadata for this specific item and language
29
+ const itemLanguageMetadata = wizardData.itemMetadata.get(item.descriptor.id)?.get(language);
30
+ if (itemLanguageMetadata) {
31
+ translationItem.metadata = itemLanguageMetadata;
32
+ }
33
+ translationRequests.push(translationItem);
34
+ }
35
+ }
36
+ try {
37
+ const batchResult = await requestBatchTranslation({
38
+ sessionId: editContext.sessionId,
39
+ batchId,
40
+ provider: wizardData.translationProvider,
41
+ batchMetadata: wizardData.metadata,
42
+ translations: translationRequests,
43
+ });
44
+ if (batchResult.type === "error") {
45
+ console.error('Translation request failed:', batchResult);
46
+ return { started: [], batchId };
47
+ }
48
+ const batchResponse = batchResult.data;
49
+ if (!batchResponse) {
50
+ console.error('Batch response is undefined');
51
+ return { started: [], batchId };
52
+ }
53
+ const started = batchResponse.started?.map((result) => ({
54
+ itemId: result.itemId,
55
+ targetLanguage: result.targetLanguage,
56
+ jobId: result.jobId,
57
+ })) || [];
58
+ if (batchResponse.skipped && batchResponse.skipped.length > 0) {
59
+ console.warn('Some translations were skipped:', batchResponse.skipped);
60
+ }
61
+ return { started, batchId };
62
+ }
63
+ catch (error) {
64
+ console.error('Translation request failed:', error);
65
+ return { started: [], batchId };
66
+ }
67
+ }
68
+ // Legacy function for backward compatibility
69
+ export const defaultTranslateAll = async (languageCodes, translationStatus, sessionId, item, translationProvider) => {
70
+ // Create a minimal wizardData-like object for the legacy function
71
+ const wizardData = {
72
+ items: [item],
73
+ targetLanguages: languageCodes,
74
+ languageData: new Map(languageCodes.map(lang => {
75
+ const status = translationStatus?.find(x => x.targetLanguage === lang);
76
+ return [lang, {
77
+ name: lang,
78
+ languageCode: lang,
79
+ items: [item.descriptor],
80
+ translationStatus: status
81
+ }];
82
+ })),
83
+ translationProvider,
84
+ includeSubitems: false,
85
+ discoveredItems: [],
86
+ itemMetadata: new Map(),
87
+ metadata: null,
88
+ translationProviders: []
89
+ };
90
+ const editContext = { sessionId };
91
+ const result = await performDefaultTranslation(wizardData, editContext);
92
+ return result;
93
+ };
@@ -0,0 +1,36 @@
1
+ export type DiscoveredItem = {
2
+ id: string;
3
+ name: string;
4
+ path: string;
5
+ parentId?: string | null;
6
+ hasChildren: boolean;
7
+ hasLayout?: boolean;
8
+ };
9
+ export type TreeNode = {
10
+ key: string;
11
+ label: string;
12
+ data: DiscoveredItem;
13
+ children?: TreeNode[];
14
+ };
15
+ export type BackendTreeNode = {
16
+ id: string;
17
+ name: string;
18
+ path: string;
19
+ hasLayout: boolean;
20
+ isFolder: boolean;
21
+ icon?: string;
22
+ children: BackendTreeNode[];
23
+ };
24
+ export type DiscoverItemsTreeRequest = {
25
+ rootItemIds: string[];
26
+ language?: string;
27
+ };
28
+ export type DiscoverItemsTreeResponse = {
29
+ trees: BackendTreeNode[];
30
+ totalIncluded: number;
31
+ isComplete: boolean;
32
+ };
33
+ export declare function discoverItemsTree(req: DiscoverItemsTreeRequest, sessionId?: string): Promise<DiscoverItemsTreeResponse>;
34
+ export declare function convertBackendTreeToTreeNodes(trees: BackendTreeNode[]): TreeNode[];
35
+ export declare function flattenPagesFromBackendTrees(trees: BackendTreeNode[]): DiscoveredItem[];
36
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../../src/api/discovery.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAInG,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAWpC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,CAQlF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAQvF"}
@@ -0,0 +1,29 @@
1
+ import { post } from "@parhelia/core";
2
+ export async function discoverItemsTree(req, sessionId) {
3
+ const { data, type, details } = await post("/parhelia/translation/discoveritemstree", {
4
+ RootItemIds: req.rootItemIds,
5
+ Language: req.language,
6
+ }, sessionId);
7
+ if (type !== "success" || !data)
8
+ throw new Error(details || "Failed to discover items tree");
9
+ return data;
10
+ }
11
+ export function convertBackendTreeToTreeNodes(trees) {
12
+ const convert = (n) => ({
13
+ key: n.id,
14
+ label: n.name,
15
+ data: { id: n.id, name: n.name, path: n.path, hasLayout: n.hasLayout, icon: n.icon, parentId: undefined, hasChildren: (n.children?.length ?? 0) > 0 },
16
+ children: n.children?.map(convert) || [],
17
+ });
18
+ return trees.map(convert);
19
+ }
20
+ export function flattenPagesFromBackendTrees(trees) {
21
+ const out = [];
22
+ const walk = (n) => {
23
+ if (n.hasLayout)
24
+ out.push({ id: n.id, name: n.name, path: n.path, hasChildren: (n.children?.length ?? 0) > 0, parentId: undefined });
25
+ n.children?.forEach(walk);
26
+ };
27
+ trees.forEach(walk);
28
+ return out;
29
+ }
@@ -0,0 +1,15 @@
1
+ export declare const DISCOVERY_CONFIRMATION_THRESHOLD = 100;
2
+ export declare const EXCLUDED_TEMPLATE_IDS: string[];
3
+ export declare const DIALOG_DIMENSIONS: {
4
+ width: string;
5
+ height: string;
6
+ };
7
+ export declare const STEP_STATES: {
8
+ readonly COMPLETED: "completed";
9
+ readonly CURRENT: "current";
10
+ readonly PENDING: "pending";
11
+ };
12
+ export declare const STORAGE_KEYS: {
13
+ readonly TRANSLATION_PROVIDER: "editor.translationProvider";
14
+ };
15
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAGpD,eAAO,MAAM,qBAAqB,UAEjC,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;CAG7B,CAAC;AAGF,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAGX,eAAO,MAAM,YAAY;;CAEf,CAAC"}
@@ -0,0 +1,21 @@
1
+ // Discovery configuration
2
+ export const DISCOVERY_CONFIRMATION_THRESHOLD = 100; // Pause every N subitems
3
+ // Template IDs to exclude from translation
4
+ export const EXCLUDED_TEMPLATE_IDS = [
5
+ "de8bfdac-5f9f-40c3-a680-2fce9997ae28" // System template ID
6
+ ];
7
+ // Dialog dimensions
8
+ export const DIALOG_DIMENSIONS = {
9
+ width: "75vw",
10
+ height: "75vh"
11
+ };
12
+ // Step progress indicators
13
+ export const STEP_STATES = {
14
+ COMPLETED: "completed",
15
+ CURRENT: "current",
16
+ PENDING: "pending"
17
+ };
18
+ // Local storage keys
19
+ export const STORAGE_KEYS = {
20
+ TRANSLATION_PROVIDER: "editor.translationProvider"
21
+ };
@@ -0,0 +1,6 @@
1
+ import { TranslationWizardData, LocalizeItemDialogProps } from "../steps/types";
2
+ export declare function useTranslationWizard(props: LocalizeItemDialogProps): {
3
+ wizardData: TranslationWizardData;
4
+ setWizardData: import("react").Dispatch<import("react").SetStateAction<TranslationWizardData>>;
5
+ };
6
+ //# sourceMappingURL=useTranslationWizard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTranslationWizard.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTranslationWizard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAMhF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,uBAAuB;;;EA8FlE"}
@@ -0,0 +1,78 @@
1
+ import { useState, useEffect } from "react";
2
+ import { getLanguagesAndVersions } from "@parhelia/core";
3
+ import { getTranslationStatus, getTranslationProviders } from "../services/translationService";
4
+ export function useTranslationWizard(props) {
5
+ const [wizardData, setWizardData] = useState({
6
+ items: props.items,
7
+ targetLanguages: [],
8
+ translationProvider: "",
9
+ includeSubitems: false,
10
+ discoveredItems: [],
11
+ languageData: new Map(),
12
+ translationProviders: [],
13
+ itemMetadata: new Map(), // Initialize as Map<string, Map<string, string>>
14
+ metadata: null, // Initialize with test value
15
+ });
16
+ // Load initial data
17
+ useEffect(() => {
18
+ const loadInitialData = async () => {
19
+ const itemDescriptors = props.items.map((x) => x.descriptor);
20
+ // Load translation providers
21
+ const providersResult = await getTranslationProviders();
22
+ const providers = providersResult.data || [];
23
+ if (providers.length > 0) {
24
+ const savedProvider = localStorage.getItem("editor.translationProvider");
25
+ // Validate that saved provider exists in current providers by name
26
+ const validProvider = savedProvider && providers.find(p => p.name === savedProvider)
27
+ ? savedProvider
28
+ : providers[0]?.name || "";
29
+ const defaultProvider = validProvider;
30
+ // Load languages for each item
31
+ const itemLanguages = await Promise.all(itemDescriptors.map(async (item) => {
32
+ const result = await getLanguagesAndVersions(item);
33
+ return result.data?.languages || [];
34
+ }));
35
+ // Load translation status for each item
36
+ const translationStatuses = await Promise.all(itemDescriptors.map(async (item) => {
37
+ const result = await getTranslationStatus(item.id);
38
+ return result.data || [];
39
+ }));
40
+ const languageMap = new Map();
41
+ itemLanguages.forEach((languages, itemIndex) => {
42
+ languages.forEach((language) => {
43
+ const languageData = languageMap.get(language.languageCode) || {
44
+ name: language.name,
45
+ languageCode: language.languageCode,
46
+ items: [],
47
+ };
48
+ if (language.versions > 0 && itemDescriptors[itemIndex]) {
49
+ languageData.items.push(itemDescriptors[itemIndex]);
50
+ }
51
+ const status = translationStatuses[itemIndex]?.find((s) => s.targetLanguage === language.languageCode);
52
+ if (status && !languageData.translationStatus) {
53
+ languageData.translationStatus = status;
54
+ }
55
+ languageMap.set(language.languageCode, languageData);
56
+ });
57
+ });
58
+ setWizardData(prevData => ({
59
+ ...prevData,
60
+ translationProviders: providers,
61
+ translationProvider: defaultProvider,
62
+ languageData: languageMap,
63
+ }));
64
+ }
65
+ };
66
+ loadInitialData();
67
+ }, [props.items]);
68
+ // Save translation provider to localStorage when it changes
69
+ useEffect(() => {
70
+ if (wizardData.translationProvider) {
71
+ localStorage.setItem("editor.translationProvider", wizardData.translationProvider);
72
+ }
73
+ }, [wizardData.translationProvider]);
74
+ return {
75
+ wizardData,
76
+ setWizardData,
77
+ };
78
+ }
@@ -0,0 +1,69 @@
1
+ import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
2
+ import { SubitemDiscoveryStep } from "./steps/SubitemDiscoveryStep";
3
+ import { TranslationWizardData } from "./steps/types";
4
+ import { EditorConfiguration } from "@parhelia/core";
5
+ declare const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION: {
6
+ steps: ({
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ component: typeof ServiceLanguageSelectionStep;
11
+ skipCondition?: undefined;
12
+ } | {
13
+ id: string;
14
+ name: string;
15
+ description: string;
16
+ component: typeof SubitemDiscoveryStep;
17
+ skipCondition: (data: TranslationWizardData) => boolean;
18
+ })[];
19
+ };
20
+ declare const SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION: {
21
+ steps: {
22
+ id: string;
23
+ name: string;
24
+ description: string;
25
+ component: typeof ServiceLanguageSelectionStep;
26
+ }[];
27
+ };
28
+ export { DEFAULT_TRANSLATION_WIZARD_CONFIGURATION, SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION };
29
+ export { LocalizeItemDialog } from "./LocalizeItemDialog";
30
+ export { localizeItemCommand } from "./LocalizeItemCommand";
31
+ export { TranslationManagement } from "./translation-center/TranslationManagement";
32
+ export { RecentTranslations } from "./translation-center/RecentTranslations";
33
+ export { BatchTranslationView } from "./translation-center/BatchTranslationView";
34
+ export { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
35
+ export type { LocalizeItemCommand, LocalizeItemCommandContext, LocalizeItemCommandData } from "./LocalizeItemCommand";
36
+ export { defaultTranslateAll, generateBatchId, performDefaultTranslation, type StartedTranslation, type TranslationResult } from "./LocalizeItemUtils";
37
+ export * from "./steps/types";
38
+ export type { TranslationProviderInfo, TranslationStatus } from "./steps/types";
39
+ export type LocalizationConfigOptions = {
40
+ /**
41
+ * Enable multi-item translation (allows translating multiple items and their subitems).
42
+ * When false, restricts translation to single items only.
43
+ * @default true
44
+ */
45
+ multiItem?: boolean;
46
+ /**
47
+ * Enable Translation Management view for tracking and managing translation batches.
48
+ * @default true
49
+ */
50
+ translationManagement?: boolean;
51
+ };
52
+ /**
53
+ * Configures the localization extension for the editor.
54
+ *
55
+ * @example
56
+ * // Full-featured (default): Multi-item translation with subitem discovery
57
+ * configureLocalization(configuration);
58
+ * // or explicitly:
59
+ * configureLocalization(configuration, { multiItem: true });
60
+ *
61
+ * @example
62
+ * // Single-item only: Restricts translation to one item at a time
63
+ * configureLocalization(configuration, { multiItem: false });
64
+ *
65
+ * @param configuration - The editor configuration object
66
+ * @param options - Optional configuration for localization features
67
+ */
68
+ export declare function configureLocalization(configuration: EditorConfiguration, options?: LocalizationConfigOptions): EditorConfiguration;
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAOtD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,gBAAgB,CAAC;AAEzE,QAAA,MAAM,wCAAwC;;;;;;;;;;;;8BAalB,qBAAqB;;CAGhD,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;CAUjD,CAAC;AAEF,OAAO,EAAE,wCAAwC,EAAE,4CAA4C,EAAE,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,mBAAmB,EAClC,OAAO,CAAC,EAAE,yBAAyB,uBAyGpC"}