@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.
- package/LICENSE +8 -0
- package/README.md +29 -0
- package/dist/core/src/editor/ui/DragPreview.d.ts +15 -0
- package/dist/core/src/editor/ui/DragPreview.d.ts.map +1 -0
- package/dist/core/src/editor/ui/DragPreview.js +32 -0
- package/dist/core/src/editor/ui/PerfectTree.d.ts +79 -0
- package/dist/core/src/editor/ui/PerfectTree.d.ts.map +1 -0
- package/dist/core/src/editor/ui/PerfectTree.js +857 -0
- package/dist/localization/src/LocalizeItemCommand.d.ts +8 -0
- package/dist/localization/src/LocalizeItemCommand.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemCommand.js +44 -0
- package/dist/localization/src/LocalizeItemDialog.d.ts +4 -0
- package/dist/localization/src/LocalizeItemDialog.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemDialog.js +126 -0
- package/dist/localization/src/LocalizeItemUtils.d.ts +17 -0
- package/dist/localization/src/LocalizeItemUtils.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemUtils.js +93 -0
- package/dist/localization/src/api/discovery.d.ts +36 -0
- package/dist/localization/src/api/discovery.d.ts.map +1 -0
- package/dist/localization/src/api/discovery.js +29 -0
- package/dist/localization/src/constants.d.ts +15 -0
- package/dist/localization/src/constants.d.ts.map +1 -0
- package/dist/localization/src/constants.js +21 -0
- package/dist/localization/src/hooks/useTranslationWizard.d.ts +6 -0
- package/dist/localization/src/hooks/useTranslationWizard.d.ts.map +1 -0
- package/dist/localization/src/hooks/useTranslationWizard.js +78 -0
- package/dist/localization/src/index.d.ts +69 -0
- package/dist/localization/src/index.d.ts.map +1 -0
- package/dist/localization/src/index.js +152 -0
- package/dist/localization/src/services/translationService.d.ts +102 -0
- package/dist/localization/src/services/translationService.d.ts.map +1 -0
- package/dist/localization/src/services/translationService.js +37 -0
- package/dist/localization/src/setup/LocalizationSetupStep.d.ts +3 -0
- package/dist/localization/src/setup/LocalizationSetupStep.d.ts.map +1 -0
- package/dist/localization/src/setup/LocalizationSetupStep.js +108 -0
- package/dist/localization/src/sidebar/TranslationSidebar.d.ts +2 -0
- package/dist/localization/src/sidebar/TranslationSidebar.d.ts.map +1 -0
- package/dist/localization/src/sidebar/TranslationSidebar.js +93 -0
- package/dist/localization/src/steps/MetadataInputStep.d.ts +4 -0
- package/dist/localization/src/steps/MetadataInputStep.d.ts.map +1 -0
- package/dist/localization/src/steps/MetadataInputStep.js +38 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts +3 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts.map +1 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.js +91 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts +3 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts.map +1 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.js +391 -0
- package/dist/localization/src/steps/index.d.ts +5 -0
- package/dist/localization/src/steps/index.d.ts.map +1 -0
- package/dist/localization/src/steps/index.js +4 -0
- package/dist/localization/src/steps/types.d.ts +68 -0
- package/dist/localization/src/steps/types.d.ts.map +1 -0
- package/dist/localization/src/steps/types.js +1 -0
- package/dist/localization/src/translation-center/BatchTranslationView.d.ts +7 -0
- package/dist/localization/src/translation-center/BatchTranslationView.d.ts.map +1 -0
- package/dist/localization/src/translation-center/BatchTranslationView.js +487 -0
- package/dist/localization/src/translation-center/RecentTranslations.d.ts +2 -0
- package/dist/localization/src/translation-center/RecentTranslations.d.ts.map +1 -0
- package/dist/localization/src/translation-center/RecentTranslations.js +199 -0
- package/dist/localization/src/translation-center/TranslationManagement.d.ts +2 -0
- package/dist/localization/src/translation-center/TranslationManagement.d.ts.map +1 -0
- package/dist/localization/src/translation-center/TranslationManagement.js +25 -0
- package/dist/localization/src/types.d.ts +18 -0
- package/dist/localization/src/types.d.ts.map +1 -0
- package/dist/localization/src/types.js +1 -0
- package/dist/localization/src/utils/createVersions.d.ts +14 -0
- package/dist/localization/src/utils/createVersions.d.ts.map +1 -0
- package/dist/localization/src/utils/createVersions.js +26 -0
- package/package.json +47 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
|
|
3
|
+
import { SubitemDiscoveryStep } from "./steps/SubitemDiscoveryStep";
|
|
4
|
+
import { localizeItemCommand } from "./LocalizeItemCommand";
|
|
5
|
+
import { TranslationSidebar } from "./sidebar/TranslationSidebar";
|
|
6
|
+
import { TranslationManagement } from "./translation-center/TranslationManagement";
|
|
7
|
+
import { RecentTranslations } from "./translation-center/RecentTranslations";
|
|
8
|
+
import { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
|
|
9
|
+
import { Globe, Languages } from "lucide-react";
|
|
10
|
+
import { pageEditorViewBase } from "@parhelia/core";
|
|
11
|
+
const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
12
|
+
steps: [
|
|
13
|
+
{
|
|
14
|
+
id: "service-language-selection",
|
|
15
|
+
name: "Configure Translation",
|
|
16
|
+
description: "Select translation provider and target languages for your content.",
|
|
17
|
+
component: ServiceLanguageSelectionStep,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "subitem-discovery",
|
|
21
|
+
name: "Discover Subitems",
|
|
22
|
+
description: "Find and include child items that should also be translated.",
|
|
23
|
+
component: SubitemDiscoveryStep,
|
|
24
|
+
skipCondition: (data) => !data.includeSubitems,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
const SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
29
|
+
steps: [
|
|
30
|
+
{
|
|
31
|
+
id: "service-language-selection",
|
|
32
|
+
name: "Configure Translation",
|
|
33
|
+
description: "Select translation provider and target languages for your content.",
|
|
34
|
+
component: ServiceLanguageSelectionStep,
|
|
35
|
+
},
|
|
36
|
+
// No SubitemDiscoveryStep - single item translation only
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
export { DEFAULT_TRANSLATION_WIZARD_CONFIGURATION, SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION };
|
|
40
|
+
export { LocalizeItemDialog } from "./LocalizeItemDialog";
|
|
41
|
+
export { localizeItemCommand } from "./LocalizeItemCommand";
|
|
42
|
+
export { TranslationManagement } from "./translation-center/TranslationManagement";
|
|
43
|
+
export { RecentTranslations } from "./translation-center/RecentTranslations";
|
|
44
|
+
export { BatchTranslationView } from "./translation-center/BatchTranslationView";
|
|
45
|
+
export { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
|
|
46
|
+
export { defaultTranslateAll, generateBatchId, performDefaultTranslation } from "./LocalizeItemUtils";
|
|
47
|
+
export * from "./steps/types";
|
|
48
|
+
/**
|
|
49
|
+
* Configures the localization extension for the editor.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // Full-featured (default): Multi-item translation with subitem discovery
|
|
53
|
+
* configureLocalization(configuration);
|
|
54
|
+
* // or explicitly:
|
|
55
|
+
* configureLocalization(configuration, { multiItem: true });
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // Single-item only: Restricts translation to one item at a time
|
|
59
|
+
* configureLocalization(configuration, { multiItem: false });
|
|
60
|
+
*
|
|
61
|
+
* @param configuration - The editor configuration object
|
|
62
|
+
* @param options - Optional configuration for localization features
|
|
63
|
+
*/
|
|
64
|
+
export function configureLocalization(configuration, options) {
|
|
65
|
+
const enableMultiItem = options?.multiItem !== false; // Default to true
|
|
66
|
+
// Add localization setup step
|
|
67
|
+
if (!configuration.setup) {
|
|
68
|
+
configuration.setup = { steps: [] };
|
|
69
|
+
}
|
|
70
|
+
// Check if the step is already added (to prevent duplicates)
|
|
71
|
+
const hasLocalizationSetup = configuration.setup.steps.some((step) => step === LocalizationSetupStep || step.name === "LocalizationSetupStep");
|
|
72
|
+
if (!hasLocalizationSetup) {
|
|
73
|
+
configuration.setup.steps.push(LocalizationSetupStep);
|
|
74
|
+
}
|
|
75
|
+
// Initialize the translation wizard configuration if it doesn't exist
|
|
76
|
+
if (!configuration.translationWizard) {
|
|
77
|
+
configuration.translationWizard = enableMultiItem
|
|
78
|
+
? DEFAULT_TRANSLATION_WIZARD_CONFIGURATION
|
|
79
|
+
: SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION;
|
|
80
|
+
}
|
|
81
|
+
// Store settings in configuration for other components to access
|
|
82
|
+
if (!configuration.localization) {
|
|
83
|
+
configuration.localization = {};
|
|
84
|
+
}
|
|
85
|
+
configuration.localization.multiItem = enableMultiItem;
|
|
86
|
+
configuration.localization.translationManagement = options?.translationManagement !== false;
|
|
87
|
+
if (!configuration.commands.allItemCommands) {
|
|
88
|
+
configuration.commands.allItemCommands = [];
|
|
89
|
+
}
|
|
90
|
+
// Check if the command is already added (to prevent duplicates)
|
|
91
|
+
const hasLocalizeCommand = configuration.commands.allItemCommands.some((cmd) => cmd.id === "localizeItem");
|
|
92
|
+
if (!hasLocalizeCommand) {
|
|
93
|
+
configuration.commands.allItemCommands.push(localizeItemCommand);
|
|
94
|
+
}
|
|
95
|
+
// Add Translation view to the editor views
|
|
96
|
+
if (configuration.editor && configuration.editor.views) {
|
|
97
|
+
const hasTranslateView = configuration.editor.views.some((view) => view.name === "translate");
|
|
98
|
+
if (!hasTranslateView) {
|
|
99
|
+
configuration.editor.views.push({
|
|
100
|
+
name: "translate",
|
|
101
|
+
icon: _jsx(Languages, { strokeWidth: 1 }),
|
|
102
|
+
title: "Translate",
|
|
103
|
+
leftSidebar: {
|
|
104
|
+
panels: [
|
|
105
|
+
{
|
|
106
|
+
name: "translation",
|
|
107
|
+
title: "Languages & Versions",
|
|
108
|
+
content: _jsx(TranslationSidebar, {}),
|
|
109
|
+
initialSize: 20,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
...pageEditorViewBase,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Add Translation Management view
|
|
118
|
+
if (options?.translationManagement !== false && configuration.editor && configuration.editor.views) {
|
|
119
|
+
const hasTranslationManagementView = configuration.editor.views.some((view) => view.name === "translation-management");
|
|
120
|
+
if (!hasTranslationManagementView) {
|
|
121
|
+
configuration.editor.views.push({
|
|
122
|
+
name: "translation-management",
|
|
123
|
+
icon: _jsx(Globe, { strokeWidth: 1 }),
|
|
124
|
+
title: "Translation Management",
|
|
125
|
+
defaultCenterPanelView: _jsx(TranslationManagement, {}),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Configure translation center with Recent Translations panel
|
|
130
|
+
if (!configuration.translationCenter) {
|
|
131
|
+
configuration.translationCenter = {
|
|
132
|
+
groups: []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Add recent translations panel to translation center
|
|
136
|
+
const hasRecentTranslationsGroup = configuration.translationCenter.groups?.some((group) => group.panels?.some((panel) => panel.id === "recent-translations"));
|
|
137
|
+
if (!hasRecentTranslationsGroup) {
|
|
138
|
+
configuration.translationCenter.groups.push({
|
|
139
|
+
title: "Translation Tracking",
|
|
140
|
+
icon: _jsx(Languages, { strokeWidth: 1 }),
|
|
141
|
+
panels: [
|
|
142
|
+
{
|
|
143
|
+
id: "recent-translations",
|
|
144
|
+
title: "Recent Translations",
|
|
145
|
+
icon: _jsx(Languages, { strokeWidth: 1 }),
|
|
146
|
+
content: _jsx(RecentTranslations, {}),
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return configuration;
|
|
152
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { TranslationStatus } from "../types";
|
|
2
|
+
import { TranslationProviderInfo } from "../steps/types";
|
|
3
|
+
export type TranslationRequestResponse = {
|
|
4
|
+
jobId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function requestTranslation({ itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId }: {
|
|
7
|
+
itemId: string;
|
|
8
|
+
targetLanguage: string;
|
|
9
|
+
sourceLanguage: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
metadata: string;
|
|
13
|
+
batchId?: string;
|
|
14
|
+
}): Promise<import("@parhelia/core").ExecutionResult<TranslationRequestResponse>>;
|
|
15
|
+
export declare function getTranslationStatus(itemId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationStatus[]>>;
|
|
16
|
+
export declare function getTranslationProviders(): Promise<import("@parhelia/core").ExecutionResult<TranslationProviderInfo[]>>;
|
|
17
|
+
export type AvailableTranslationService = {
|
|
18
|
+
serviceName: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
isConfigured: boolean;
|
|
21
|
+
supportedLanguages?: string[];
|
|
22
|
+
settingsItemId?: string;
|
|
23
|
+
templateId: string;
|
|
24
|
+
templateName: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function getAvailableTranslationServices(): Promise<import("@parhelia/core").ExecutionResult<AvailableTranslationService[]>>;
|
|
27
|
+
export declare function createProviderSettings(serviceName: string, templateId: string, language?: string): Promise<import("@parhelia/core").ExecutionResult<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
itemId: string;
|
|
30
|
+
itemPath: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}>>;
|
|
33
|
+
export type TranslationJobRecord = {
|
|
34
|
+
itemId: string;
|
|
35
|
+
targetLanguage: string;
|
|
36
|
+
sourceLanguage: string;
|
|
37
|
+
status: string;
|
|
38
|
+
timestamp: string;
|
|
39
|
+
hash?: string;
|
|
40
|
+
message?: string;
|
|
41
|
+
batchId?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function listTranslationJobs(take?: number, skip?: number): Promise<import("@parhelia/core").ExecutionResult<TranslationJobRecord[]>>;
|
|
44
|
+
export declare function listBatchTranslationJobs(batchId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationJobRecord[]>>;
|
|
45
|
+
export type TranslationBatchInfo = {
|
|
46
|
+
batchId: string;
|
|
47
|
+
createdAtUtc?: string;
|
|
48
|
+
startedAtUtc?: string;
|
|
49
|
+
completedAtUtc?: string;
|
|
50
|
+
initiatedByUser?: string;
|
|
51
|
+
initiatorSessionId?: string;
|
|
52
|
+
provider?: string;
|
|
53
|
+
status?: string;
|
|
54
|
+
expectedJobs?: number;
|
|
55
|
+
completedJobs?: number;
|
|
56
|
+
errorJobs?: number;
|
|
57
|
+
includeSubitems?: boolean | null;
|
|
58
|
+
scopeItemId?: string | null;
|
|
59
|
+
scopeItemPath?: string | null;
|
|
60
|
+
metadata?: string | null;
|
|
61
|
+
lastUpdatedUtc?: string;
|
|
62
|
+
exists?: boolean;
|
|
63
|
+
};
|
|
64
|
+
export declare function getBatchInfo(batchId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationBatchInfo>>;
|
|
65
|
+
export declare function listBatches(take?: number, skip?: number): Promise<import("@parhelia/core").ExecutionResult<TranslationBatchInfo[]>>;
|
|
66
|
+
export declare function subscribeToBatch(batchId: string, sessionId: string): Promise<import("@parhelia/core").ExecutionResult<{
|
|
67
|
+
success: boolean;
|
|
68
|
+
message: string;
|
|
69
|
+
}>>;
|
|
70
|
+
export declare function unsubscribeFromBatch(batchId: string, sessionId: string): Promise<import("@parhelia/core").ExecutionResult<{
|
|
71
|
+
success: boolean;
|
|
72
|
+
message: string;
|
|
73
|
+
}>>;
|
|
74
|
+
export type BatchTranslationItem = {
|
|
75
|
+
itemId: string;
|
|
76
|
+
targetLanguage: string;
|
|
77
|
+
sourceLanguage: string;
|
|
78
|
+
metadata?: string;
|
|
79
|
+
};
|
|
80
|
+
export type BatchTranslationResult = {
|
|
81
|
+
itemId: string;
|
|
82
|
+
targetLanguage: string;
|
|
83
|
+
jobId: string;
|
|
84
|
+
};
|
|
85
|
+
export type BatchTranslationError = {
|
|
86
|
+
itemId: string;
|
|
87
|
+
targetLanguage: string;
|
|
88
|
+
error: string;
|
|
89
|
+
};
|
|
90
|
+
export type BatchTranslationResponse = {
|
|
91
|
+
batchId: string;
|
|
92
|
+
started: BatchTranslationResult[];
|
|
93
|
+
skipped: BatchTranslationError[];
|
|
94
|
+
};
|
|
95
|
+
export declare function requestBatchTranslation({ sessionId, batchId, provider, batchMetadata, translations }: {
|
|
96
|
+
sessionId: string;
|
|
97
|
+
batchId: string;
|
|
98
|
+
provider: string;
|
|
99
|
+
batchMetadata?: string;
|
|
100
|
+
translations: BatchTranslationItem[];
|
|
101
|
+
}): Promise<import("@parhelia/core").ExecutionResult<BatchTranslationResponse>>;
|
|
102
|
+
//# sourceMappingURL=translationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translationService.d.ts","sourceRoot":"","sources":["../../../../src/services/translationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { get, post } from "@parhelia/core";
|
|
2
|
+
export async function requestTranslation({ itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId }) {
|
|
3
|
+
return await post("/parhelia/translation/requestTranslation", { itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId });
|
|
4
|
+
}
|
|
5
|
+
export async function getTranslationStatus(itemId) {
|
|
6
|
+
return await get(`/parhelia/translation/getTranslations?itemId=${itemId}`);
|
|
7
|
+
}
|
|
8
|
+
export async function getTranslationProviders() {
|
|
9
|
+
return await get(`/parhelia/translation/providers`);
|
|
10
|
+
}
|
|
11
|
+
export async function getAvailableTranslationServices() {
|
|
12
|
+
return await get(`/parhelia/translation/availableServices`);
|
|
13
|
+
}
|
|
14
|
+
export async function createProviderSettings(serviceName, templateId, language = "en") {
|
|
15
|
+
return await post("/parhelia/translation/createProviderSettings", { serviceName, templateId, language });
|
|
16
|
+
}
|
|
17
|
+
export async function listTranslationJobs(take = 100, skip = 0) {
|
|
18
|
+
return await get(`/parhelia/translation/jobs?take=${take}&skip=${skip}`);
|
|
19
|
+
}
|
|
20
|
+
export async function listBatchTranslationJobs(batchId) {
|
|
21
|
+
return await get(`/parhelia/translation/batchJobs?batchId=${encodeURIComponent(batchId)}`);
|
|
22
|
+
}
|
|
23
|
+
export async function getBatchInfo(batchId) {
|
|
24
|
+
return await get(`/parhelia/translation/batchInfo?batchId=${encodeURIComponent(batchId)}`);
|
|
25
|
+
}
|
|
26
|
+
export async function listBatches(take = 100, skip = 0) {
|
|
27
|
+
return await get(`/parhelia/translation/batches?take=${take}&skip=${skip}`);
|
|
28
|
+
}
|
|
29
|
+
export async function subscribeToBatch(batchId, sessionId) {
|
|
30
|
+
return await post("/parhelia/translation/subscribeToBatch", { batchId, sessionId });
|
|
31
|
+
}
|
|
32
|
+
export async function unsubscribeFromBatch(batchId, sessionId) {
|
|
33
|
+
return await post("/parhelia/translation/unsubscribeFromBatch", { batchId, sessionId });
|
|
34
|
+
}
|
|
35
|
+
export async function requestBatchTranslation({ sessionId, batchId, provider, batchMetadata, translations }) {
|
|
36
|
+
return await post("/parhelia/translation/requestBatchTranslation", { sessionId, batchId, provider, batchMetadata: batchMetadata || "", translations });
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,4CA+MpC;AAED,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useState } from "react";
|
|
3
|
+
import { Card, Button, useEditContext } from "@parhelia/core";
|
|
4
|
+
import { CheckCircle, AlertCircle, Languages, RefreshCw, Plus } from "lucide-react";
|
|
5
|
+
import { getAvailableTranslationServices, createProviderSettings } from "../services/translationService";
|
|
6
|
+
export function LocalizationSetupStep() {
|
|
7
|
+
const editContext = useEditContext();
|
|
8
|
+
const [state, setState] = useState("checking");
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const [services, setServices] = useState([]);
|
|
11
|
+
const statusIcon = useCallback((state) => {
|
|
12
|
+
if (state === "success")
|
|
13
|
+
return _jsx(CheckCircle, { className: "h-4 w-4 text-green-600", strokeWidth: 1 });
|
|
14
|
+
if (state === "error")
|
|
15
|
+
return _jsx(AlertCircle, { className: "h-4 w-4 text-red-600", strokeWidth: 1 });
|
|
16
|
+
if (state === "partial")
|
|
17
|
+
return _jsx(AlertCircle, { className: "h-4 w-4 text-amber-600", strokeWidth: 1 });
|
|
18
|
+
return (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin text-amber-600", strokeWidth: 1 }));
|
|
19
|
+
}, []);
|
|
20
|
+
const checkLocalization = useCallback(async () => {
|
|
21
|
+
try {
|
|
22
|
+
setState("checking");
|
|
23
|
+
setError(null);
|
|
24
|
+
setServices([]);
|
|
25
|
+
const result = await getAvailableTranslationServices();
|
|
26
|
+
if (result.type === "success" && result.data) {
|
|
27
|
+
// Backend returns ApiResponse<T>, so data is wrapped: { success: true, data: [...] }
|
|
28
|
+
const availableServices = Array.isArray(result.data) ? result.data : result.data.data || [];
|
|
29
|
+
if (availableServices.length === 0) {
|
|
30
|
+
setState("error");
|
|
31
|
+
setError("No translation services registered in dependency injection.");
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const serviceStates = availableServices.map((svc) => ({
|
|
35
|
+
...svc,
|
|
36
|
+
creating: false
|
|
37
|
+
}));
|
|
38
|
+
setServices(serviceStates);
|
|
39
|
+
const configuredCount = serviceStates.filter(s => s.isConfigured).length;
|
|
40
|
+
const totalCount = serviceStates.length;
|
|
41
|
+
if (configuredCount === 0) {
|
|
42
|
+
setState("error");
|
|
43
|
+
setError(`Found ${totalCount} translation service${totalCount !== 1 ? 's' : ''} in DI, but none have Sitecore Settings items configured.`);
|
|
44
|
+
}
|
|
45
|
+
else if (configuredCount < totalCount) {
|
|
46
|
+
setState("partial");
|
|
47
|
+
// Don't set error message - the status is already shown in the header
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
setState("success");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
setState("error");
|
|
56
|
+
setError(result.details || "Failed to check translation services. The translation API may not be available.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
setState("error");
|
|
61
|
+
setError(e?.message || "Failed to check localization setup");
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
64
|
+
const createSettings = useCallback(async (serviceName, templateId) => {
|
|
65
|
+
try {
|
|
66
|
+
setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: true } : s));
|
|
67
|
+
setError(null);
|
|
68
|
+
const language = editContext?.contentEditorItem?.language || "en";
|
|
69
|
+
const result = await createProviderSettings(serviceName, templateId, language);
|
|
70
|
+
if (result.type === "success" && result.data) {
|
|
71
|
+
// Backend returns ApiResponse<T>, so unwrap: { success: true, data: { itemId, itemPath } }
|
|
72
|
+
const response = result.data.success ? result.data.data : result.data;
|
|
73
|
+
const success = result.data.success !== false;
|
|
74
|
+
if (success && response) {
|
|
75
|
+
setServices(prev => prev.map(s => s.serviceName === serviceName
|
|
76
|
+
? { ...s, isConfigured: true, creating: false, settingsItemId: response.itemId }
|
|
77
|
+
: s));
|
|
78
|
+
setTimeout(() => checkLocalization(), 500);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error(result.data.error || result.details || "Failed to create settings");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
throw new Error(result.details || "Failed to create settings");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
setError(`Failed to create settings for ${serviceName}: ${e?.message || "Unknown error"}`);
|
|
90
|
+
setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: false } : s));
|
|
91
|
+
}
|
|
92
|
+
}, [editContext, checkLocalization]);
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
checkLocalization();
|
|
95
|
+
}, [checkLocalization]);
|
|
96
|
+
const configuredCount = services.filter(s => s.isConfigured).length;
|
|
97
|
+
const totalCount = services.length;
|
|
98
|
+
return (_jsx(Card, { icon: _jsx(Languages, { strokeWidth: 1, className: "h-5 w-5" }), title: "Localization setup", description: "Verifies that translation services registered in DI have corresponding Sitecore Settings items.", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: state === "success"
|
|
99
|
+
? `All ${totalCount} translation service${totalCount !== 1 ? 's' : ''} configured`
|
|
100
|
+
: state === "partial"
|
|
101
|
+
? `${configuredCount} of ${totalCount} services configured`
|
|
102
|
+
: state === "error" && totalCount > 0
|
|
103
|
+
? `${totalCount} service${totalCount !== 1 ? 's' : ''} found, none configured`
|
|
104
|
+
: state === "error"
|
|
105
|
+
? "No services found"
|
|
106
|
+
: "Checking..." })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: checkLocalization, children: [_jsx(RefreshCw, { strokeWidth: 1, className: "h-4 w-4" }), "Recheck"] })] }), services.length > 0 && (_jsxs("div", { className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-3", children: [_jsx("div", { className: "text-xs font-semibold text-gray-600 uppercase", children: "Translation Services" }), services.map((service) => (_jsxs("div", { className: "flex items-center justify-between rounded bg-white p-2 shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [service.isConfigured ? (_jsx(CheckCircle, { className: "h-3.5 w-3.5 text-green-600", strokeWidth: 1.5 })) : (_jsx(AlertCircle, { className: "h-3.5 w-3.5 text-amber-600", strokeWidth: 1.5 })), _jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-gray-800", children: service.displayName || service.serviceName }), service.displayName && service.displayName !== service.serviceName && (_jsxs("div", { className: "text-xs text-gray-500", children: ["Service: ", service.serviceName] })), service.isConfigured && service.supportedLanguages && service.supportedLanguages.length > 0 && (_jsxs("div", { className: "text-xs text-gray-500", children: ["Languages: ", service.supportedLanguages.join(", ")] }))] })] }), !service.isConfigured && (_jsxs(Button, { size: "xs", onClick: () => createSettings(service.serviceName, service.templateId), disabled: service.creating, title: `Create using template: ${service.templateName}`, children: [service.creating ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3 w-3 animate-spin" })) : (_jsx(Plus, { strokeWidth: 1.5, className: "h-3 w-3" })), "Create Settings"] }))] }, service.serviceName)))] })), error && (_jsx("div", { className: "rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800", children: error }))] }) }));
|
|
107
|
+
}
|
|
108
|
+
export default LocalizationSetupStep;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationSidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/TranslationSidebar.tsx"],"names":[],"mappings":"AAOA,wBAAgB,kBAAkB,mDA0KjC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, useEditContext, SimpleTable, confirmCreateVersion, Progress } from "@parhelia/core";
|
|
3
|
+
import { getTranslationStatus } from "../services/translationService";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { localizeItemCommand } from "../LocalizeItemCommand";
|
|
6
|
+
export function TranslationSidebar() {
|
|
7
|
+
const editContext = useEditContext();
|
|
8
|
+
if (!editContext)
|
|
9
|
+
return null;
|
|
10
|
+
const [translationStatus, setTranslationStatus] = useState(null);
|
|
11
|
+
const languages = editContext?.itemLanguages || [];
|
|
12
|
+
const [translationProgress, setTranslationProgress] = useState(new Map());
|
|
13
|
+
const item = editContext.contentEditorItem;
|
|
14
|
+
const selectLanguage = (language) => {
|
|
15
|
+
if (!item)
|
|
16
|
+
return;
|
|
17
|
+
if (!language.versions) {
|
|
18
|
+
confirmCreateVersion(editContext, language);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
editContext.loadItem({
|
|
22
|
+
...item.descriptor,
|
|
23
|
+
language: language.languageCode,
|
|
24
|
+
version: 0,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const languageName = (language) => {
|
|
28
|
+
return (_jsxs("div", { className: "flex items-center gap-2", onClick: () => selectLanguage(language), children: [_jsx("img", { src: language.icon, className: "h-5" }), " ", language.name] }, language.languageCode));
|
|
29
|
+
};
|
|
30
|
+
const loadTranslationStatus = async () => {
|
|
31
|
+
if (!item)
|
|
32
|
+
return;
|
|
33
|
+
const status = await getTranslationStatus(item.descriptor.id);
|
|
34
|
+
setTranslationStatus(status.data || []);
|
|
35
|
+
};
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
loadTranslationStatus();
|
|
38
|
+
setTranslationProgress(new Map());
|
|
39
|
+
}, [editContext.currentItemDescriptor?.id]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const removeSocketMessageListener = editContext.addSocketMessageListener((message) => {
|
|
42
|
+
if (message.type === "translation-started" || message.type === "translation-finished" || message.type === "translation-error") {
|
|
43
|
+
if (message.payload.itemId === item?.descriptor.id) {
|
|
44
|
+
loadTranslationStatus();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (message.type === "translation-progress") {
|
|
48
|
+
if (message.payload.itemId === item?.descriptor.id) {
|
|
49
|
+
setTranslationProgress((p) => {
|
|
50
|
+
p.set(message.payload.language, message.payload);
|
|
51
|
+
return new Map(p);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return removeSocketMessageListener;
|
|
57
|
+
}, [editContext, setTranslationProgress]);
|
|
58
|
+
const languageData = languages.map((x) => ({
|
|
59
|
+
...x,
|
|
60
|
+
translationStatus: translationStatus?.find((y) => y.targetLanguage === x.languageCode),
|
|
61
|
+
translationProgress: translationProgress.get(x.languageCode),
|
|
62
|
+
}));
|
|
63
|
+
languageData.sort((a, b) => {
|
|
64
|
+
// Sort languages with 0 versions to bottom
|
|
65
|
+
if (a.versions === 0 && b.versions > 0)
|
|
66
|
+
return 1;
|
|
67
|
+
if (b.versions === 0 && a.versions > 0)
|
|
68
|
+
return -1;
|
|
69
|
+
// Sort by name for languages that both have versions or both don't
|
|
70
|
+
return a.name.localeCompare(b.name);
|
|
71
|
+
});
|
|
72
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-2", "data-testid": "translation-sidebar", children: [_jsxs("div", { className: "flex w-full flex-wrap items-center justify-between gap-2 bg-gray-50 p-3", children: [_jsx("div", { className: "flex items-center gap-2 text-sm", children: _jsx("span", { className: "text-gray-500 font-medium", children: "Current Item Languages & Versions" }) }), item && (_jsxs(Button, { size: "sm", "data-testid": "translation-sidebar-localize-button", onClick: () => {
|
|
73
|
+
editContext.executeCommand({
|
|
74
|
+
command: localizeItemCommand,
|
|
75
|
+
data: {
|
|
76
|
+
items: [item],
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}, children: [_jsx("i", { className: "pi pi-plus" }), "Translate"] }))] }), _jsx("div", { className: "relative flex-1", children: _jsx("div", { className: "absolute inset-0 overflow-y-auto", children: _jsx(SimpleTable, { "data-testid": "translation-sidebar-language-table", columns: [
|
|
80
|
+
{ header: "Language", body: languageName },
|
|
81
|
+
{ header: "Ver.", body: (x) => x.versions.toString() },
|
|
82
|
+
{
|
|
83
|
+
header: "Src",
|
|
84
|
+
body: (x) => x.translationStatus?.sourceLanguage,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
header: "Status",
|
|
88
|
+
body: (x) => (_jsxs("div", { className: "flex items-center gap-2", "data-testid": `translation-status-${x.languageCode}`, children: [x.translationStatus?.status === "In Progress" && (_jsxs("div", { className: "relative w-full", children: [_jsx(Progress, { className: "h-5", value: (x.translationProgress?.progress || 0) * 100, showValue: false, "data-testid": `translation-progress-${x.languageCode}` }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center text-white text-xs", children: x.translationProgress?.message })] })), x.translationStatus?.status !== "In Progress" && (_jsx("div", { title: x.translationStatus?.message, children: x.translationStatus?.status || "Not started" }))] })),
|
|
89
|
+
},
|
|
90
|
+
], items: languageData, onRowClick: (data) => selectLanguage(data.item), rowClassName: (item) => item.languageCode == editContext.currentItemDescriptor?.language
|
|
91
|
+
? "bg-neutral-100"
|
|
92
|
+
: "" }) }) })] }));
|
|
93
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TranslationStepProps } from "./types";
|
|
2
|
+
/** Example Step for metadata input testing */
|
|
3
|
+
export declare function MetadataInputStep({ data, setData, onStepCompleted, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=MetadataInputStep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetadataInputStep.d.ts","sourceRoot":"","sources":["../../../../src/steps/MetadataInputStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,eAAe,GAChB,EAAE,oBAAoB,2CA0EtB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
/** Example Step for metadata input testing */
|
|
4
|
+
export function MetadataInputStep({ data, setData, onStepCompleted, }) {
|
|
5
|
+
// Convert Map<string, Map<string, string>> to a flat object for local state management
|
|
6
|
+
const [itemMetadata, setItemMetadata] = useState(() => {
|
|
7
|
+
const result = {};
|
|
8
|
+
data.itemMetadata.forEach((languageMap, itemId) => {
|
|
9
|
+
result[itemId] = Object.fromEntries(languageMap);
|
|
10
|
+
});
|
|
11
|
+
return result;
|
|
12
|
+
});
|
|
13
|
+
const allItems = data.includeSubitems ? data.discoveredItems : data.items;
|
|
14
|
+
const handleMetadataChange = (itemId, language, metadata) => {
|
|
15
|
+
const updated = {
|
|
16
|
+
...itemMetadata,
|
|
17
|
+
[itemId]: {
|
|
18
|
+
...(itemMetadata[itemId] || {}),
|
|
19
|
+
[language]: metadata
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
setItemMetadata(updated);
|
|
23
|
+
// Convert back to Map structure for wizard data
|
|
24
|
+
const metadataMap = new Map();
|
|
25
|
+
Object.entries(updated).forEach(([itemId, languageMap]) => {
|
|
26
|
+
metadataMap.set(itemId, new Map(Object.entries(languageMap)));
|
|
27
|
+
});
|
|
28
|
+
setData({ ...data, itemMetadata: metadataMap });
|
|
29
|
+
console.log('Updated itemMetadata:', metadataMap);
|
|
30
|
+
};
|
|
31
|
+
// Data is saved automatically on change
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
// Mark step as completed since metadata is optional
|
|
34
|
+
onStepCompleted(true);
|
|
35
|
+
}, [onStepCompleted]);
|
|
36
|
+
// No footer actions needed since data is saved automatically
|
|
37
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", "data-testid": "metadata-input-step", children: [_jsx("h3", { className: "text-lg font-medium", children: "Enter Metadata Per Item Per Language" }), _jsx("p", { className: "text-sm text-gray-600", children: "Provide custom metadata for each item and language combination (optional)." }), _jsx("div", { className: "max-h-64 overflow-y-auto space-y-4", "data-testid": "metadata-input-container", children: allItems.map((item) => (_jsxs("div", { className: "border rounded p-3", "data-testid": `metadata-item-${item.id}`, children: [_jsx("label", { className: "block text-sm font-medium mb-2", "data-testid": `metadata-item-label-${item.id}`, children: item.name || item.id }), data.targetLanguages?.map((language) => (_jsxs("div", { className: "mb-2", "data-testid": `metadata-field-${item.id}-${language}`, children: [_jsxs("label", { className: "block text-xs font-medium mb-1 text-gray-600", "data-testid": `metadata-language-label-${item.id}-${language}`, children: [language.toUpperCase(), ":"] }), _jsx("textarea", { className: "w-full border rounded p-2 text-sm", rows: 2, value: itemMetadata[item.id]?.[language] || "", onChange: (e) => handleMetadataChange(item.id, language, e.target.value), placeholder: `Enter metadata for ${language}...`, "data-testid": `metadata-textarea-${item.id}-${language}` })] }, `${item.id}-${language}`)))] }, item.id))) })] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { TranslationStepProps } from "./types";
|
|
2
|
+
export declare function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, editContext, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
//# sourceMappingURL=ServiceLanguageSelectionStep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE,wBAAgB,4BAA4B,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAuMjJ"}
|