@parhelia/localization 0.1.11455 → 0.1.11458

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 (61) hide show
  1. package/dist/dist/LocalizeItemCommand.d.ts +8 -0
  2. package/dist/dist/LocalizeItemCommand.d.ts.map +1 -0
  3. package/dist/dist/LocalizeItemCommand.js +48 -0
  4. package/dist/dist/LocalizeItemDialog.d.ts +4 -0
  5. package/dist/dist/LocalizeItemDialog.d.ts.map +1 -0
  6. package/dist/dist/LocalizeItemDialog.js +129 -0
  7. package/dist/dist/LocalizeItemUtils.d.ts +17 -0
  8. package/dist/dist/LocalizeItemUtils.d.ts.map +1 -0
  9. package/dist/dist/LocalizeItemUtils.js +93 -0
  10. package/dist/dist/api/discovery.d.ts +36 -0
  11. package/dist/dist/api/discovery.d.ts.map +1 -0
  12. package/dist/dist/api/discovery.js +29 -0
  13. package/dist/dist/constants.d.ts +15 -0
  14. package/dist/dist/constants.d.ts.map +1 -0
  15. package/dist/dist/constants.js +21 -0
  16. package/dist/dist/hooks/useTranslationWizard.d.ts +6 -0
  17. package/dist/dist/hooks/useTranslationWizard.d.ts.map +1 -0
  18. package/dist/dist/hooks/useTranslationWizard.js +78 -0
  19. package/dist/dist/index.d.ts +69 -0
  20. package/dist/dist/index.d.ts.map +1 -0
  21. package/dist/dist/index.js +152 -0
  22. package/dist/dist/services/translationService.d.ts +102 -0
  23. package/dist/dist/services/translationService.d.ts.map +1 -0
  24. package/dist/dist/services/translationService.js +37 -0
  25. package/dist/dist/setup/LocalizationSetupStep.d.ts +3 -0
  26. package/dist/dist/setup/LocalizationSetupStep.d.ts.map +1 -0
  27. package/dist/dist/setup/LocalizationSetupStep.js +115 -0
  28. package/dist/dist/sidebar/TranslationSidebar.d.ts +2 -0
  29. package/dist/dist/sidebar/TranslationSidebar.d.ts.map +1 -0
  30. package/dist/dist/sidebar/TranslationSidebar.js +95 -0
  31. package/dist/dist/steps/MetadataInputStep.d.ts +4 -0
  32. package/dist/dist/steps/MetadataInputStep.d.ts.map +1 -0
  33. package/dist/dist/steps/MetadataInputStep.js +38 -0
  34. package/dist/dist/steps/ServiceLanguageSelectionStep.d.ts +3 -0
  35. package/dist/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -0
  36. package/dist/dist/steps/ServiceLanguageSelectionStep.js +120 -0
  37. package/dist/dist/steps/SubitemDiscoveryStep.d.ts +3 -0
  38. package/dist/dist/steps/SubitemDiscoveryStep.d.ts.map +1 -0
  39. package/dist/dist/steps/SubitemDiscoveryStep.js +415 -0
  40. package/dist/dist/steps/index.d.ts +5 -0
  41. package/dist/dist/steps/index.d.ts.map +1 -0
  42. package/dist/dist/steps/index.js +4 -0
  43. package/dist/dist/steps/types.d.ts +68 -0
  44. package/dist/dist/steps/types.d.ts.map +1 -0
  45. package/dist/dist/steps/types.js +1 -0
  46. package/dist/dist/translation-center/BatchTranslationView.d.ts +7 -0
  47. package/dist/dist/translation-center/BatchTranslationView.d.ts.map +1 -0
  48. package/dist/dist/translation-center/BatchTranslationView.js +507 -0
  49. package/dist/dist/translation-center/RecentTranslations.d.ts +2 -0
  50. package/dist/dist/translation-center/RecentTranslations.d.ts.map +1 -0
  51. package/dist/dist/translation-center/RecentTranslations.js +195 -0
  52. package/dist/dist/translation-center/TranslationManagement.d.ts +2 -0
  53. package/dist/dist/translation-center/TranslationManagement.d.ts.map +1 -0
  54. package/dist/dist/translation-center/TranslationManagement.js +25 -0
  55. package/dist/dist/types.d.ts +18 -0
  56. package/dist/dist/types.d.ts.map +1 -0
  57. package/dist/dist/types.js +1 -0
  58. package/dist/dist/utils/createVersions.d.ts +14 -0
  59. package/dist/dist/utils/createVersions.d.ts.map +1 -0
  60. package/dist/dist/utils/createVersions.js +26 -0
  61. package/package.json +1 -1
@@ -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":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAUhF,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,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Globe as LucideGlobe } from "lucide-react";
4
+ import { LocalizeItemDialog } from "./LocalizeItemDialog";
5
+ // Icon wrapper to avoid React 19 JSX type issues with forwardRef components.
6
+ // Use React.createElement so we don't call the forwardRef objects as functions.
7
+ const GlobeIcon = (props) => React.createElement(LucideGlobe, props);
8
+ export const localizeItemCommand = {
9
+ id: "localizeItem",
10
+ label: "Localize",
11
+ icon: _jsx(GlobeIcon, { strokeWidth: 1 }),
12
+ disabled: (context) => {
13
+ if (!context.data?.items || context.data.items.length === 0) {
14
+ return true;
15
+ }
16
+ // Check if multi-item is disabled and multiple items are selected
17
+ const multiItemEnabled = context.editContext?.configuration?.localization?.multiItem !== false;
18
+ if (!multiItemEnabled && context.data.items.length > 1) {
19
+ return true; // Disable command when multiple items selected in single-item mode
20
+ }
21
+ return false;
22
+ },
23
+ execute: async (context) => {
24
+ const items = context.data?.items;
25
+ if (!items || !items?.length) {
26
+ return;
27
+ }
28
+ const result = await context.openDialog(LocalizeItemDialog, {
29
+ items: items,
30
+ editContext: context.editContext,
31
+ });
32
+ // If translation was started (result contains batchId), navigate to the batch view
33
+ if (result?.batchId) {
34
+ const translationManagementEnabled = context.editContext?.configuration?.localization?.translationManagement !== false;
35
+ if (translationManagementEnabled) {
36
+ // Synchronous URL update to avoid race with view URL sync
37
+ const params = new URLSearchParams(window.location.search);
38
+ params.set("view", "translation-management");
39
+ params.set("batchId", result.batchId);
40
+ const newUrl = `${window.location.pathname}?${params.toString()}`;
41
+ window.history.pushState(null, "", newUrl);
42
+ // Now switch the view; URL already contains batchId
43
+ context.editContext.switchView("translation-management");
44
+ }
45
+ }
46
+ return result;
47
+ },
48
+ };
@@ -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;AAgBvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA8PtE"}
@@ -0,0 +1,129 @@
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 as LucideChevronRight } from "lucide-react";
5
+ import { useTranslationWizard } from "./hooks/useTranslationWizard";
6
+ import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
7
+ // Wrapper so React 19 sees a plain function component instead of a forwardRef exotic component
8
+ const ChevronRightIcon = (props) => LucideChevronRight(props);
9
+ // Note: DialogButtons is an internal component that might need to be added to core exports
10
+ // For now, we'll implement it inline
11
+ 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 }));
12
+ export function LocalizeItemDialog(props) {
13
+ const editContext = props.editContext;
14
+ const configuration = editContext.configuration.translationWizard;
15
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
16
+ const [completedByStepId, setCompletedByStepId] = useState({});
17
+ const [beforeNextCallback, setBeforeNextCallback] = useState(null);
18
+ const { wizardData, setWizardData } = useTranslationWizard(props);
19
+ // Keep a ref to the latest wizard data to avoid stale closure issues
20
+ const latestWizardDataRef = useRef(wizardData);
21
+ latestWizardDataRef.current = wizardData;
22
+ // Clear footer actions when step changes to avoid stale actions from previous steps
23
+ const [footerActions, setFooterActions] = useState([]);
24
+ useEffect(() => {
25
+ setFooterActions([]);
26
+ }, [currentStepIndex]);
27
+ // Provide stable callbacks to steps to avoid update loops
28
+ const lastActionsSigRef = useRef("");
29
+ const provideFooterActions = useCallback((actions) => {
30
+ const normalized = (actions || []).map(a => ({ key: a.key, label: a.label, disabled: !!a.disabled, signature: a.signature || "" }));
31
+ const signature = JSON.stringify(normalized);
32
+ if (signature === lastActionsSigRef.current)
33
+ return;
34
+ lastActionsSigRef.current = signature;
35
+ setFooterActions(actions || []);
36
+ }, []);
37
+ const requestCloseCb = useCallback((result) => {
38
+ props.onClose?.(result);
39
+ }, [props.onClose]);
40
+ // Filter steps based on skip conditions
41
+ const activeSteps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
42
+ const currentStep = activeSteps[currentStepIndex];
43
+ const isLastStep = currentStepIndex === activeSteps.length - 1;
44
+ const canProceed = !!(currentStep && completedByStepId[currentStep.id]);
45
+ const handleNext = async () => {
46
+ if (beforeNextCallback && typeof beforeNextCallback === 'function') {
47
+ const canProceed = await beforeNextCallback();
48
+ if (!canProceed)
49
+ return;
50
+ }
51
+ if (currentStep?.beforeNext) {
52
+ const canProceed = await currentStep.beforeNext(wizardData, editContext);
53
+ if (!canProceed)
54
+ return;
55
+ }
56
+ if (isLastStep) {
57
+ await handleFinish();
58
+ }
59
+ else {
60
+ setCurrentStepIndex(currentStepIndex + 1);
61
+ }
62
+ };
63
+ const handlePrevious = () => {
64
+ if (currentStepIndex > 0) {
65
+ // Clear the callback when moving to previous step
66
+ setBeforeNextCallback(null);
67
+ setCurrentStepIndex(currentStepIndex - 1);
68
+ }
69
+ };
70
+ const handleFinish = useCallback(async () => {
71
+ try {
72
+ // Use the ref to get the absolute latest wizard data
73
+ const currentWizardData = latestWizardDataRef.current;
74
+ const result = {
75
+ translationProvider: currentWizardData.translationProvider,
76
+ targetLanguages: currentWizardData.targetLanguages,
77
+ includeSubitems: currentWizardData.includeSubitems,
78
+ discoveredItems: currentWizardData.discoveredItems,
79
+ };
80
+ if (currentWizardData.translationProvider === "Create Versions") {
81
+ props.onClose?.(result);
82
+ }
83
+ else {
84
+ try {
85
+ const batchId = generateBatchId();
86
+ const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
87
+ console.log("Translation completed successfully:", translationResult);
88
+ // Include batchId in result for navigation to translation management
89
+ const resultWithBatch = { ...result, batchId };
90
+ // Close dialog and let parent component handle navigation to translation management
91
+ props.onClose?.(resultWithBatch);
92
+ }
93
+ catch (error) {
94
+ console.error("Translation failed:", error);
95
+ // Still close the dialog even on error
96
+ props.onClose?.(result);
97
+ }
98
+ }
99
+ }
100
+ catch (error) {
101
+ console.error("Error in handleFinish:", error);
102
+ props.onClose?.(null);
103
+ }
104
+ }, [editContext, props]);
105
+ const handleStepCompleted = useCallback((completed) => {
106
+ if (!currentStep)
107
+ return;
108
+ setCompletedByStepId(prev => ({ ...prev, [currentStep.id]: completed }));
109
+ }, [currentStep?.id]);
110
+ const StepComponent = currentStep?.component;
111
+ return (_jsx(Dialog, { open: true, onOpenChange: (open) => {
112
+ // Only close when explicitly requested (e.g., close button clicked)
113
+ if (!open) {
114
+ props.onClose?.(null);
115
+ }
116
+ }, 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: {
117
+ width: 'min(90vw, 1280px)',
118
+ height: 'min(85vh, 900px)',
119
+ minHeight: '700px'
120
+ }, 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 &&
121
+ "border-blue-500 text-blue-500 bg-blue-50", currentStepIndex < index && "border-gray-300 text-gray-400", currentStepIndex > index &&
122
+ "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
123
+ ? "text-blue-600 font-medium"
124
+ : currentStepIndex > index
125
+ ? "text-blue-600"
126
+ : "text-gray-400"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRightIcon, { 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) => {
127
+ setBeforeNextCallback(() => cb);
128
+ }, setFooterActions: provideFooterActions, requestClose: requestCloseCb }) })) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", "data-testid": "translation-wizard-cancel-button", children: "Cancel" }), currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", "data-testid": "translation-wizard-previous-button", children: "Previous" })), 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))), _jsx(Button, { onClick: handleNext, disabled: !canProceed, variant: "default", size: "default", "data-testid": "translation-wizard-next-button", children: isLastStep ? "Start Translation" : "Next" })] })] })] }) }));
129
+ }
@@ -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":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAWtD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,gBAAgB,CAAC;AASzE,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,uBAmGpC"}