From e704077864630df895bb97feb778e3b093bdd66c Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Fri, 30 May 2025 11:44:33 +0200 Subject: [PATCH] refactor(editor): Extract `@n8n/i18n` package for internationalization (no-changelog) (#15466) --- packages/cli/src/auth/auth.service.ts | 3 +- .../src/binary-data/binary-data.service.ts | 3 +- .../utils/binary-helper-functions.ts | 3 +- .../frontend/@n8n/composables/tsup.config.ts | 2 +- packages/frontend/@n8n/i18n/.eslintrc.cjs | 10 + packages/frontend/@n8n/i18n/.gitignore | 24 + packages/frontend/@n8n/i18n/README.md | 27 + packages/frontend/@n8n/i18n/biome.jsonc | 4 + .../plugins => @n8n}/i18n/docs/ADDENDUM.md | 0 .../src/plugins => @n8n}/i18n/docs/README.md | 0 .../plugins => @n8n}/i18n/docs/img/cred.png | Bin .../i18n/docs/img/header1.png | Bin .../i18n/docs/img/header2.png | Bin .../i18n/docs/img/header3.png | Bin .../i18n/docs/img/header4.png | Bin .../i18n/docs/img/header5.png | Bin .../plugins => @n8n}/i18n/docs/img/node1.png | Bin .../plugins => @n8n}/i18n/docs/img/node2.png | Bin .../plugins => @n8n}/i18n/docs/img/node4.png | Bin packages/frontend/@n8n/i18n/package.json | 60 ++ .../plugins/i18n => @n8n/i18n/src}/index.ts | 46 +- .../i18n => @n8n/i18n/src}/locales/en.json | 0 packages/frontend/@n8n/i18n/src/shims.d.ts | 5 + packages/frontend/@n8n/i18n/src/types.ts | 18 + .../plugins/i18n => @n8n/i18n/src}/utils.ts | 0 packages/frontend/@n8n/i18n/tsconfig.json | 12 + packages/frontend/@n8n/i18n/tsup.config.ts | 11 + packages/frontend/@n8n/i18n/vite.config.ts | 4 + packages/frontend/@n8n/stores/tsup.config.ts | 2 +- packages/frontend/editor-ui/package.json | 3 +- packages/frontend/editor-ui/src/App.vue | 16 +- packages/frontend/editor-ui/src/Interface.ts | 9 - .../editor-ui/src/__tests__/render.ts | 2 +- .../frontend/editor-ui/src/api/nodeTypes.ts | 5 +- .../editor-ui/src/components/AboutModal.vue | 2 +- .../src/components/ActivationModal.vue | 2 +- .../src/components/AiUpdatedCodeMessage.vue | 2 +- .../editor-ui/src/components/ApiKeyCard.vue | 2 +- .../components/ApiKeyCreateOrEditModal.vue | 4 +- .../editor-ui/src/components/ApiKeyScopes.vue | 2 +- .../Agent/AskAssistantBuild.test.ts | 3 +- .../AskAssistant/Agent/AskAssistantBuild.vue | 2 +- .../AskAssistant/Chat/AskAssistantChat.vue | 2 +- .../Chat/AskAssistantFloatingButton.vue | 2 +- .../Chat/NewAssistantSessionModal.vue | 2 +- .../components/AskAssistant/HubSwitcher.vue | 2 +- .../AssignmentCollection.vue | 2 +- .../AssignmentCollection/TypeSelect.vue | 4 +- .../BecomeTemplateCreatorCta.vue | 2 +- .../src/components/BinaryDataDisplay.vue | 2 +- .../src/components/BinaryDataDisplayEmbed.vue | 2 +- .../ButtonParameter/ButtonParameter.test.ts | 20 +- .../ButtonParameter/ButtonParameter.vue | 13 +- .../CanvasChat/components/ChatLogsPanel.vue | 2 +- .../components/ChatMessagesPanel.vue | 2 +- .../composables/useChatMessaging.ts | 2 +- .../CanvasChat/composables/useChatState.ts | 2 +- .../future/components/LogDetailsPanel.vue | 2 +- .../future/components/LogsOverviewPanel.vue | 2 +- .../future/components/LogsOverviewRow.vue | 2 +- .../future/components/LogsPanelActions.vue | 2 +- .../LogsViewConsumedTokenCountText.vue | 2 +- .../components/LogsViewExecutionSummary.vue | 2 +- .../future/components/LogsViewRunData.vue | 2 +- .../src/components/ChangePasswordModal.vue | 2 +- .../src/components/ChatEmbedModal.vue | 2 +- .../components/CodeNodeEditor/AskAI/AskAI.vue | 4 +- .../CodeNodeEditor/CodeNodeEditor.vue | 2 +- .../completions/base.completions.ts | 2 +- .../completions/execution.completions.ts | 2 +- .../completions/itemField.completions.ts | 2 +- .../completions/itemIndex.completions.ts | 2 +- .../completions/jsonField.completions.ts | 2 +- .../completions/prevNode.completions.ts | 2 +- .../completions/workflow.completions.ts | 2 +- .../src/components/CodeNodeEditor/linter.ts | 2 +- .../src/components/CollectionParameter.vue | 11 +- .../src/components/CommunityPackageCard.vue | 2 +- .../CommunityPackageInstallModal.vue | 2 +- .../CommunityPackageManageConfirmModal.vue | 2 +- .../CommunityPlusEnrollmentModal.vue | 2 +- .../src/components/ConsumedTokensDetails.vue | 2 +- .../editor-ui/src/components/CopyInput.vue | 2 +- .../src/components/CredentialCard.vue | 2 +- .../CredentialEdit/AuthTypeSelector.vue | 2 +- .../CredentialEdit/CredentialConfig.vue | 4 +- .../CredentialEdit/CredentialEdit.vue | 2 +- .../CredentialEdit/CredentialInfo.vue | 2 +- .../CredentialEdit/CredentialSharing.ee.vue | 2 +- .../CredentialEdit/GoogleAuthButton.vue | 2 +- .../components/CredentialEdit/OauthButton.vue | 2 +- .../CredentialPicker/CredentialPicker.vue | 2 +- .../CredentialPicker/CredentialsDropdown.vue | 2 +- .../src/components/CredentialsSelect.vue | 2 +- .../src/components/CredentialsSelectModal.vue | 2 +- .../src/components/DebugPaywallModal.vue | 2 +- .../src/components/DeleteUserModal.vue | 2 +- .../components/DuplicateWorkflowDialog.vue | 2 +- .../src/components/Error/NodeErrorView.vue | 4 +- .../Evaluations.ee/ListRuns/RunsSection.vue | 2 +- .../Evaluations.ee/ListRuns/TestRunsTable.vue | 4 +- .../Paywall/EvaluationsPaywall.vue | 2 +- .../SetupWizard/SetupWizard.vue | 2 +- .../Evaluations.ee/shared/StepHeader.vue | 2 +- .../Evaluations.ee/shared/TableStatusCell.vue | 4 +- .../Evaluations.ee/shared/errorCodes.ts | 2 +- .../src/components/ExpressionEditModal.vue | 2 +- .../ExternalSecretsProviderCard.ee.vue | 2 +- ...rnalSecretsProviderConnectionSwitch.ee.vue | 2 +- .../ExternalSecretsProviderModal.ee.vue | 2 +- .../editor-ui/src/components/Feedback.test.ts | 2 +- .../editor-ui/src/components/Feedback.vue | 2 +- .../FilterConditions/CombinatorSelect.vue | 2 +- .../components/FilterConditions/Condition.vue | 2 +- .../FilterConditions/FilterConditions.vue | 2 +- .../FilterConditions/OperatorSelect.vue | 2 +- .../src/components/FilterConditions/types.ts | 2 +- .../src/components/FilterConditions/utils.ts | 2 +- .../components/FixedCollectionParameter.vue | 15 +- .../components/Folders/DeleteFolderModal.vue | 2 +- .../Folders/EmptySharedSectionActionBox.vue | 2 +- .../components/Folders/FolderBreadcrumbs.vue | 2 +- .../src/components/Folders/FolderCard.vue | 2 +- .../Folders/MoveToFolderDropdown.vue | 2 +- .../components/Folders/MoveToFolderModal.vue | 2 +- .../Folders/ProjectBreadcrumb.test.ts | 3 +- .../components/Folders/ProjectBreadcrumb.vue | 2 +- .../src/components/FreeAiCreditsCallout.vue | 2 +- .../src/components/FromAiParametersModal.vue | 2 +- .../editor-ui/src/components/GoBackButton.vue | 2 +- .../src/components/ImportCurlModal.vue | 2 +- .../src/components/ImportCurlParameter.vue | 2 +- .../src/components/ImportWorkflowUrlModal.vue | 2 +- .../ExpressionOutput.vue | 2 +- .../InlineExpressionEditorOutput.vue | 2 +- .../InlineExpressionTip.vue | 2 +- .../OutputItemSelect.vue | 2 +- .../src/components/InputNodeSelect.vue | 2 +- .../editor-ui/src/components/InputPanel.vue | 2 +- .../src/components/InviteUsersModal.vue | 2 +- .../src/components/MainHeader/MainHeader.vue | 2 +- .../components/MainHeader/WorkflowDetails.vue | 4 +- .../MainHeader/WorkflowHistoryButton.vue | 2 +- .../editor-ui/src/components/MainSidebar.vue | 2 +- .../components/MainSidebarSourceControl.vue | 2 +- .../src/components/MfaSetupModal.vue | 2 +- .../src/components/MultipleParameter.vue | 13 +- .../src/components/NDVSubConnections.vue | 2 +- .../src/components/Node/NodeCreation.vue | 2 +- .../ItemTypes/CommunityNodeItem.vue | 2 +- .../Node/NodeCreator/ItemTypes/NodeItem.vue | 2 +- .../NodeCreator/ItemTypes/SubcategoryItem.vue | 4 +- .../Node/NodeCreator/Modes/ActionsMode.vue | 2 +- .../Node/NodeCreator/Modes/NodesMode.vue | 4 +- .../Panel/CommunityNodeDetails.vue | 2 +- .../Panel/CommunityNodeDocsLink.vue | 2 +- .../NodeCreator/Panel/CommunityNodeInfo.vue | 2 +- .../Node/NodeCreator/Panel/NoResults.vue | 2 +- .../Node/NodeCreator/Panel/NodesListPanel.vue | 2 +- .../Renderers/CategorizedItemsRenderer.vue | 2 +- .../NodeCreator/composables/useActions.ts | 4 +- .../composables/useActionsGeneration.ts | 2 +- .../NodeCreator/composables/useViewStacks.ts | 2 +- .../src/components/Node/NodeCreator/utils.ts | 2 +- .../components/Node/NodeCreator/viewsData.ts | 4 +- .../src/components/NodeCredentials.vue | 2 +- .../src/components/NodeDetailsView.vue | 2 +- .../src/components/NodeExecuteButton.vue | 2 +- .../components/NodeExecutionErrorMessage.vue | 2 +- .../editor-ui/src/components/NodeSettings.vue | 2 +- .../src/components/NodeSettingsTabs.vue | 2 +- .../editor-ui/src/components/NodeTitle.vue | 2 +- .../NodeViewUnfinishedWorkflowMessage.vue | 2 +- .../editor-ui/src/components/NodeWebhooks.vue | 2 +- .../editor-ui/src/components/NpsSurvey.vue | 2 +- .../editor-ui/src/components/OutputPanel.vue | 2 +- .../src/components/ParameterInput.vue | 22 +- .../src/components/ParameterInputExpanded.vue | 13 +- .../src/components/ParameterInputFull.vue | 38 +- .../src/components/ParameterInputList.vue | 11 +- .../FromAiOverrideButton.vue | 2 +- .../FromAiOverrideField.vue | 2 +- .../src/components/ParameterIssues.vue | 2 +- .../src/components/ParameterOptions.vue | 2 +- .../src/components/PersonalizationModal.vue | 2 +- .../components/Projects/ProjectCardBadge.vue | 2 +- .../Projects/ProjectDeleteDialog.vue | 2 +- .../src/components/Projects/ProjectHeader.vue | 2 +- .../Projects/ProjectMoveResourceModal.vue | 2 +- .../ProjectMoveSuccessToastMessage.vue | 2 +- .../components/Projects/ProjectNavigation.vue | 2 +- .../Projects/ProjectRoleUpgradeDialog.vue | 2 +- .../components/Projects/ProjectSharing.vue | 2 +- .../src/components/Projects/ProjectTabs.vue | 4 +- .../PromptMfaCodeModal/PromptMfaCodeModal.vue | 2 +- .../src/components/PushConnectionTracker.vue | 2 +- .../ResourceLocator/ResourceLocator.vue | 4 +- .../ResourceLocatorDropdown.vue | 2 +- .../ResourceMapper/MappingFields.vue | 4 +- .../ResourceMapper/MappingModeSelect.vue | 2 +- .../ResourceMapper/MatchingColumnsSelect.vue | 2 +- .../ResourceMapper/ResourceMapper.vue | 2 +- .../editor-ui/src/components/RunData.vue | 2 +- .../src/components/RunDataAi/RunDataAi.vue | 2 +- .../components/RunDataAi/RunDataAiContent.vue | 2 +- .../components/RunDataDisplayModeSelect.vue | 2 +- .../src/components/RunDataItemCount.vue | 2 +- .../src/components/RunDataJsonActions.test.ts | 2 +- .../src/components/RunDataJsonActions.vue | 2 +- .../src/components/RunDataPaginationBar.vue | 2 +- .../src/components/RunDataParsedAiContent.vue | 2 +- .../src/components/RunDataPinButton.vue | 2 +- .../src/components/RunDataSearch.vue | 2 +- .../editor-ui/src/components/RunDataTable.vue | 2 +- .../editor-ui/src/components/RunInfo.vue | 2 +- .../editor-ui/src/components/SSOLogin.vue | 2 +- .../editor-ui/src/components/SaveButton.vue | 2 +- .../editor-ui/src/components/ScopesNotice.vue | 2 +- .../EventDestinationCard.ee.vue | 4 +- .../EventDestinationSettingsModal.ee.vue | 4 +- .../EventSelection.ee.vue | 4 +- .../src/components/SettingsSidebar.vue | 2 +- .../SetupWorkflowCredentialsButton.vue | 2 +- .../SetupWorkflowCredentialsModal.vue | 2 +- ...ourceControlInitializationErrorMessage.vue | 2 +- .../components/SourceControlPullModal.ee.vue | 2 +- .../components/SourceControlPushModal.ee.vue | 2 +- .../editor-ui/src/components/TagsDropdown.vue | 2 +- .../TagsManager/AnnotationTagsManager.ee.vue | 2 +- .../src/components/TagsManager/NoTagsView.vue | 4 +- .../components/TagsManager/TagsManager.vue | 4 +- .../TagsManager/TagsView/TagsTable.vue | 4 +- .../TagsManager/TagsView/TagsTableHeader.vue | 2 +- .../TagsManager/TagsView/TagsView.vue | 4 +- .../TagsManager/WorkflowTagsManager.vue | 2 +- .../editor-ui/src/components/TemplateCard.vue | 4 +- .../src/components/TemplateDetails.vue | 2 +- .../src/components/TemplateFilters.vue | 2 +- .../editor-ui/src/components/TemplateList.vue | 2 +- .../src/components/TemplatesInfoCard.vue | 2 +- .../editor-ui/src/components/TextEdit.vue | 15 +- .../editor-ui/src/components/TimeAgo.vue | 2 +- .../editor-ui/src/components/TriggerPanel.vue | 2 +- .../editor-ui/src/components/UpdatesPanel.vue | 2 +- .../src/components/VariablesForm.vue | 2 +- .../src/components/VariablesUsageBadge.vue | 2 +- .../editor-ui/src/components/VersionCard.vue | 2 +- .../src/components/ViewSubExecution.vue | 2 +- .../src/components/VirtualSchema.vue | 2 +- .../src/components/VirtualSchemaHeader.vue | 2 +- .../src/components/WorkerList.ee.vue | 2 +- .../src/components/Workers/WorkerCard.ee.vue | 2 +- .../Workers/WorkerChartsAccordion.ee.vue | 2 +- .../Workers/WorkerJobAccordion.ee.vue | 2 +- .../Workers/WorkerNetAccordion.ee.vue | 2 +- .../WorkflowActivationErrorMessage.vue | 2 +- .../src/components/WorkflowActivator.vue | 2 +- .../editor-ui/src/components/WorkflowCard.vue | 2 +- .../WorkflowHistoryContent.vue | 2 +- .../WorkflowHistory/WorkflowHistoryList.vue | 2 +- .../WorkflowHistoryListItem.vue | 2 +- .../WorkflowHistoryVersionRestoreModal.vue | 2 +- .../src/components/WorkflowPreview.vue | 2 +- .../WorkflowSelectorParameterInput.vue | 2 +- .../useWorkflowResourceLocatorModes.ts | 2 +- .../src/components/WorkflowSettings.vue | 2 +- .../src/components/WorkflowShareModal.ee.vue | 4 +- .../src/components/banners/BaseBanner.vue | 2 +- .../banners/EmailConfirmationBanner.vue | 2 +- .../banners/NonProductionLicenseBanner.vue | 2 +- .../src/components/banners/TrialBanner.vue | 2 +- .../components/banners/TrialOverBanner.vue | 2 +- .../src/components/banners/V1Banner.vue | 2 +- .../CanvasClearExecutionDataButton.vue | 2 +- .../elements/buttons/CanvasControlButtons.vue | 2 +- .../buttons/CanvasRunWorkflowButton.vue | 2 +- .../CanvasStopCurrentExecutionButton.vue | 2 +- .../CanvasStopWaitingForWebhookButton.vue | 2 +- .../elements/edges/CanvasEdgeToolbar.vue | 2 +- .../render-types/CanvasHandleMainOutput.vue | 2 +- .../elements/nodes/CanvasNodeToolbar.vue | 2 +- .../nodes/render-types/CanvasNodeAIPrompt.vue | 2 +- .../nodes/render-types/CanvasNodeAddNodes.vue | 2 +- .../nodes/render-types/CanvasNodeDefault.vue | 2 +- .../parts/CanvasNodeStatusIcons.vue | 2 +- .../render-types/parts/CanvasNodeTrigger.vue | 2 +- .../toolbar/CanvasNodeStickyColorSelector.vue | 2 +- .../executions/ConcurrentExecutionsHeader.vue | 2 +- .../executions/ExecutionsFilter.vue | 2 +- .../components/executions/ExecutionsTime.vue | 2 +- .../global/GlobalExecutionsList.vue | 2 +- .../global/GlobalExecutionsListItem.vue | 2 +- .../GlobalExecutionsListItemQueuedTooltip.vue | 2 +- .../WorkflowExecutionAnnotationPanel.ee.vue | 2 +- .../workflow/WorkflowExecutionsCard.vue | 2 +- .../WorkflowExecutionsInfoAccordion.vue | 2 +- .../WorkflowExecutionsLandingPage.vue | 2 +- .../workflow/WorkflowExecutionsPreview.vue | 2 +- .../workflow/WorkflowExecutionsSidebar.vue | 2 +- .../forms/ResourceFiltersDropdown.vue | 2 +- .../layouts/ResourcesListLayout.vue | 4 +- .../src/composables/useAIAssistantHelpers.ts | 2 +- .../src/composables/useBeforeUnload.ts | 2 +- .../src/composables/useCanvasMapping.ts | 4 +- .../src/composables/useCanvasOperations.ts | 6 +- .../src/composables/useContextMenu.ts | 2 +- .../src/composables/useDataSchema.ts | 2 +- .../src/composables/useExecutionDebugging.ts | 2 +- .../composables/useExecutionHelpers.test.ts | 2 +- .../src/composables/useExecutionHelpers.ts | 2 +- .../src/composables/useExpressionEditor.ts | 2 +- .../editor-ui/src/composables/useFolders.ts | 2 +- .../composables/useGlobalEntityCreation.ts | 2 +- .../editor-ui/src/composables/useI18n.ts | 5 - .../src/composables/useImportCurlCommand.ts | 4 +- .../src/composables/useLoadingService.ts | 2 +- .../src/composables/useNodeHelpers.ts | 2 +- .../composables/useNodeSpecificationValues.ts | 2 +- .../src/composables/usePinnedData.test.ts | 2 +- .../src/composables/usePinnedData.ts | 2 +- .../handlers/executionFinished.ts | 2 +- .../handlers/workflowFailedToActivate.ts | 2 +- .../src/composables/useRunWorkflow.test.ts | 4 +- .../src/composables/useRunWorkflow.ts | 2 +- .../editor-ui/src/composables/useToast.ts | 2 +- .../src/composables/useWorkflowActivate.ts | 2 +- .../src/composables/useWorkflowHelpers.ts | 2 +- .../src/composables/useWorkflowSaving.ts | 2 +- .../insights/components/InsightsDashboard.vue | 2 +- .../insights/components/InsightsPaywall.vue | 2 +- .../insights/components/InsightsSummary.vue | 2 +- .../components/InsightsUpgradeModal.vue | 4 +- .../charts/InsightsChartAverageRuntime.vue | 2 +- .../components/charts/InsightsChartFailed.vue | 2 +- .../charts/InsightsChartFailureRate.vue | 2 +- .../charts/InsightsChartTimeSaved.vue | 2 +- .../components/charts/InsightsChartTotal.vue | 2 +- .../tables/InsightsTableWorkflows.vue | 2 +- .../features/insights/insights.constants.ts | 2 +- packages/frontend/editor-ui/src/init.ts | 2 +- packages/frontend/editor-ui/src/main.ts | 2 +- .../codemirror/completions/constants.ts | 2 +- .../completions/datatype.completions.ts | 2 +- .../completions/dollar.completions.ts | 2 +- .../codemirror/completions/infoBoxRenderer.ts | 2 +- .../luxon.instance.docs.ts | 2 +- .../luxon.static.docs.ts | 2 +- .../completions/nonDollar.completions.ts | 2 +- .../frontend/editor-ui/src/shims-vue.d.ts | 2 +- .../editor-ui/src/stores/assistant.store.ts | 2 +- .../editor-ui/src/stores/builder.store.ts | 2 +- .../editor-ui/src/stores/credentials.store.ts | 2 +- .../editor-ui/src/stores/folders.store.ts | 2 +- .../editor-ui/src/stores/nodeTypes.store.ts | 2 +- .../editor-ui/src/stores/settings.store.ts | 3 +- .../src/stores/workflows.ee.store.ts | 2 +- .../editor-ui/src/stores/workflows.store.ts | 2 +- .../src/utils/credentialOnlyNodes.ts | 2 +- .../src/utils/executionUtils.test.ts | 2 +- .../editor-ui/src/utils/executionUtils.ts | 12 +- .../editor-ui/src/utils/expressions.ts | 2 +- .../src/utils/formatters/listFormatter.ts | 2 +- .../src/utils/fromAIOverrideUtils.ts | 2 +- .../editor-ui/src/utils/nodeTypesUtils.ts | 2 +- .../editor-ui/src/utils/sourceControlUtils.ts | 4 +- .../editor-ui/src/views/CanvasAddButton.vue | 2 +- .../src/views/ChangePasswordView.vue | 2 +- .../editor-ui/src/views/CredentialsView.vue | 2 +- .../editor-ui/src/views/ErrorView.vue | 4 +- .../Evaluations.ee/EvaluationsRootView.vue | 2 +- .../views/Evaluations.ee/EvaluationsView.vue | 2 +- .../Evaluations.ee/TestRunDetailView.vue | 4 +- .../editor-ui/src/views/ExecutionsView.vue | 2 +- .../src/views/ForgotMyPasswordView.vue | 2 +- .../frontend/editor-ui/src/views/MfaView.vue | 2 +- .../frontend/editor-ui/src/views/NodeView.vue | 2 +- .../editor-ui/src/views/ProjectSettings.vue | 2 +- .../editor-ui/src/views/SamlOnboarding.vue | 2 +- .../editor-ui/src/views/SettingsApiView.vue | 2 +- .../src/views/SettingsCommunityNodesView.vue | 2 +- .../src/views/SettingsExternalSecrets.vue | 2 +- .../editor-ui/src/views/SettingsLdapView.vue | 2 +- .../src/views/SettingsLogStreamingView.vue | 2 +- .../src/views/SettingsPersonalView.vue | 4 +- .../src/views/SettingsSourceControl.vue | 2 +- .../editor-ui/src/views/SettingsSso.vue | 2 +- .../src/views/SettingsUsageAndPlan.vue | 2 +- .../editor-ui/src/views/SettingsUsersView.vue | 2 +- .../editor-ui/src/views/SetupView.vue | 2 +- .../AppsRequiringCredsNotice.vue | 2 +- .../SetupTemplateFormStep.vue | 2 +- .../SetupWorkflowFromTemplateView.vue | 2 +- .../editor-ui/src/views/SigninView.vue | 2 +- .../editor-ui/src/views/SignoutView.vue | 2 +- .../editor-ui/src/views/SignupView.vue | 2 +- .../src/views/TemplatesCollectionView.vue | 2 +- .../src/views/TemplatesSearchView.vue | 2 +- .../src/views/TemplatesWorkflowView.vue | 2 +- .../editor-ui/src/views/VariablesView.vue | 2 +- .../editor-ui/src/views/WorkerView.vue | 2 +- .../src/views/WorkflowExecutionsView.vue | 2 +- .../editor-ui/src/views/WorkflowHistory.vue | 2 +- .../src/views/WorkflowOnboardingView.vue | 2 +- .../editor-ui/src/views/WorkflowsView.vue | 2 +- packages/frontend/editor-ui/tsconfig.json | 1 + packages/frontend/editor-ui/vite.config.mts | 4 + pnpm-lock.yaml | 517 +++++++++--------- pnpm-workspace.yaml | 1 + 408 files changed, 1002 insertions(+), 767 deletions(-) create mode 100644 packages/frontend/@n8n/i18n/.eslintrc.cjs create mode 100644 packages/frontend/@n8n/i18n/.gitignore create mode 100644 packages/frontend/@n8n/i18n/README.md create mode 100644 packages/frontend/@n8n/i18n/biome.jsonc rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/ADDENDUM.md (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/README.md (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/cred.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/header1.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/header2.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/header3.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/header4.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/header5.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/node1.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/node2.png (100%) rename packages/frontend/{editor-ui/src/plugins => @n8n}/i18n/docs/img/node4.png (100%) create mode 100644 packages/frontend/@n8n/i18n/package.json rename packages/frontend/{editor-ui/src/plugins/i18n => @n8n/i18n/src}/index.ts (90%) rename packages/frontend/{editor-ui/src/plugins/i18n => @n8n/i18n/src}/locales/en.json (100%) create mode 100644 packages/frontend/@n8n/i18n/src/shims.d.ts create mode 100644 packages/frontend/@n8n/i18n/src/types.ts rename packages/frontend/{editor-ui/src/plugins/i18n => @n8n/i18n/src}/utils.ts (100%) create mode 100644 packages/frontend/@n8n/i18n/tsconfig.json create mode 100644 packages/frontend/@n8n/i18n/tsup.config.ts create mode 100644 packages/frontend/@n8n/i18n/vite.config.ts delete mode 100644 packages/frontend/editor-ui/src/composables/useI18n.ts diff --git a/packages/cli/src/auth/auth.service.ts b/packages/cli/src/auth/auth.service.ts index 0181b62d1e..f0a3b99283 100644 --- a/packages/cli/src/auth/auth.service.ts +++ b/packages/cli/src/auth/auth.service.ts @@ -5,6 +5,7 @@ import { Service } from '@n8n/di'; import { createHash } from 'crypto'; import type { NextFunction, Response } from 'express'; import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken'; +import type { StringValue as TimeUnitValue } from 'ms'; import { Logger } from 'n8n-core'; import config from '@/config'; @@ -180,7 +181,7 @@ export class AuthService { return user; } - generatePasswordResetToken(user: User, expiresIn = '20m') { + generatePasswordResetToken(user: User, expiresIn: TimeUnitValue = '20m') { const payload: PasswordResetToken = { sub: user.id, hash: this.createJWTHash(user) }; return this.jwtService.sign(payload, { expiresIn }); } diff --git a/packages/core/src/binary-data/binary-data.service.ts b/packages/core/src/binary-data/binary-data.service.ts index b8b21d6f83..6085e27f57 100644 --- a/packages/core/src/binary-data/binary-data.service.ts +++ b/packages/core/src/binary-data/binary-data.service.ts @@ -1,5 +1,6 @@ import { Container, Service } from '@n8n/di'; import jwt from 'jsonwebtoken'; +import type { StringValue as TimeUnitValue } from 'ms'; import { BINARY_ENCODING, UnexpectedError } from 'n8n-workflow'; import type { INodeExecutionData, IBinaryData } from 'n8n-workflow'; import { readFile, stat } from 'node:fs/promises'; @@ -45,7 +46,7 @@ export class BinaryDataService { } } - createSignedToken(binaryData: IBinaryData, expiresIn = '1 day') { + createSignedToken(binaryData: IBinaryData, expiresIn: TimeUnitValue = '1 day') { if (!binaryData.id) { throw new UnexpectedError('URL signing is not available in memory mode'); } diff --git a/packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts b/packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts index d111a93be5..9a34622127 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/binary-helper-functions.ts @@ -4,6 +4,7 @@ import FileType from 'file-type'; import { IncomingMessage } from 'http'; import iconv from 'iconv-lite'; import { extension, lookup } from 'mime-types'; +import type { StringValue as TimeUnitValue } from 'ms'; import type { BinaryHelperFunctions, IBinaryData, @@ -279,7 +280,7 @@ export const getBinaryHelperFunctions = ( getBinaryMetadata, binaryToBuffer, binaryToString, - createBinarySignedUrl(binaryData: IBinaryData, expiresIn?: string) { + createBinarySignedUrl(binaryData: IBinaryData, expiresIn?: TimeUnitValue) { const token = Container.get(BinaryDataService).createSignedToken(binaryData, expiresIn); return `${restApiUrl}/binary-data/signed?token=${token}`; }, diff --git a/packages/frontend/@n8n/composables/tsup.config.ts b/packages/frontend/@n8n/composables/tsup.config.ts index 0d555b1ab0..bff21e2550 100644 --- a/packages/frontend/@n8n/composables/tsup.config.ts +++ b/packages/frontend/@n8n/composables/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__**/*'], + entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__/**/*'], format: ['cjs', 'esm'], clean: true, dts: true, diff --git a/packages/frontend/@n8n/i18n/.eslintrc.cjs b/packages/frontend/@n8n/i18n/.eslintrc.cjs new file mode 100644 index 0000000000..3f9a316c08 --- /dev/null +++ b/packages/frontend/@n8n/i18n/.eslintrc.cjs @@ -0,0 +1,10 @@ +const sharedOptions = require('@n8n/eslint-config/shared'); + +/** + * @type {import('@types/eslint').ESLint.ConfigData} + */ +module.exports = { + extends: ['@n8n/eslint-config/frontend'], + + ...sharedOptions(__dirname, 'frontend'), +}; diff --git a/packages/frontend/@n8n/i18n/.gitignore b/packages/frontend/@n8n/i18n/.gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/packages/frontend/@n8n/i18n/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/frontend/@n8n/i18n/README.md b/packages/frontend/@n8n/i18n/README.md new file mode 100644 index 0000000000..7b0020ca88 --- /dev/null +++ b/packages/frontend/@n8n/i18n/README.md @@ -0,0 +1,27 @@ +# @n8n/i18n + +A package for managing internationalization (i18n) in n8n's Frontend codebase. It provides a structured way to handle translations and localization, ensuring that the application can be easily adapted to different languages and regions. + +## Table of Contents + +- [Features](#features) +- [Contributing](#contributing) +- [License](#license) + +## Features + +- **Translation Management**: Simplifies the process of managing translations for different languages. +- **Localization Support**: Provides tools to adapt the application for different regions and cultures. +- **Easy Integration**: Seamlessly integrates with n8n's Frontend codebase, making it easy to implement and use. +- **Reusable Base Text**: Allows for the definition of reusable base text strings, reducing redundancy in translations. +- **Pluralization and Interpolation**: Supports pluralization and interpolation in base text strings, making it flexible for various use cases. +- **Versioned Nodes Support**: Facilitates the management of translations for nodes in versioned directories, ensuring consistency across different versions. +- **Documentation**: Comprehensive documentation to help developers understand and utilize the package effectively. + +## Contributing + +For more details, please read our [CONTRIBUTING.md](CONTRIBUTING.md). + +## License + +For more details, please read our [LICENSE.md](LICENSE.md). diff --git a/packages/frontend/@n8n/i18n/biome.jsonc b/packages/frontend/@n8n/i18n/biome.jsonc new file mode 100644 index 0000000000..f882da95a5 --- /dev/null +++ b/packages/frontend/@n8n/i18n/biome.jsonc @@ -0,0 +1,4 @@ +{ + "$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["../../../../biome.jsonc"] +} diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/ADDENDUM.md b/packages/frontend/@n8n/i18n/docs/ADDENDUM.md similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/ADDENDUM.md rename to packages/frontend/@n8n/i18n/docs/ADDENDUM.md diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/README.md b/packages/frontend/@n8n/i18n/docs/README.md similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/README.md rename to packages/frontend/@n8n/i18n/docs/README.md diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/cred.png b/packages/frontend/@n8n/i18n/docs/img/cred.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/cred.png rename to packages/frontend/@n8n/i18n/docs/img/cred.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/header1.png b/packages/frontend/@n8n/i18n/docs/img/header1.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/header1.png rename to packages/frontend/@n8n/i18n/docs/img/header1.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/header2.png b/packages/frontend/@n8n/i18n/docs/img/header2.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/header2.png rename to packages/frontend/@n8n/i18n/docs/img/header2.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/header3.png b/packages/frontend/@n8n/i18n/docs/img/header3.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/header3.png rename to packages/frontend/@n8n/i18n/docs/img/header3.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/header4.png b/packages/frontend/@n8n/i18n/docs/img/header4.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/header4.png rename to packages/frontend/@n8n/i18n/docs/img/header4.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/header5.png b/packages/frontend/@n8n/i18n/docs/img/header5.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/header5.png rename to packages/frontend/@n8n/i18n/docs/img/header5.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/node1.png b/packages/frontend/@n8n/i18n/docs/img/node1.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/node1.png rename to packages/frontend/@n8n/i18n/docs/img/node1.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/node2.png b/packages/frontend/@n8n/i18n/docs/img/node2.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/node2.png rename to packages/frontend/@n8n/i18n/docs/img/node2.png diff --git a/packages/frontend/editor-ui/src/plugins/i18n/docs/img/node4.png b/packages/frontend/@n8n/i18n/docs/img/node4.png similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/docs/img/node4.png rename to packages/frontend/@n8n/i18n/docs/img/node4.png diff --git a/packages/frontend/@n8n/i18n/package.json b/packages/frontend/@n8n/i18n/package.json new file mode 100644 index 0000000000..3d178e793d --- /dev/null +++ b/packages/frontend/@n8n/i18n/package.json @@ -0,0 +1,60 @@ +{ + "name": "@n8n/i18n", + "type": "module", + "version": "1.0.0", + "files": [ + "dist" + ], + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./*": { + "types": "./dist/*.d.ts", + "import": "./dist/*.js", + "require": "./dist/*.cjs" + } + }, + "scripts": { + "dev": "vite", + "build": "pnpm run typecheck && tsup", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit", + "test": "vitest run", + "test:dev": "vitest --silent=false", + "lint": "eslint src --ext .js,.ts,.vue --quiet", + "lintfix": "eslint src --ext .js,.ts,.vue --fix", + "format": "biome format --write . && prettier --write . --ignore-path ../../../../.prettierignore", + "format:check": "biome ci . && prettier --check . --ignore-path ../../../../.prettierignore" + }, + "dependencies": { + "n8n-workflow": "workspace:*", + "vue-i18n": "catalog:frontend" + }, + "devDependencies": { + "@n8n/eslint-config": "workspace:*", + "@n8n/typescript-config": "workspace:*", + "@n8n/vitest-config": "workspace:*", + "@testing-library/jest-dom": "catalog:frontend", + "@testing-library/user-event": "catalog:frontend", + "@testing-library/vue": "catalog:frontend", + "@vitejs/plugin-vue": "catalog:frontend", + "@vue/tsconfig": "catalog:frontend", + "@vueuse/core": "catalog:frontend", + "vue": "catalog:frontend", + "tsup": "catalog:", + "typescript": "catalog:frontend", + "vite": "catalog:frontend", + "vitest": "catalog:frontend", + "vue-tsc": "catalog:frontend" + }, + "peerDependencies": { + "vue": "catalog:frontend" + }, + "license": "See LICENSE.md file in the root of the repository" +} diff --git a/packages/frontend/editor-ui/src/plugins/i18n/index.ts b/packages/frontend/@n8n/i18n/src/index.ts similarity index 90% rename from packages/frontend/editor-ui/src/plugins/i18n/index.ts rename to packages/frontend/@n8n/i18n/src/index.ts index d27245b5a7..5c477d1339 100644 --- a/packages/frontend/editor-ui/src/plugins/i18n/index.ts +++ b/packages/frontend/@n8n/i18n/src/index.ts @@ -1,13 +1,9 @@ -import axios from 'axios'; -import { createI18n } from 'vue-i18n'; -import { locale } from '@n8n/design-system'; +/* eslint-disable @typescript-eslint/no-this-alias */ import type { INodeProperties, INodePropertyCollection, INodePropertyOptions } from 'n8n-workflow'; +import { createI18n } from 'vue-i18n'; -import type { INodeTranslationHeaders } from '@/Interface'; -import { useUIStore } from '@/stores/ui.store'; -import { useNDVStore } from '@/stores/ndv.store'; -import { useRootStore } from '@n8n/stores/useRootStore'; import englishBaseText from './locales/en.json'; +import type { BaseTextKey, INodeTranslationHeaders } from './types'; import { deriveMiddleKey, isNestedInCollectionLike, @@ -15,6 +11,8 @@ import { insertOptionsAndValues, } from './utils'; +export * from './types'; + export const i18nInstance = createI18n({ locale: 'en', fallbackLocale: 'en', @@ -118,9 +116,7 @@ export class I18nClass { /** * Namespace for methods to render text in the credentials details modal. */ - credText() { - const uiStore = useUIStore(); - const credentialType = uiStore.activeCredentialType; + credText(credentialType: string | null) { const credentialPrefix = `n8n-nodes-base.credentials.${credentialType}`; const context = this; @@ -204,10 +200,8 @@ export class I18nClass { * Namespace for methods to render text in the node details view, * except for `eventTriggerDescription`. */ - nodeText() { - const ndvStore = useNDVStore(); - const activeNode = ndvStore.activeNode; - const nodeType = activeNode ? this.shortNodeType(activeNode.type) : ''; // unused in eventTriggerDescription + nodeText(activeNodeType?: string | null) { + const nodeType = activeNodeType ? this.shortNodeType(activeNodeType) : ''; // unused in eventTriggerDescription const initialKey = `n8n-nodes-base.nodes.${nodeType}.nodeView`; const context = this; @@ -355,10 +349,8 @@ export class I18nClass { }; } - localizeNodeName(nodeName: string, type: string) { - const isEnglishLocale = useRootStore().defaultLocale === 'en'; - - if (isEnglishLocale) return nodeName; + localizeNodeName(language: string, nodeName: string, type: string) { + if (language === 'en') return nodeName; const nodeTypeName = this.shortNodeType(type); @@ -377,12 +369,8 @@ const loadedLanguages = ['en']; async function setLanguage(language: string) { i18nInstance.global.locale = language as 'en'; - axios.defaults.headers.common['Accept-Language'] = language; document!.querySelector('html')!.setAttribute('lang', language); - // update n8n design system and element ui - await locale.use(language); - return language; } @@ -449,14 +437,6 @@ export function addHeaders(headers: INodeTranslationHeaders, language: string) { export const i18n: I18nClass = new I18nClass(); -// ---------------------------------- -// typings -// ---------------------------------- - -type GetBaseTextKey = T extends `_${string}` ? never : T; - -export type BaseTextKey = GetBaseTextKey; - -type GetCategoryName = T extends `nodeCreator.categoryNames.${infer C}` ? C : never; - -export type CategoryName = GetCategoryName; +export function useI18n() { + return i18n; +} diff --git a/packages/frontend/editor-ui/src/plugins/i18n/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/locales/en.json rename to packages/frontend/@n8n/i18n/src/locales/en.json diff --git a/packages/frontend/@n8n/i18n/src/shims.d.ts b/packages/frontend/@n8n/i18n/src/shims.d.ts new file mode 100644 index 0000000000..737ee1e318 --- /dev/null +++ b/packages/frontend/@n8n/i18n/src/shims.d.ts @@ -0,0 +1,5 @@ +/// + +export {}; + +declare module '*.json'; diff --git a/packages/frontend/@n8n/i18n/src/types.ts b/packages/frontend/@n8n/i18n/src/types.ts new file mode 100644 index 0000000000..d81a1bf33a --- /dev/null +++ b/packages/frontend/@n8n/i18n/src/types.ts @@ -0,0 +1,18 @@ +import type englishBaseText from './locales/en.json'; + +export type GetBaseTextKey = T extends `_${string}` ? never : T; + +export type BaseTextKey = GetBaseTextKey; + +export type GetCategoryName = T extends `nodeCreator.categoryNames.${infer C}` ? C : never; + +export type CategoryName = GetCategoryName; + +export interface INodeTranslationHeaders { + data: { + [key: string]: { + displayName: string; + description: string; + }; + }; +} diff --git a/packages/frontend/editor-ui/src/plugins/i18n/utils.ts b/packages/frontend/@n8n/i18n/src/utils.ts similarity index 100% rename from packages/frontend/editor-ui/src/plugins/i18n/utils.ts rename to packages/frontend/@n8n/i18n/src/utils.ts diff --git a/packages/frontend/@n8n/i18n/tsconfig.json b/packages/frontend/@n8n/i18n/tsconfig.json new file mode 100644 index 0000000000..63e10d0192 --- /dev/null +++ b/packages/frontend/@n8n/i18n/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@n8n/typescript-config/tsconfig.frontend.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "outDir": "dist", + "types": ["vite/client", "vitest/globals"], + "isolatedModules": true, + "resolveJsonModule": true + }, + "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts", "tsup.config.ts"] +} diff --git a/packages/frontend/@n8n/i18n/tsup.config.ts b/packages/frontend/@n8n/i18n/tsup.config.ts new file mode 100644 index 0000000000..bff21e2550 --- /dev/null +++ b/packages/frontend/@n8n/i18n/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__/**/*'], + format: ['cjs', 'esm'], + clean: true, + dts: true, + cjsInterop: true, + splitting: true, + sourcemap: true, +}); diff --git a/packages/frontend/@n8n/i18n/vite.config.ts b/packages/frontend/@n8n/i18n/vite.config.ts new file mode 100644 index 0000000000..784f3fb497 --- /dev/null +++ b/packages/frontend/@n8n/i18n/vite.config.ts @@ -0,0 +1,4 @@ +import { defineConfig, mergeConfig } from 'vite'; +import { vitestConfig } from '@n8n/vitest-config/frontend'; + +export default mergeConfig(defineConfig({}), vitestConfig); diff --git a/packages/frontend/@n8n/stores/tsup.config.ts b/packages/frontend/@n8n/stores/tsup.config.ts index 0d555b1ab0..bff21e2550 100644 --- a/packages/frontend/@n8n/stores/tsup.config.ts +++ b/packages/frontend/@n8n/stores/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__**/*'], + entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__/**/*'], format: ['cjs', 'esm'], clean: true, dts: true, diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json index b03cfe919a..eaecfb409d 100644 --- a/packages/frontend/editor-ui/package.json +++ b/packages/frontend/editor-ui/package.json @@ -37,6 +37,7 @@ "@n8n/codemirror-lang-sql": "^1.0.2", "@n8n/composables": "workspace:*", "@n8n/design-system": "workspace:*", + "@n8n/i18n": "workspace:*", "@n8n/permissions": "workspace:*", "@n8n/stores": "workspace:*", "@n8n/utils": "workspace:*", @@ -84,7 +85,7 @@ "vue-agile": "^2.0.0", "vue-chartjs": "^5.2.0", "vue-github-button": "^3.1.3", - "vue-i18n": "^11.1.2", + "vue-i18n": "catalog:frontend", "vue-json-pretty": "2.2.4", "vue-markdown-render": "catalog:frontend", "vue-router": "catalog:frontend", diff --git a/packages/frontend/editor-ui/src/App.vue b/packages/frontend/editor-ui/src/App.vue index ebd33d65ec..9e3e774b81 100644 --- a/packages/frontend/editor-ui/src/App.vue +++ b/packages/frontend/editor-ui/src/App.vue @@ -9,7 +9,7 @@ import Modals from '@/components/Modals.vue'; import Telemetry from '@/components/Telemetry.vue'; import AskAssistantFloatingButton from '@/components/AskAssistant/Chat/AskAssistantFloatingButton.vue'; import AssistantsHub from '@/components/AskAssistant/AssistantsHub.vue'; -import { loadLanguage } from '@/plugins/i18n'; +import { loadLanguage } from '@n8n/i18n'; import { APP_MODALS_ELEMENT_ID, HIRING_BANNER, VIEWS } from '@/constants'; import { useRootStore } from '@n8n/stores/useRootStore'; import { useAssistantStore } from '@/stores/assistant.store'; @@ -19,6 +19,8 @@ import { useUsersStore } from '@/stores/users.store'; import { useSettingsStore } from '@/stores/settings.store'; import { useHistoryHelper } from '@/composables/useHistoryHelper'; import { useStyles } from './composables/useStyles'; +import { locale } from '@n8n/design-system'; +import axios from 'axios'; const route = useRoute(); const rootStore = useRootStore(); @@ -79,9 +81,15 @@ watch(route, (r) => { ); }); -watch(defaultLocale, (newLocale) => { - void loadLanguage(newLocale); -}); +watch( + defaultLocale, + (newLocale) => { + void loadLanguage(newLocale); + void locale.use(newLocale); + axios.defaults.headers.common['Accept-Language'] = newLocale; + }, + { immediate: true }, +);