From 4ae0f5b6fba65bfa8f236657d89358f53e465c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 11 Apr 2022 15:12:13 +0200 Subject: [PATCH] fix(editor): Fix i18n issues (#3072) * :bug: Fix `defaultLocale` watcher * :zap: Improve error handling for headers * :pencil2: Improve naming * :bug: Fix hiring banner check * :zap: Flatten base text keys * :zap: Fix miscorrected key * :zap: Implement pluralization * :pencil2: Update docs * :truck: Move headers fetching to `App.vue` * fix hiring banner * :zap: Fix missing import * :pencil2: Alphabetize translations * :zap: Switch to async check * feat(editor): Refactor Output Panel + fix i18n issues (#3097) * update main panel * finish up tabs * fix docs link * add icon * update node settings * clean up settings * add rename modal * fix component styles * fix spacing * truncate name * remove mixin * fix spacing * fix spacing * hide docs url * fix bug * fix renaming * refactor tabs out * refactor execute button * refactor header * add more views * fix error view * fix workflow rename bug * rename component * fix small screen bug * move items, fix positions * add hover state * show selector on empty state * add empty run state * fix binary view * 1 item * add vjs styles * show empty row for every item * refactor tabs * add branch names * fix spacing * fix up spacing * add run selector * fix positioning * clean up * increase width of selector * fix up spacing * fix copy button * fix branch naming; type issues * fix docs in custom nodes * add type * hide items when run selector is shown * increase selector size * add select prepend * clean up a bit * Add pagination * add stale icon * enable stale data in execution run * Revert "enable stale data in execution run" 8edb68dbffa0aa0d8189117e1a53381cb2c27608 * move metadata to its own state * fix smaller size * add scroll buttons * update tabs on resize * update stale data on rename * remove metadata on delete * hide x * change title colors * binary data classes * remove duplicate css * add colors * delete unused keys * use event bus * update styles of pagination * fix ts issues * fix ts issues * use chevron icons * fix design with download button * add back to canvas button * add trigger warning disabled * show trigger warning tooltip * update button labels for triggers * update node output message * fix add-option bug * add page selector * fix pagination selector bug * fix executions bug * remove hint * add json colors * add colors for json * add color json keys * fix select options bug * update keys * address comments * update name limit * align pencil * update icon size * update radio buttons height * address comments * fix pencil bug * change buttons alignment * fully center * change order of buttons * add no output message in branch * scroll to top * change active state * fix page size * all items * update expression background * update naming * align pencil * update modal background * add schedule group * update schedule nodes messages * use ellpises for last chars * fix spacing * fix tabs issue * fix too far data bug * fix executions bug * fix table wrapping * fix rename bug * add padding * handle unkown errors * add sticky header * ignore empty input, trim node name * nudge lightness of color * center buttons * update pagination * set colors of title * increase table font, fix alignment * fix pencil bug * fix spacing * use date now * address pagination issues * delete unused keys * update keys sort * fix prepend * fix radio button position * Revert "fix radio button position" ae42781786f2e6dcfb00d1be770b19a67f533bdf Co-authored-by: Mutasem Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> --- packages/cli/src/Server.ts | 11 +- .../src/components/N8nIcon/Icon.vue | 3 + .../components/N8nInfoTip/InfoTip.stories.js | 10 +- .../src/components/N8nInfoTip/InfoTip.vue | 63 +- .../N8nRadioButtons/RadioButton.vue | 66 + .../N8nRadioButtons/RadioButtons.stories.js | 51 + .../N8nRadioButtons/RadioButtons.vue | 49 + .../src/components/N8nRadioButtons/index.js | 3 + .../src/components/N8nSelect/Select.vue | 71 +- .../src/components/N8nTabs/Tabs.stories.js | 54 + .../src/components/N8nTabs/Tabs.vue | 192 ++ .../src/components/N8nTabs/index.js | 3 + .../design-system/src/components/index.js | 8 + .../src/components/utils/helpers.ts | 2 +- .../src/styleguide/colors.stories.mdx | 12 + packages/design-system/theme/src/_tokens.scss | 15 +- .../design-system/theme/src/common/var.scss | 4 +- packages/design-system/theme/src/index.scss | 2 +- .../design-system/theme/src/pagination.scss | 20 +- packages/editor-ui/src/App.vue | 18 +- packages/editor-ui/src/Interface.ts | 13 + .../editor-ui/src/components/DataDisplay.vue | 151 +- .../src/components/DisplayWithChange.vue | 128 -- .../src/components/ExpressionEdit.vue | 2 +- .../ExecutionDetails/ExecutionDetails.vue | 8 +- .../components/MainHeader/WorkflowDetails.vue | 8 +- .../src/components/NodeExecuteButton.vue | 65 + .../editor-ui/src/components/NodeSettings.vue | 162 +- .../editor-ui/src/components/NodeTabs.vue | 85 + .../editor-ui/src/components/NodeTitle.vue | 135 ++ packages/editor-ui/src/components/RunData.vue | 1029 +++++---- ...{WorkflowNameShort.vue => ShortenName.vue} | 2 +- .../TagsManager/TagsView/TagsView.vue | 10 +- .../src/components/mixins/nodeHelpers.ts | 3 + packages/editor-ui/src/n8n-theme.scss | 30 +- packages/editor-ui/src/plugins/components.ts | 11 +- .../src/plugins/i18n/docs/ADDENDUM.md | 68 +- .../editor-ui/src/plugins/i18n/docs/README.md | 127 +- packages/editor-ui/src/plugins/i18n/index.ts | 6 +- .../src/plugins/i18n/locales/en.json | 1852 +++++++---------- packages/editor-ui/src/plugins/icons.ts | 2 + packages/editor-ui/src/store.ts | 15 + packages/editor-ui/src/views/NodeView.vue | 45 +- packages/nodes-base/nodes/Cron/Cron.node.ts | 2 +- .../nodes/Interval/Interval.node.ts | 2 +- 45 files changed, 2579 insertions(+), 2039 deletions(-) create mode 100644 packages/design-system/src/components/N8nRadioButtons/RadioButton.vue create mode 100644 packages/design-system/src/components/N8nRadioButtons/RadioButtons.stories.js create mode 100644 packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue create mode 100644 packages/design-system/src/components/N8nRadioButtons/index.js create mode 100644 packages/design-system/src/components/N8nTabs/Tabs.stories.js create mode 100644 packages/design-system/src/components/N8nTabs/Tabs.vue create mode 100644 packages/design-system/src/components/N8nTabs/index.js delete mode 100644 packages/editor-ui/src/components/DisplayWithChange.vue create mode 100644 packages/editor-ui/src/components/NodeExecuteButton.vue create mode 100644 packages/editor-ui/src/components/NodeTabs.vue create mode 100644 packages/editor-ui/src/components/NodeTitle.vue rename packages/editor-ui/src/components/{WorkflowNameShort.vue => ShortenName.vue} (96%) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index d27ca2da11..14aff9569f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -30,7 +30,7 @@ /* eslint-disable no-await-in-loop */ import express from 'express'; -import { readFileSync } from 'fs'; +import { readFileSync, promises } from 'fs'; import { readFile } from 'fs/promises'; import _, { cloneDeep } from 'lodash'; import { dirname as pathDirname, join as pathJoin, resolve as pathResolve } from 'path'; @@ -1503,10 +1503,17 @@ class App { async (req: express.Request, res: express.Response): Promise => { const packagesPath = pathJoin(__dirname, '..', '..', '..'); const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers'); + + try { + await promises.access(`${headersPath}.js`); + } catch (_) { + return; // no headers available + } + try { return require(headersPath); } catch (error) { - res.status(500).send('Failed to find headers file'); + res.status(500).send('Failed to load headers file'); } }, ), diff --git a/packages/design-system/src/components/N8nIcon/Icon.vue b/packages/design-system/src/components/N8nIcon/Icon.vue index 8ffe65edec..b89b43a57e 100644 --- a/packages/design-system/src/components/N8nIcon/Icon.vue +++ b/packages/design-system/src/components/N8nIcon/Icon.vue @@ -2,6 +2,7 @@ diff --git a/packages/design-system/src/components/N8nInfoTip/InfoTip.stories.js b/packages/design-system/src/components/N8nInfoTip/InfoTip.stories.js index 382a46dac6..f61d1d9f9d 100644 --- a/packages/design-system/src/components/N8nInfoTip/InfoTip.stories.js +++ b/packages/design-system/src/components/N8nInfoTip/InfoTip.stories.js @@ -11,7 +11,13 @@ const Template = (args, { argTypes }) => ({ N8nInfoTip, }, template: - 'Need help doing something? Open docs', + 'Need help doing something? Open docs', }); -export const InputLabel = Template.bind({}); +export const Note = Template.bind({}); + +export const Tooltip = Template.bind({}); +Tooltip.args = { + type: 'tooltip', + tooltipPlacement: 'right', +}; diff --git a/packages/design-system/src/components/N8nInfoTip/InfoTip.vue b/packages/design-system/src/components/N8nInfoTip/InfoTip.vue index 75a56f49e4..4d26093f47 100644 --- a/packages/design-system/src/components/N8nInfoTip/InfoTip.vue +++ b/packages/design-system/src/components/N8nInfoTip/InfoTip.vue @@ -1,23 +1,48 @@ - @@ -81,7 +81,7 @@ import mixins from "vue-typed-mixins"; import { mapGetters } from "vuex"; import { MAX_WORKFLOW_NAME_LENGTH } from "@/constants"; -import WorkflowNameShort from "@/components/WorkflowNameShort.vue"; +import ShortenName from "@/components/ShortenName.vue"; import TagsContainer from "@/components/TagsContainer.vue"; import PushConnectionTracker from "@/components/PushConnectionTracker.vue"; import WorkflowActivator from "@/components/WorkflowActivator.vue"; @@ -105,7 +105,7 @@ export default mixins(workflowHelpers).extend({ components: { TagsContainer, PushConnectionTracker, - WorkflowNameShort, + ShortenName, WorkflowActivator, SaveButton, TagsDropdown, diff --git a/packages/editor-ui/src/components/NodeExecuteButton.vue b/packages/editor-ui/src/components/NodeExecuteButton.vue new file mode 100644 index 0000000000..e478da18e1 --- /dev/null +++ b/packages/editor-ui/src/components/NodeExecuteButton.vue @@ -0,0 +1,65 @@ + + + diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index 0d59691cf9..5f4a76c0dc 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -1,17 +1,15 @@ @@ -59,12 +55,11 @@ import { IUpdateInformation, } from '@/Interface'; -import { ElTabPane } from "element-ui/types/tab-pane"; - -import DisplayWithChange from '@/components/DisplayWithChange.vue'; +import NodeTitle from '@/components/NodeTitle.vue'; import ParameterInputFull from '@/components/ParameterInputFull.vue'; import ParameterInputList from '@/components/ParameterInputList.vue'; import NodeCredentials from '@/components/NodeCredentials.vue'; +import NodeTabs from '@/components/NodeTabs.vue'; import NodeWebhooks from '@/components/NodeWebhooks.vue'; import { get, set, unset } from 'lodash'; @@ -73,21 +68,23 @@ import { genericHelpers } from '@/components/mixins/genericHelpers'; import { nodeHelpers } from '@/components/mixins/nodeHelpers'; import mixins from 'vue-typed-mixins'; +import NodeExecuteButton from './NodeExecuteButton.vue'; export default mixins( externalHooks, genericHelpers, nodeHelpers, ) - .extend({ name: 'NodeSettings', components: { - DisplayWithChange, + NodeTitle, NodeCredentials, ParameterInputFull, ParameterInputList, + NodeTabs, NodeWebhooks, + NodeExecuteButton, }, computed: { nodeType (): INodeTypeDescription | null { @@ -150,14 +147,16 @@ export default mixins( return this.nodeType.properties; }, - workflowRunning (): boolean { - return this.$store.getters.isActionActive('workflowRunning'); + }, + props: { + eventBus: { }, }, data () { return { nodeValid: true, nodeColor: null, + openPanel: 'params', nodeValues: { color: '#ff0000', alwaysOutputData: false, @@ -271,7 +270,9 @@ export default mixins( }, }, methods: { - noOp () {}, + onNodeExecute () { + this.$emit('execute'); + }, setValue (name: string, value: NodeParameterValue) { const nameParts = name.split('.'); let lastNamePart: string | undefined = nameParts.pop(); @@ -337,6 +338,13 @@ export default mixins( this.$externalHooks().run('nodeSettings.credentialSelected', { updateInformation }); }, + nameChanged(name: string) { + // @ts-ignore + this.valueChanged({ + value: name, + name: 'name', + }); + }, valueChanged (parameterData: IUpdateInformation) { let newValue: NodeParameterValue; if (parameterData.hasOwnProperty('value')) { @@ -362,7 +370,6 @@ export default mixins( }; this.$emit('valueChanged', sendData); - this.$store.commit('setActiveNode', newValue); } else if (parameterData.name.startsWith('parameters.')) { // A node parameter changed @@ -514,20 +521,25 @@ export default mixins( this.nodeValid = false; } }, - handleTabClick(tab: ElTabPane) { - if(tab.label === 'Settings') { - this.$telemetry.track('User viewed node settings', { node_type: this.node ? this.node.type : '', workflow_id: this.$store.getters.workflowId }); - } - }, }, mounted () { this.setNodeValues(); + if (this.eventBus) { + (this.eventBus as Vue).$on('openSettings', () => { + this.openPanel = 'settings'; + }); + } }, }); - + diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index 43091926db..ad2348b266 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -1,73 +1,52 @@ @@ -225,6 +241,7 @@ import { IBinaryDisplayData, IExecutionResponse, INodeUi, + ITab, ITableData, } from '@/Interface'; @@ -234,15 +251,16 @@ import { } from '@/constants'; import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue'; +import WarningTooltip from '@/components/WarningTooltip.vue'; import NodeErrorView from '@/components/Error/NodeErrorView.vue'; import { copyPaste } from '@/components/mixins/copyPaste'; import { externalHooks } from "@/components/mixins/externalHooks"; import { genericHelpers } from '@/components/mixins/genericHelpers'; import { nodeHelpers } from '@/components/mixins/nodeHelpers'; -import { workflowRun } from '@/components/mixins/workflowRun'; import mixins from 'vue-typed-mixins'; +import Vue from 'vue/types/umd'; import { saveAs } from 'file-saver'; @@ -254,7 +272,6 @@ export default mixins( externalHooks, genericHelpers, nodeHelpers, - workflowRun, ) .extend({ name: 'RunData', @@ -262,13 +279,14 @@ export default mixins( BinaryDataDisplay, NodeErrorView, VueJsonPretty, + WarningTooltip, }, data () { return { binaryDataPreviewActive: false, dataSize: 0, deselectedPlaceholder, - displayMode: this.$locale.baseText('runData.table'), + displayMode: 'table', state: { value: '' as object | number | string, path: deselectedPlaceholder, @@ -276,18 +294,48 @@ export default mixins( runIndex: 0, showData: false, outputIndex: 0, - maxDisplayItems: 25 as number | null, binaryDataDisplayVisible: false, binaryDataDisplayData: null as IBinaryDisplayData | null, MAX_DISPLAY_DATA_SIZE, MAX_DISPLAY_ITEMS_AUTO_ALL, + currentPage: 1, + pageSize: 10, + pageSizes: [10, 25, 50, 100], }; }, mounted() { this.init(); }, computed: { + nodeType (): INodeTypeDescription | null { + if (this.node) { + return this.$store.getters.nodeType(this.node.type, this.node.typeVersion); + } + return null; + }, + isTriggerNode (): boolean { + return !!(this.nodeType && this.nodeType.group.includes('trigger')); + }, + isPollingTypeNode (): boolean { + return !!(this.nodeType && this.nodeType.polling); + }, + isScheduleTrigger (): boolean { + return !!(this.nodeType && this.nodeType.group.includes('schedule')); + }, + buttons(): Array<{label: string, value: string}> { + const defaults = [ + { label: this.$locale.baseText('runData.table'), value: 'table'}, + { label: this.$locale.baseText('runData.json'), value: 'json'}, + ]; + if (this.binaryData.length) { + return [ ...defaults, + { label: this.$locale.baseText('runData.binary'), value: 'binary'}, + ]; + } + + return defaults; + }, hasNodeRun(): boolean { return Boolean(this.node && this.workflowRunData && this.workflowRunData.hasOwnProperty(this.node.name)); }, @@ -305,19 +353,15 @@ export default mixins( return null; } const executionData: IRunExecutionData = this.workflowExecution.data; - return executionData.resultData.runData; - }, - maxDisplayItemsOptions (): number[] { - const options = [25, 50, 100, 250, 500, 1000].filter(option => option <= this.dataCount); - if (!options.includes(this.dataCount)) { - options.push(this.dataCount); + if (executionData && executionData.resultData) { + return executionData.resultData.runData; } - return options; + return null; }, node (): INodeUi | null { return this.$store.getters.activeNode; }, - runMetadata () { + runTaskData (): ITaskData | null { if (!this.node || this.workflowExecution === null) { return null; } @@ -332,40 +376,33 @@ export default mixins( return null; } - const taskData: ITaskData = runData[this.node.name][this.runIndex]; + return runData[this.node.name][this.runIndex]; + }, + runMetadata (): {executionTime: number, startTime: string} | null { + if (!this.runTaskData) { + return null; + } return { - executionTime: taskData.executionTime, - startTime: new Date(taskData.startTime).toLocaleString(), + executionTime: this.runTaskData.executionTime, + startTime: new Date(this.runTaskData.startTime).toLocaleString(), }; }, + staleData(): boolean { + if (!this.node) { + return false; + } + const updatedAt = this.$store.getters.getParametersLastUpdated(this.node.name); + if (!updatedAt || !this.runTaskData) { + return false; + } + const runAt = this.runTaskData.startTime; + return updatedAt > runAt; + }, dataCount (): number { - if (this.node === null) { - return 0; - } - - const runData: IRunData | null = this.workflowRunData; - - if (runData === null || !runData.hasOwnProperty(this.node.name)) { - return 0; - } - - if (runData[this.node.name].length <= this.runIndex) { - return 0; - } - - if (runData[this.node.name][this.runIndex].hasOwnProperty('error')) { - return 1; - } - - if (!runData[this.node.name][this.runIndex].hasOwnProperty('data') || - runData[this.node.name][this.runIndex].data === undefined - ) { - return 0; - } - - const inputData = this.getMainInputData(runData[this.node.name][this.runIndex].data!, this.outputIndex); - - return inputData.length; + return this.getDataCount(this.runIndex, this.outputIndex); + }, + dataSizeInMB(): string { + return (this.dataSize / 1024 / 1000).toLocaleString(); }, maxOutputIndex (): number { if (this.node === null) { @@ -407,29 +444,22 @@ export default mixins( return 0; }, - jsonData (): IDataObject[] { + inputData (): INodeExecutionData[] { let inputData = this.getNodeInputData(this.node, this.runIndex, this.outputIndex); if (inputData.length === 0 || !Array.isArray(inputData)) { return []; } - if (this.maxDisplayItems !== null) { - inputData = inputData.slice(0, this.maxDisplayItems); - } + const offset = this.pageSize * (this.currentPage - 1); + inputData = inputData.slice(offset, offset + this.pageSize); - return this.convertToJson(inputData); + return inputData; + }, + jsonData (): IDataObject[] { + return this.convertToJson(this.inputData); }, tableData (): ITableData | undefined { - let inputData = this.getNodeInputData(this.node, this.runIndex, this.outputIndex); - if (inputData.length === 0) { - return undefined; - } - - if (this.maxDisplayItems !== null) { - inputData = inputData.slice(0,this.maxDisplayItems); - } - - return this.convertToTable(inputData); + return this.convertToTable(this.inputData); }, binaryData (): IBinaryKeyData[] { if (this.node === null) { @@ -438,17 +468,106 @@ export default mixins( return this.getBinaryData(this.workflowRunData, this.node.name, this.runIndex, this.outputIndex); }, + branches (): ITab[] { + function capitalize(name: string) { + return name.charAt(0).toLocaleUpperCase() + name.slice(1); + } + const branches: ITab[] = []; + for (let i = 0; i <= this.maxOutputIndex; i++) { + const itemsCount = this.getDataCount(this.runIndex, i); + const items = this.$locale.baseText(itemsCount === 1 ? 'ndv.output.item': 'ndv.output.items'); + let outputName = this.getOutputName(i); + if (`${outputName}` === `${i}`) { + outputName = `${this.$locale.baseText('ndv.output')} ${outputName}`; + } + else { + outputName = capitalize(`${this.getOutputName(i)} ${this.$locale.baseText('ndv.output.branch')}`); + } + branches.push({ + label: itemsCount ? `${outputName} (${itemsCount} ${items})` : outputName, + value: i, + }); + } + return branches; + }, }, methods: { + onPageSizeChange(pageSize: number) { + this.pageSize = pageSize; + const maxPage = Math.ceil(this.dataCount / this.pageSize); + if (maxPage < this.currentPage) { + this.currentPage = maxPage; + } + }, + onDisplayModeChange(displayMode: string) { + const previous = this.displayMode; + this.displayMode = displayMode; + + const dataContainer = this.$refs.dataContainer; + if (dataContainer) { + const dataDisplay = (dataContainer as Element).children[0]; + + if (dataDisplay){ + dataDisplay.scrollTo(0, 0); + } + } + + this.closeBinaryDataDisplay(); + this.$externalHooks().run('runData.displayModeChanged', { newValue: displayMode, oldValue: previous }); + if(this.node) { + const nodeType = this.node ? this.node.type : ''; + this.$telemetry.track('User changed node output view mode', { old_mode: previous, new_mode: displayMode, node_type: nodeType, workflow_id: this.$store.getters.workflowId }); + } + }, + getRunLabel(option: number) { + let itemsCount = 0; + for (let i = 0; i <= this.maxOutputIndex; i++) { + itemsCount += this.getDataCount(option - 1, i); + } + const items = this.$locale.baseText(itemsCount === 1 ? 'ndv.output.item': 'ndv.output.items'); + const itemsLabel = itemsCount > 0 ? ` (${itemsCount} ${items})` : ''; + return option + this.$locale.baseText('ndv.output.of') + (this.maxRunIndex+1) + itemsLabel; + }, + getDataCount(runIndex: number, outputIndex: number) { + if (this.node === null) { + return 0; + } + + const runData: IRunData | null = this.workflowRunData; + + if (runData === null || !runData.hasOwnProperty(this.node.name)) { + return 0; + } + + if (runData[this.node.name].length <= runIndex) { + return 0; + } + + if (runData[this.node.name][runIndex].hasOwnProperty('error')) { + return 1; + } + + if (!runData[this.node.name][runIndex].hasOwnProperty('data') || + runData[this.node.name][runIndex].data === undefined + ) { + return 0; + } + + const inputData = this.getMainInputData(runData[this.node.name][runIndex].data!, outputIndex); + + return inputData.length; + }, + openSettings() { + this.$emit('openSettings'); + }, init() { // Reset the selected output index every time another node gets selected this.outputIndex = 0; - this.maxDisplayItems = 25; this.refreshDataSize(); - if (this.displayMode === this.$locale.baseText('runData.binary')) { + if (this.displayMode === 'binary') { this.closeBinaryDataDisplay(); if (this.binaryData.length === 0) { - this.displayMode = this.$locale.baseText('runData.table'); + this.displayMode = 'table'; } } }, @@ -562,7 +681,7 @@ export default mixins( return outputIndex + 1; } - const nodeType = this.$store.getters.nodeType(this.node.type, this.node.typeVersion) as INodeTypeDescription | null; + const nodeType = this.nodeType; if (!nodeType || !nodeType.outputNames || nodeType.outputNames.length <= outputIndex) { return outputIndex + 1; } @@ -643,7 +762,8 @@ export default mixins( // Check how much data there is to display const inputData = this.getNodeInputData(this.node, this.runIndex, this.outputIndex); - const jsonItems = inputData.slice(0, this.maxDisplayItems || inputData.length).map(item => item.json); + const offset = this.pageSize * (this.currentPage - 1); + const jsonItems = inputData.slice(offset, offset + this.pageSize).map(item => item.json); this.dataSize = JSON.stringify(jsonItems).length; @@ -660,14 +780,6 @@ export default mixins( jsonData () { this.refreshDataSize(); }, - displayMode (newValue, oldValue) { - this.closeBinaryDataDisplay(); - this.$externalHooks().run('runData.displayModeChanged', { newValue, oldValue }); - if(this.node) { - const nodeType = this.node ? this.node.type : ''; - this.$telemetry.track('User changed node output view mode', { old_mode: oldValue, new_mode: newValue, node_type: nodeType, workflow_id: this.$store.getters.workflowId }); - } - }, maxRunIndex () { this.runIndex = Math.min(this.runIndex, this.maxRunIndex); }, @@ -675,188 +787,279 @@ export default mixins( }); - + + diff --git a/packages/editor-ui/src/components/WorkflowNameShort.vue b/packages/editor-ui/src/components/ShortenName.vue similarity index 96% rename from packages/editor-ui/src/components/WorkflowNameShort.vue rename to packages/editor-ui/src/components/ShortenName.vue index eb9b73c063..dca88204b1 100644 --- a/packages/editor-ui/src/components/WorkflowNameShort.vue +++ b/packages/editor-ui/src/components/ShortenName.vue @@ -11,7 +11,7 @@ const DEFAULT_WORKFLOW_NAME_LIMIT = 25; const WORKFLOW_NAME_END_COUNT_TO_KEEP = 4; export default Vue.extend({ - name: "WorkflowNameShort", + name: "ShortenName", props: ["name", "limit"], computed: { shortenedName(): string { diff --git a/packages/editor-ui/src/components/TagsManager/TagsView/TagsView.vue b/packages/editor-ui/src/components/TagsManager/TagsView/TagsView.vue index ce0121e204..d4a42fc26b 100644 --- a/packages/editor-ui/src/components/TagsManager/TagsView/TagsView.vue +++ b/packages/editor-ui/src/components/TagsManager/TagsView/TagsView.vue @@ -57,15 +57,7 @@ export default Vue.extend({ }, rows(): ITagRow[] { const getUsage = (count: number | undefined) => count && count > 0 - ? this.$locale.baseText( - count > 1 ? - 'tagsView.inUse.plural' : 'tagsView.inUse.singular', - { - interpolate: { - count: count.toString(), - }, - }, - ) + ? this.$locale.baseText('tagsView.inUse', { adjustToNumber: count }) : this.$locale.baseText('tagsView.notBeingUsed'); const disabled = this.isCreateEnabled || this.$data.updateId || this.$data.deleteId; diff --git a/packages/editor-ui/src/components/mixins/nodeHelpers.ts b/packages/editor-ui/src/components/mixins/nodeHelpers.ts index f4c33408cb..6335a7e4c5 100644 --- a/packages/editor-ui/src/components/mixins/nodeHelpers.ts +++ b/packages/editor-ui/src/components/mixins/nodeHelpers.ts @@ -287,6 +287,9 @@ export const nodeHelpers = mixins( return []; } const executionData: IRunExecutionData = this.$store.getters.getWorkflowExecution.data; + if (!executionData || !executionData.resultData) { // unknown status + return []; + } const runData = executionData.resultData.runData; if (runData === null || runData[node.name] === undefined || diff --git a/packages/editor-ui/src/n8n-theme.scss b/packages/editor-ui/src/n8n-theme.scss index acd8ab1c75..120c2cb8a8 100644 --- a/packages/editor-ui/src/n8n-theme.scss +++ b/packages/editor-ui/src/n8n-theme.scss @@ -120,9 +120,6 @@ .el-tabs__nav:focus { outline: none; } -.el-tabs__item { - color: #555; -} .el-tabs__item.is-active { font-weight: bold; } @@ -185,3 +182,30 @@ } } } + +.add-option { + > * { + border: none; + } + + i.el-select__caret { + color: var(--color-foreground-xlight); + } + .el-input .el-input__inner { + &, + &:hover, + &:focus { + border-radius: 20px; + color: var(--color-foreground-xlight); + font-weight: 600; + background-color: var(--color-primary); + border-color: var(--color-primary); + text-align: center; + } + + &::placeholder { + color: var(--color-foreground-xlight); + opacity: 1; /** Firefox */ + } + } +} diff --git a/packages/editor-ui/src/plugins/components.ts b/packages/editor-ui/src/plugins/components.ts index 9a6cca9964..e902e1580c 100644 --- a/packages/editor-ui/src/plugins/components.ts +++ b/packages/editor-ui/src/plugins/components.ts @@ -41,6 +41,8 @@ import { Message, Notification, CollapseTransition, + Pagination, + Popover, N8nActionBox, N8nAvatar, @@ -60,8 +62,10 @@ import { N8nMenu, N8nMenuItem, N8nOption, + N8nRadioButtons, N8nSelect, N8nSpinner, + N8nTabs, N8nFormInputs, N8nFormBox, N8nSquareButton, @@ -81,7 +85,7 @@ Vue.use(N8nAvatar); Vue.use(N8nButton); Vue.component('n8n-form-box', N8nFormBox); Vue.component('n8n-form-inputs', N8nFormInputs); -Vue.use('n8n-icon', N8nIcon); +Vue.component('n8n-icon', N8nIcon); Vue.use(N8nIconButton); Vue.use(N8nInfoTip); Vue.use(N8nInput); @@ -96,8 +100,10 @@ Vue.use(N8nMenuItem); Vue.use(N8nOption); Vue.use(N8nSelect); Vue.use(N8nSpinner); +Vue.use(N8nRadioButtons); Vue.component('n8n-square-button', N8nSquareButton); Vue.use(N8nTags); +Vue.component('n8n-tabs', N8nTabs); Vue.use(N8nTag); Vue.component('n8n-text', N8nText); Vue.use(N8nTooltip); @@ -130,6 +136,9 @@ Vue.use(Badge); Vue.use(Card); Vue.use(ColorPicker); Vue.use(Container); +Vue.use(Pagination); +Vue.use(Popover); + Vue.use(VueAgile); Vue.component(CollapseTransition.name, CollapseTransition); diff --git a/packages/editor-ui/src/plugins/i18n/docs/ADDENDUM.md b/packages/editor-ui/src/plugins/i18n/docs/ADDENDUM.md index 8c19e032d2..644b26269c 100644 --- a/packages/editor-ui/src/plugins/i18n/docs/ADDENDUM.md +++ b/packages/editor-ui/src/plugins/i18n/docs/ADDENDUM.md @@ -2,16 +2,24 @@ ## Base text -### Interpolation +### Pluralization -Certain base text strings use [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) to allow for a variable to be passed in, signalled by curly braces: +Certain base text strings accept [singular and plural versions](https://kazupon.github.io/vue-i18n/guide/pluralization.html) separated by a `|` character: ```json { - "stopExecution": { - "message": "The execution with the ID {activeExecutionId} got stopped!", - "title": "Execution stopped" - } + "tagsView.inUse": "{count} workflow | {count} workflows", +} +``` + +### Interpolation + +Certain base text strings use [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) to allow for a variable between curly braces: + +```json +{ + "stopExecution.message": "The execution with the ID {activeExecutionId} got stopped!", + "stopExecution.title": "Execution stopped" } ``` @@ -19,10 +27,8 @@ When translating a string containing an interpolated variable, leave the variabl ```json { - "stopExecution": { - "message": "Die Ausführung mit der ID {activeExecutionId} wurde gestoppt", - "title": "Execution stopped" - } + "stopExecution.message": "Die Ausführung mit der ID {activeExecutionId} wurde gestoppt", + "stopExecution.title": "Execution stopped" } ``` @@ -32,18 +38,12 @@ As a convenience, the base text file may contain the special key `reusableBaseTe ```json { - "reusableBaseText": { - "save": "🇩🇪 Save", - }, - "duplicateWorkflowDialog": { - "enterWorkflowName": "🇩🇪 Enter workflow name", - "save": "@:reusableBaseText.save", - }, - "saveButton": { - "save": "@:reusableBaseText.save", - "saving": "🇩🇪 Saving", - "saved": "🇩🇪 Saved", - }, + "reusableBaseText.save": "🇩🇪 Save", + "duplicateWorkflowDialog.enterWorkflowName": "🇩🇪 Enter workflow name", + "duplicateWorkflowDialog.save": "@:reusableBaseText.save", + "saveButton.save": "@:reusableBaseText.save", + "saveButton.saving": "🇩🇪 Saving", + "saveButton.saved": "🇩🇪 Saved", } ``` @@ -92,23 +92,27 @@ Currently only the keys `oauth.clientId` and `oauth.clientSecret` are supported ```json { - "reusableDynamicText": { - "oauth2": { - "clientId": "🇩🇪 Client ID", - "clientSecret": "🇩🇪 Client Secret", - } - } + "reusableDynamicText.oauth2.clientId": "🇩🇪 Client ID", + "reusableDynamicText.oauth2.clientSecret": "🇩🇪 Client Secret", } ``` ### Special cases -`eventTriggerDescription` is a dynamic node property that is not part of node parameters. To translate it, set the `eventTriggerDescription` key at the root level of the `nodeView` property in the node translation file. +`eventTriggerDescription` and `activationMessage` are dynamic node properties that are not part of node parameters. To translate them, set the key at the root level of the `nodeView` property in the node translation file. + +Webhook node: ```json { - "nodeView": { - "eventTriggerDescription": "🇩🇪 Waiting for you to call the Test URL" - } + "nodeView.eventTriggerDescription": "🇩🇪 Waiting for you to call the Test URL", +} +``` + +Cron node: + +```json +{ + "nodeView.activationMessage": "🇩🇪 'Your cron trigger will now trigger executions on the schedule you have defined." } ``` diff --git a/packages/editor-ui/src/plugins/i18n/docs/README.md b/packages/editor-ui/src/plugins/i18n/docs/README.md index cdee3d747d..125d5a73be 100644 --- a/packages/editor-ui/src/plugins/i18n/docs/README.md +++ b/packages/editor-ui/src/plugins/i18n/docs/README.md @@ -6,6 +6,7 @@ n8n allows for internalization of the majority of UI text: - base text, e.g. menu display items in the left-hand sidebar menu, - node text, e.g. parameter display names and placeholders in the node view, +- credential text, e.g. parameter display names and placeholders in the credential modal, - header text, e.g. node display names and descriptions at various spots. Currently, n8n does _not_ allow for internalization of: @@ -55,12 +56,10 @@ Base text is rendered with no dependencies, i.e. base text is fixed and does not The base text file for each locale is located at `/packages/editor-ui/src/plugins/i18n/locales/` and is named `{localeIdentifier}.json`. Keys in the base text file can be Vue component dirs, Vue component names, and references to symbols in those Vue components. These keys are added by the team as the UI is modified or expanded. ```json -"nodeCreator": { - "categoryNames": { - "analytics": "🇩🇪 Analytics", - "communication": "🇩🇪 Communication", - "coreNodes": "🇩🇪 Core Nodes" - } +{ + "nodeCreator.categoryNames.analytics": "🇩🇪 Analytics", + "nodeCreator.categoryNames.communication": "🇩🇪 Communication", + "nodeCreator.categoryNames.coreNodes": "🇩🇪 Core Nodes" } ``` @@ -98,9 +97,9 @@ A credential translation file is placed at `/nodes-base/credentials/translations ``` credentials └── translations - └── de - ├── githubApi.json - └── githubOAuth2Api.json + └── de + ├── githubApi.json + └── githubOAuth2Api.json ``` Every credential must have its own credential translation file. @@ -123,9 +122,9 @@ GitHub ├── GitHub.node.ts ├── GitHubTrigger.node.ts └── translations - └── de - ├── github.json - └── githubTrigger.json + └── de + ├── github.json + └── githubTrigger.json ``` Every node must have its own node translation file. @@ -184,16 +183,10 @@ The object for each node credential parameter allows for the keys `displayName`, ```json { - "server": { - "displayName": "🇩🇪 Github Server", - "description": "🇩🇪 The server to connect to. Only has to be set if Github Enterprise is used.", - }, - "user": { - "placeholder": "🇩🇪 Hans", - }, - "accessToken": { - "placeholder": "🇩🇪 123", - }, + "server.displayName": "🇩🇪 Github Server", + "server.description": "🇩🇪 The server to connect to. Only has to be set if Github Enterprise is used.", + "user.placeholder": "🇩🇪 Hans", + "accessToken.placeholder": "🇩🇪 123", } ``` @@ -224,10 +217,8 @@ export class Github implements INodeType { ```json { - "header": { - "displayName": "🇩🇪 GitHub", - "description": "🇩🇪 Consume GitHub API", - }, + "header.displayName": "🇩🇪 GitHub", + "header.description": "🇩🇪 Consume GitHub API", } ``` @@ -264,11 +255,7 @@ export class Github implements INodeType { ```json { - "nodeView": { - "resource": { - "displayName": "🇩🇪 Resource", - }, - }, + "nodeView.resource.displayName": "🇩🇪 Resource", } ``` @@ -291,13 +278,9 @@ Allowed keys: `displayName`, `description`, `placeholder` ```json { - "nodeView": { - "owner": { - "displayName": "🇩🇪 Repository Owner", - "placeholder": "🇩🇪 n8n-io", - "description": "🇩🇪 Owner of the repository", - }, - }, + "nodeView.owner.displayName": "🇩🇪 Repository Owner", + "nodeView.owner.placeholder": "🇩🇪 n8n-io", + "nodeView.owner.description": "🇩🇪 Owner of the repository", } ``` @@ -333,20 +316,10 @@ Allowed subkeys: `options.{optionName}.displayName` and `options.{optionName}.de ```json { - "nodeView": { - "resource": { - "displayName": "🇩🇪 Resource", - "description": "🇩🇪 Resource to operate on", - "options": { - "file": { - "displayName": "🇩🇪 File", - }, - "issue": { - "displayName": "🇩🇪 Issue", - }, - }, - }, - }, + "nodeView.resource.displayName": "🇩🇪 Resource", + "nodeView.resource.description": "🇩🇪 Resource to operate on", + "nodeView.resource.options.file.displayName": "🇩🇪 File", + "nodeView.resource.options.issue.displayName": "🇩🇪 Issue", } ``` @@ -394,19 +367,11 @@ Example of `collection` parameter: ```json { - "nodeView": { - "labels": { - "displayName": "🇩🇪 Labels", - "multipleValueButtonText": "🇩🇪 Add Label", - "options": { - "label": { - "displayName": "🇩🇪 Label", - "description": "🇩🇪 Label to add to issue", - "placeholder": "🇩🇪 Some placeholder" - } - } - } - } + "nodeView.labels.displayName": "🇩🇪 Labels", + "nodeView.labels.multipleValueButtonText": "🇩🇪 Add Label", + "nodeView.labels.options.label.displayName": "🇩🇪 Label", + "nodeView.labels.options.label.description": "🇩🇪 Label to add to issue", + "nodeView.labels.options.label.placeholder": "🇩🇪 Some placeholder" } ``` @@ -461,29 +426,15 @@ Example of `fixedCollection` parameter: ```json { - "nodeView": { - "additionalParameters": { - "displayName": "🇩🇪 Additional Parameters", - "placeholder": "🇩🇪 Add Field", - "options": { - "author": { - "displayName": "🇩🇪 Author", - "values": { - "name": { - "displayName": "🇩🇪 Name", - "description": "🇩🇪 Name of the author of the commit", - "placeholder": "🇩🇪 Jan" - }, - "email": { - "displayName": "🇩🇪 Email", - "description": "🇩🇪 Email of the author of the commit", - "placeholder": "🇩🇪 jan@n8n.io" - } - } - }, - } - } - } + "nodeView.additionalParameters.displayName": "🇩🇪 Additional Parameters", + "nodeView.additionalParameters.placeholder": "🇩🇪 Add Field", + "nodeView.additionalParameters.options.author.displayName": "🇩🇪 Author", + "nodeView.additionalParameters.options.author.values.name.displayName": "🇩🇪 Name", + "nodeView.additionalParameters.options.author.values.name.description": "🇩🇪 Name of the author of the commit", + "nodeView.additionalParameters.options.author.values.name.placeholder": "🇩🇪 Jan", + "nodeView.additionalParameters.options.author.values.email.displayName": "🇩🇪 Email", + "nodeView.additionalParameters.options.author.values.email.description": "🇩🇪 Email of the author of the commit", + "nodeView.additionalParameters.options.author.values.email.placeholder": "🇩🇪 jan@n8n.io", } ``` diff --git a/packages/editor-ui/src/plugins/i18n/index.ts b/packages/editor-ui/src/plugins/i18n/index.ts index fe1d681bfa..89e064c830 100644 --- a/packages/editor-ui/src/plugins/i18n/index.ts +++ b/packages/editor-ui/src/plugins/i18n/index.ts @@ -63,8 +63,12 @@ export class I18nClass { */ baseText( key: string, - options?: { interpolate: { [key: string]: string } }, + options?: { adjustToNumber: number; interpolate: { [key: string]: string } }, ): string { + if (options && options.adjustToNumber) { + return this.i18n.tc(key, options.adjustToNumber, options && options.interpolate).toString(); + } + return this.i18n.t(key, options && options.interpolate).toString(); } diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 4405081688..b37ca0f446 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1,1093 +1,22 @@ { - "about": { - "aboutN8n": "About n8n", - "close": "Close", - "license": "License", - "n8nVersion": "n8n Version", - "n8nLicense": "Sustainable Use License", - "sourceCode": "Source Code" - }, - "binaryDataDisplay": { - "backToList": "Back to list", - "backToOverviewPage": "Back to overview page", - "noDataFoundToDisplay": "No data found to display", - "yourBrowserDoesNotSupport": "Your browser does not support the video element. Kindly update it to latest version." - }, - "codeEdit": { - "edit": "Edit" - }, - "collectionParameter": { - "choose": "Choose...", - "noProperties": "No properties" - }, - "credentialEdit": { - "credentialConfig": { - "accountConnected": "Account connected", - "clickToCopy": "Click To Copy", - "connectionTestedSuccessfully": "Connection tested successfully", - "couldntConnectWithTheseSettings": "Couldn’t connect with these settings", - "needHelpFillingOutTheseFields": "Need help filling out these fields?", - "oAuthRedirectUrl": "OAuth Redirect URL", - "openDocs": "Open docs", - "pleaseCheckTheErrorsBelow": "Please check the errors below", - "reconnect": "reconnect", - "reconnectOAuth2Credential": "Reconnect OAuth2 Credential", - "redirectUrlCopiedToClipboard": "Redirect URL copied to clipboard", - "retry": "Retry", - "retryCredentialTest": "Retry credential test", - "retrying": "Retrying", - "subtitle": "In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL", - "theServiceYouReConnectingTo": "the service you're connecting to" - }, - "credentialEdit": { - "confirmMessage": { - "beforeClose1": { - "cancelButtonText": "Keep Editing", - "confirmButtonText": "Close", - "headline": "Close without saving?", - "message": "Are you sure you want to throw away the changes you made to the {credentialDisplayName} credential?" - }, - "beforeClose2": { - "cancelButtonText": "Keep Editing", - "confirmButtonText": "Close", - "headline": "Close without connecting?", - "message": "You need to connect your credential for it to work" - }, - "deleteCredential": { - "cancelButtonText": "", - "confirmButtonText": "Yes, delete", - "headline": "Delete Credential?", - "message": "Are you sure you want to delete \"{savedCredentialName}\"?" - } - }, - "connection": "Connection", - "couldNotFindCredentialOfType": "Could not find credential of type", - "couldNotFindCredentialWithId": "Could not find credential with ID", - "details": "Details", - "delete": "Delete", - "saving": "Saving", - "showError": { - "createCredential": { - "title": "Problem creating credential" - }, - "deleteCredential": { - "title": "Problem deleting credential" - }, - "generateAuthorizationUrl": { - "message": "There was a problem generating the authorization URL", - "title": "OAuth Authorization Error" - }, - "loadCredential": { - "title": "Problem loading credential" - }, - "updateCredential": { - "title": "Problem updating credential" - } - }, - "showMessage": { - "title": "Credential deleted" - }, - "testing": "Testing" - }, - "credentialInfo": { - "allowUseBy": "Allow use by", - "created": "Created", - "id": "ID", - "lastModified": "Last modified" - }, - "oAuthButton": { - "connectMyAccount": "Connect my account", - "signInWithGoogle": "Sign in with Google" - } - }, - "credentialSelectModal": { - "addNewCredential": "Add new credential", - "continue": "Continue", - "searchForApp": "Search for app...", - "selectAnAppOrServiceToConnectTo": "Select an app or service to connect to" - }, - "credentialsList": { - "addNew": "Add New", - "confirmMessage": { - "cancelButtonText": "", - "confirmButtonText": "Yes, delete", - "headline": "Delete Credential?", - "message": "Are you sure you want to delete {credentialName}?" - }, - "createNewCredential": "Create New Credential", - "created": "Created", - "credentials": "Credentials", - "deleteCredential": "Delete Credential", - "editCredential": "Edit Credential", - "name": "@:reusableBaseText.name", - "operations": "Operations", - "showError": { - "deleteCredential": { - "title": "Problem deleting credential" - } - }, - "showMessage": { - "title": "Credential deleted" - }, - "type": "Type", - "updated": "Updated", - "yourSavedCredentials": "Your saved credentials", - "errorLoadingCredentials": "Error loading credentials" - }, - "dataDisplay": { - "needHelp": "Need help?", - "nodeDocumentation": "Node Documentation", - "openDocumentationFor": "Open {nodeTypeDisplayName} documentation" - }, - "displayWithChange": { - "cancelEdit": "Cancel Edit", - "clickToChange": "Click to Change", - "setValue": "Set Value" - }, - "duplicateWorkflowDialog": { - "cancel": "@:reusableBaseText.cancel", - "chooseOrCreateATag": "Choose or create a tag", - "duplicateWorkflow": "Duplicate Workflow", - "enterWorkflowName": "Enter workflow name", - "save": "@:reusableBaseText.save", - "showMessage": { - "message": "Please enter a name.", - "title": "Name missing" - } - }, - "executionDetails": { - "executionFailed": "Execution failed", - "executionId": "Execution ID", - "executionWaiting": "Execution waiting", - "executionWasSuccessful": "Execution was successful", - "openWorkflow": "Open Workflow", - "of": "of", - "workflow": "workflow", - "readOnly": { - "readOnly": "Read only", - "youreViewingTheLogOf": "You're viewing the log of a previous execution. You cannot
\n\t\tmake changes since this execution already occurred. Make changes
\n\t\tto this workflow by clicking on its name on the left." - } - }, - "executionsList": { - "allWorkflows": "All Workflows", - "anyStatus": "Any Status", - "autoRefresh": "Auto refresh", - "confirmMessage": { - "cancelButtonText": "", - "confirmButtonText": "Yes, delete", - "headline": "Delete Executions?", - "message": "Are you sure that you want to delete the {numSelected} selected execution(s)?" - }, - "deleteSelected": "Delete Selected", - "error": "Error", - "filters": "Filters", - "loadMore": "Load More", - "mode": "Mode", - "modes": { - "error": "error", - "integrated": "integrated", - "manual": "manual", - "retry": "retry", - "trigger": "trigger", - "webhook": "webhook" - }, - "name": "@:reusableBaseText.name", - "openPastExecution": "Open Past Execution", - "retryExecution": "Retry execution", - "retryOf": "Retry of", - "retryWithCurrentlySavedWorkflow": "Retry with currently saved workflow", - "retryWithOriginalworkflow": "Retry with original workflow", - "running": "Running", - "runningTime": "Running Time", - "selectStatus": "Select Status", - "selectWorkflow": "Select Workflow", - "selected": "Selected", - "showError": { - "handleDeleteSelected": { - "title": "Problem deleting executions" - }, - "loadMore": { - "title": "Problem loading workflows" - }, - "loadWorkflows": { - "title": "Problem loading workflows" - }, - "refreshData": { - "title": "Problem loading data" - }, - "retryExecution": { - "title": "Problem with retry" - }, - "stopExecution": { - "title": "Problem stopping execution" - } - }, - "showMessage": { - "handleDeleteSelected": { - "title": "Execution deleted" - }, - "retrySuccessfulFalse": { - "title": "Retry unsuccessful" - }, - "retrySuccessfulTrue": { - "title": "Retry successful" - }, - "stopExecution": { - "message": "Execution ID {activeExecutionId}", - "title": "Execution stopped" - } - }, - "startedAtId": "Started At / ID", - "status": "Status", - "statusTooltipText": { - "theWorkflowExecutionFailed": "The workflow execution failed.", - "theWorkflowExecutionFailedButTheRetryWasSuccessful": "The workflow execution failed but the retry {entryRetrySuccessId} was successful.", - "theWorkflowExecutionIsProbablyStillRunning": "The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ", - "theWorkflowExecutionWasARetryOfAndFailed": "The workflow execution was a retry of {entryRetryOf} and failed.
New retries have to be started from the original execution.", - "theWorkflowExecutionWasARetryOfAndItWasSuccessful": "The workflow execution was a retry of {entryRetryOf} and it was successful.", - "theWorkflowExecutionWasSuccessful": "The worklow execution was successful.", - "theWorkflowIsCurrentlyExecuting": "The worklow is currently executing.", - "theWorkflowIsWaitingIndefinitely": "The workflow is waiting indefinitely for an incoming webhook call.", - "theWorkflowIsWaitingTill": "The worklow is waiting till {waitDateDate} {waitDateTime}." - }, - "stopExecution": "Stop Execution", - "success": "Success", - "successRetry": "Success retry", - "unknown": "Unknown", - "unsavedWorkflow": "[UNSAVED WORKFLOW]", - "waiting": "Waiting", - "workflowExecutions": "Workflow Executions" - }, - "expressionEdit": { - "editExpression": "Edit Expression", - "expression": "Expression", - "result": "Result", - "variableSelector": "Variable Selector" - }, - "fixedCollectionParameter": { - "choose": "Choose...", - "currentlyNoItemsExist": "Currently no items exist", - "deleteItem": "Delete item", - "moveDown": "Move down", - "moveUp": "Move up" - }, - "genericHelpers": { - "loading": "Loading", - "min": "min", - "sec": "sec", - "showMessage": { - "message": "This is a read-only version of the workflow. To make changes, either open the original workflow or save it under a new name.", - "title": "Workflow cannot be changed" - } - }, - "mainSidebar": { - "aboutN8n": "About n8n", - "confirmMessage": { - "workflowDelete": { - "cancelButtonText": "", - "confirmButtonText": "Yes, delete", - "headline": "Delete Workflow?", - "message": "Are you sure that you want to delete '{workflowName}'?" - }, - "workflowNew": { - "cancelButtonText": "Leave without saving", - "confirmButtonText": "Save", - "headline": "Save changes before leaving?", - "message": "If you don't save, you will lose your changes." - } - }, - "credentials": "Credentials", - "delete": "Delete", - "download": "Download", - "duplicate": "Duplicate", - "executions": "Executions", - "help": "Help", - "helpMenuItems": { - "course": "Course", - "documentation": "Documentation", - "forum": "Forum" - }, - "importFromFile": "Import from File", - "importFromUrl": "Import from URL", - "new": "New", - "newTemplate": "New from template", - "open": "Open", - "prompt": { - "cancel": "@:reusableBaseText.cancel", - "import": "Import", - "importWorkflowFromUrl": "Import Workflow from URL", - "invalidUrl": "Invalid URL", - "workflowUrl": "Workflow URL" - }, - "save": "@:reusableBaseText.save", - "settings": "Settings", - "showError": { - "stopExecution": { - "title": "Problem stopping execution" - } - }, - "showMessage": { - "handleFileImport": { - "message": "The file does not contain valid JSON data", - "title": "Could not import file" - }, - "handleSelect1": { - "title": "Workflow deleted" - }, - "handleSelect2": { - "title": "Workflow created" - }, - "handleSelect3": { - "title": "Workflow created" - }, - "stopExecution": { - "title": "Execution stopped" - } - }, - "templates": "Templates", - "workflows": "Workflows" - }, - "multipleParameter": { - "addItem": "Add item", - "currentlyNoItemsExist": "Currently no items exist", - "deleteItem": "Delete item", - "moveDown": "Move down", - "moveUp": "Move up" - }, - "noTagsView": { - "readyToOrganizeYourWorkflows": "Ready to organize your workflows?", - "withWorkflowTagsYouReFree": "With workflow tags, you're free to create the perfect tagging system for your flows" - }, - "node": { - "activateDeactivateNode": "Activate/Deactivate Node", - "deleteNode": "Delete Node", - "disabled": "Disabled", - "duplicateNode": "Duplicate Node", - "editNode": "Edit Node", - "executeNode": "Execute Node", - "issues": "Issues", - "nodeIsExecuting": "Node is executing", - "nodeIsWaitingTill": "Node is waiting until {date} {time}", - "theNodeIsWaitingIndefinitelyForAnIncomingWebhookCall": "The node is waiting for an incoming webhook call (indefinitely)", - "waitingForYouToCreateAnEventIn": "Waiting for you to create an event in {nodeType}" - }, - "nodeCreator": { - "categoryNames": { - "analytics": "Analytics", - "communication": "Communication", - "coreNodes": "Core Nodes", - "customNodes": "Custom Nodes", - "dataStorage": "Data & Storage", - "development": "Development", - "financeAccounting": "Finance & Accounting", - "marketingContent": "Marketing & Content", - "miscellaneous": "Miscellaneous", - "productivity": "Productivity", - "sales": "Sales", - "suggestedNodes": "Suggested Nodes ✨", - "utility": "Utility" - }, - "mainPanel": { - "all": "All", - "regular": "Regular", - "trigger": "Trigger" - }, - "noResults": { - "dontWorryYouCanProbablyDoItWithThe": "Don’t worry, you can probably do it with the", - "httpRequest": "HTTP Request", - "node": "node", - "or": "or", - "requestTheNode": "Request the node", - "wantUsToMakeItFaster": "Want us to make it faster?", - "weDidntMakeThatYet": "We didn't make that... yet", - "webhook": "Webhook" - }, - "searchBar": { - "searchNodes": "Search nodes..." - }, - "subcategoryDescriptions": { - "dataTransformation": "Manipulate data fields, run code", - "files": "Work with CSV, XML, text, images etc.", - "flow": "Branches, core triggers, merge data", - "helpers": "HTTP Requests (API calls), date and time, scrape HTML" - }, - "subcategoryNames": { - "dataTransformation": "Data Transformation", - "files": "Files", - "flow": "Flow", - "helpers": "Helpers" - } - }, - "nodeCredentials": { - "createNew": "Create New", - "credentialFor": "Credential for {credentialType}", - "issues": "Issues", - "selectCredential": "Select Credential", - "showMessage": { - "message": "Nodes that used credential \"{oldCredentialName}\" have been updated to use \"{newCredentialName}\"", - "title": "Node credential updated" - }, - "updateCredential": "Update Credential" - }, - "nodeErrorView": { - "cause": "Cause", - "copyToClipboard": "Copy to Clipboard", - "dataBelowMayContain": "Data below may contain sensitive information. Proceed with caution when sharing.", - "details": "Details", - "error": "ERROR", - "httpCode": "HTTP Code", - "showMessage": { - "title": "Copied to clipboard" - }, - "stack": "Stack", - "theErrorCauseIsTooLargeToBeDisplayed": "The error cause is too large to be displayed", - "time": "Time" - }, - "nodeBase": { - "clickToAddNodeOrDragToConnect": "Click to add node
or drag to connect" - }, - "nodeSettings": { - "alwaysOutputData": { - "description": "If active, will output a single, empty item when the output would have been empty. Use to prevent the workflow finishing on this node.", - "displayName": "Always Output Data" - }, - "clickOnTheQuestionMarkIcon": "Click the '?' icon to open this node on n8n.io", - "continueOnFail": { - "description": "If active, the workflow continues even if this node's execution fails. When this occurs, the node passes along input data from previous nodes - so your workflow should account for unexpected output data.", - "displayName": "Continue On Fail" - }, - "executeOnce": { - "description": "If active, the node executes only once, with data from the first item it receives", - "displayName": "Execute Once" - }, - "maxTries": { - "description": "Number of times to attempt to execute the node before failing the execution", - "displayName": "Max. Tries" - }, - "noDescriptionFound": "No description found", - "nodeDescription": "Node Description", - "notes": { - "description": "Optional note to save with the node", - "displayName": "Notes" - }, - "notesInFlow": { - "description": "If active, the note above will display in the flow as a subtitle", - "displayName": "Display note in flow?" - }, - "parameters": "Parameters", - "retryOnFail": { - "description": "If active, the node tries to execute again when it fails", - "displayName": "Retry On Fail" - }, - "settings": "Settings", - "theNodeIsNotValidAsItsTypeIsUnknown": "The node is not valid as its type ({nodeType}) is unknown", - "thisNodeDoesNotHaveAnyParameters": "This node does not have any parameters", - "waitBetweenTries": { - "description": "How long to wait between each attempt (in milliseconds)", - "displayName": "Wait Between Tries (ms)" - } - }, - "nodeView": { - "addNode": "Add node", - "confirmMessage": { - "beforeRouteLeave": { - "cancelButtonText": "Leave without saving", - "confirmButtonText": "Save", - "headline": "Save changes before leaving?", - "message": "If you don't save, you will lose your changes." - }, - "initView": { - "cancelButtonText": "Leave without saving", - "confirmButtonText": "Save", - "headline": "Save changes before leaving?", - "message": "If you don't save, you will lose your changes." - }, - "receivedCopyPasteData": { - "cancelButtonText": "", - "confirmButtonText": "Yes, import", - "headline": "Import Workflow?", - "message": "Workflow will be imported from
{plainTextData}" - } - }, - "couldntImportWorkflow": "Could not import workflow", - "deletesTheCurrentExecutionData": "Deletes the current execution data", - "executesTheWorkflowFromTheStartOrWebhookNode": "Executes the workflow from the 'start' or 'webhook' node", - "itLooksLikeYouHaveBeenEditingSomething": "It looks like you made some edits. If you leave before saving, your changes will be lost.", - "loadingTemplate": "Loading template", - "moreInfo": "More info", - "noNodesGivenToAdd": "No nodes to add specified", - "prompt": { - "cancel": "@:reusableBaseText.cancel", - "invalidName": "Invalid Name", - "newName": "New Name", - "rename": "Rename", - "renameNode": "Rename Node" - }, - "redirecting": "Redirecting", - "refresh": "Refresh", - "resetZoom": "Reset Zoom", - "runButtonText": { - "executeWorkflow": "Execute Workflow", - "executingWorkflow": "Executing Workflow", - "waitingForTriggerEvent": "Waiting for Trigger Event" - }, - "showError": { - "getWorkflowDataFromUrl": { - "title": "Problem loading workflow" - }, - "importWorkflowData": { - "title": "Problem importing workflow" - }, - "mounted1": { - "message": "There was a problem loading init data", - "title": "Init Problem" - }, - "mounted2": { - "message": "There was a problem initializing the workflow", - "title": "Init Problem" - }, - "openExecution": { - "title": "Problem loading execution" - }, - "openWorkflow": { - "title": "Problem opening workflow" - }, - "stopExecution": { - "title": "Problem stopping execution" - }, - "stopWaitingForWebhook": { - "title": "Problem deleting test webhook" - } - }, - "showMessage": { - "addNodeButton": { - "message": "'{nodeTypeName}' is an unknown node type", - "title": "Could not create node" - }, - "keyDown": { - "title": "Workflow created" - }, - "showMaxNodeTypeError": { - "message": { - "plural": "Only {maxNodes} '{nodeTypeDataDisplayName}' nodes are allowed in a workflow", - "singular": "Only {maxNodes} '{nodeTypeDataDisplayName}' node is allowed in a workflow" - }, - "title": "Could not create node" - }, - "stopExecutionCatch": { - "message": "It completed before it could be stopped", - "title": "Workflow finished executing" - }, - "stopExecutionTry": { - "title": "Execution stopped" - }, - "stopWaitingForWebhook": { - "title": "Webhook deleted" - } - }, - "stopCurrentExecution": "Stop current execution", - "stopWaitingForWebhookCall": "Stop waiting for webhook call", - "stoppingCurrentExecution": "Stopping current execution", - "thereWasAProblemLoadingTheNodeParametersOfNode": "There was a problem loading the parameters of the node", - "thisExecutionHasntFinishedYet": "This execution hasn't finished yet", - "toSeeTheLatestStatus": "to see the latest status", - "workflowTemplateWithIdCouldNotBeFound": "Workflow template with ID \"{templateId}\" could not be found", - "workflowWithIdCouldNotBeFound": "Workflow with ID \"{workflowId}\" could not be found", - "zoomIn": "Zoom In", - "zoomOut": "Zoom Out", - "zoomToFit": "Zoom to Fit" - }, - "nodeWebhooks": { - "clickToCopyWebhookUrls": "Click to copy webhook URLs", - "clickToDisplayWebhookUrls": "Click to display webhook URLs", - "clickToHideWebhookUrls": "Click to hide webhook URLs", - "invalidExpression": "[INVALID EXPRESSION]", - "productionUrl": "Production URL", - "showMessage": { - "title": "URL copied" - }, - "testUrl": "Test URL", - "webhookUrls": "Webhook URLs" - }, - "parameterInput": { - "addExpression": "Add Expression", - "error": "ERROR", - "issues": "Issues", - "loadingOptions": "Loading options...", - "openEditWindow": "Open Edit Window", - "parameter": "Parameter: \"{shortPath}\"", - "parameterHasExpression": "Parameter: \"{shortPath}\" has an expression", - "parameterHasIssues": "Parameter: \"{shortPath}\" has issues", - "parameterHasIssuesAndExpression": "Parameter: \"{shortPath}\" has issues and an expression", - "parameterOptions": "Parameter Options", - "refreshList": "Refresh List", - "removeExpression": "Remove Expression", - "resetValue": "Reset Value", - "selectDateAndTime": "Select date and time", - "select": "Select" - }, - "parameterInputExpanded": { - "openDocs": "Open docs", - "thisFieldIsRequired": "This field is required" - }, - "parameterInputList": { - "delete": "Delete", - "deleteParameter": "Delete Parameter", - "parameterOptions": "Parameter Options" - }, - "personalizationModal": { - "automationConsulting": "Automation consulting", - "continue": "Continue", - "customizeN8n": "Customize n8n to you", - "eCommerce": "eCommerce", - "errorWhileSubmittingResults": "Error while submitting results", - "executiveTeam": "Executive team", - "financeOrInsurance": "Finance / Insurance", - "getStarted": "Get started", - "government": "Government", - "healthcare": "Healthcare", - "howAreYourCodingSkills": "How are your coding skills?", - "howBigIsYourCompany": "How big is your company?", - "hr": "HR", - "it": "IT", - "iCanCodeSomeUsefulThingsBut": "2. I can code some useful things, but I spend a lot of time stuck", - "iCanDoAlmostAnythingIWant": "5. I can do almost anything I want, easily (pro coder)", - "iCanFigureMostThingsOut": "4. I can figure most things out", - "iGetStuckTooQuicklyToAchieveMuch": "1. I get stuck too quickly to achieve much", - "iKnowEnoughToBeDangerousBut": "3. I know enough to be dangerous, but I'm no expert", - "imNotUsingN8nForWork": "I'm not using n8n for work", - "individualConsumers": "Customers are individual consumers", - "smallBusinesses": "Customers are small businesses (under 20 employees)", - "mediumBusinesses": "Customers are medium businesses (20-499 employees)", - "largeBusinesses": "Customers are large businesses (500+ employees)", - "whatKindOfCustomersDoYouServe": "What kind of customers do you serve?", - "cloudInfrastructure": "Cloud infrastructure", - "itSupport": "IT support", - "networkingOrCommunication": "Networking / Communication", - "itEngineering": "IT / Engineering", - "legal": "Legal", - "lessThan20People": "Less than 20 people", - "lookOutForThingsMarked": "Look out for things marked with a ✨. They are personalized to make n8n more relevant to you.", - "marketing": "Marketing", - "media": "Media", - "manufacturing": "Manufacturing", - "managedServiceProvider": "Managed service provider", - "digitalAgencyOrConsultant": "Digital agency/consultant", - "automationAgencyOrConsultant": "Automation agency/consultant", - "neverCoded": "0. Never coded", - "operations": "Operations", - "otherPleaseSpecify": "Other (please specify)", - "other": "Other", - "people": "people", - "proCoder": "Pro coder", - "physicalRetailOrServices": "Physical retail or services", - "realEstateOrConstruction": "Real estate / Construction", - "saas": "SaaS", - "education": "Education", - "salesBizDev": "Sales / Bizdev", - "salesBusinessDevelopment": "Sales / Business Development", - "security": "Security", - "select": "Select...", - "specifyYourCompanysIndustry": "Specify your company's industry", - "specifyYourWorkArea": "Specify your work area", - "support": "Support", - "systemsIntegration": "Systems Integration", - "systemsIntegrator": "Systems Integrator", - "thanks": "Thanks!", - "telecoms": "Telecoms", - "theseQuestionsHelpUs": "These questions help us tailor n8n to you", - "whichIndustriesIsYourCompanyIn": "Which industries is your company in?", - "whatBestDescribesYourCompany": "What best describes your company?", - "whatDoesYourCompanyFocusOn": "Which services does your company focus on?", - "pleaseSpecifyYourCompanyFocus": "Please specify your company focus", - "whatAreYouLookingToAutomate": "What are you looking to automate?", - "customerIntegrations": "Customer integrations", - "customerSupport": "Customer support", - "financeOrAccounting": "Finance / Accounting", - "product": "Product (e.g. fast prototyping)", - "salesAndMarketing": "Sales and Marketing", - "notSureYet": "Not sure yet", - "specifyYourAutomationGoal": "Please specify your automation goal" - }, - "pushConnection": { - "showMessage": { - "title": "Workflow executed successfully" - } - }, - "pushConnectionTracker": { - "cannotConnectToServer": "You have a connection issue or the server is down.
n8n should reconnect automatically once the issue is resolved.", - "connectionLost": "Connection lost" - }, - "reusableBaseText": { - "cancel": "Cancel", - "name": "Name", - "save": "Save" - }, - "reusableDynamicText": { - "oauth2": { - "clientId": "Client ID", - "clientSecret": "Client Secret" - } - }, - "runData": { - "binary": "Binary", - "copyItemPath": "Copy Item Path", - "copyParameterPath": "Copy Parameter Path", - "copyToClipboard": "Copy to Clipboard", - "copyValue": "Copy Value", - "dataOfExecution": "Data of Execution", - "dataReturnedByThisNodeWillDisplayHere": "Data returned by this node will display here", - "displayDataAnyway": "Display Data Anyway", - "entriesExistButThey": "Entries exist but they do not contain any JSON data", - "executeNode": "Execute Node", - "executesThisNodeAfterExecuting": "Executes this {nodeName} node after executing any previous nodes that have not yet returned data", - "executionTime": "Execution Time", - "fileExtension": "File Extension", - "fileName": "File Name", - "items": "Items", - "json": "JSON", - "mimeType": "Mime Type", - "ms": "ms", - "noBinaryDataFound": "No binary data found", - "noData": "No data", - "noTextDataFound": "No text data found", - "nodeReturnedALargeAmountOfData": "Node returned a large amount of data", - "output": "Output", - "downloadBinaryData": "Download", - "showBinaryData": "View", - "startTime": "Start Time", - "table": "Table", - "theNodeContains": "The node contains {numberOfKb} KB of data.
Displaying it could cause problems.

If you do decide to display it, consider avoiding the JSON view." - }, - "saveButton": { - "save": "@:reusableBaseText.save", - "saved": "Saved", - "saving": "Saving" - }, - "showMessage": { - "cancel": "@:reusableBaseText.cancel", - "ok": "OK", - "showDetails": "Show Details" - }, - "tagsDropdown": { - "createTag": "Create tag \"{filter}\"", - "manageTags": "Manage tags", - "noMatchingTagsExist": "No matching tags exist", - "noTagsExist": "No tags exist", - "showError": { - "message": "A problem occurred when trying to create the '{name}' tag", - "title": "Could not create tag" - }, - "typeToCreateATag": "Type to create a tag" - }, - "tagsManager": { - "couldNotDeleteTag": "Could not delete tag", - "done": "Done", - "manageTags": "Manage tags", - "showError": { - "onCreate": { - "message": "A problem occurred when trying to create the tag '{escapedName}'", - "title": "Could not create tag" - }, - "onDelete": { - "message": "A problem occurred when trying to delete the tag '{escapedName}'", - "title": "Could not delete tag" - }, - "onUpdate": { - "message": "A problem occurred when trying to update the tag '{escapedName}'", - "title": "Could not update tag" - } - }, - "showMessage": { - "onDelete": { - "title": "Tag deleted" - }, - "onUpdate": { - "title": "Tag updated" - } - }, - "tagNameCannotBeEmpty": "Tag name cannot be empty" - }, - "tagsTable": { - "areYouSureYouWantToDeleteThisTag": "Are you sure you want to delete this tag?", - "cancel": "@:reusableBaseText.cancel", - "createTag": "Create tag", - "deleteTag": "Delete tag", - "editTag": "Edit Tag", - "name": "@:reusableBaseText.name", - "noMatchingTagsExist": "No matching tags exist", - "saveChanges": "Save changes?", - "usage": "Usage" - }, - "tagsTableHeader": { - "addNew": "Add new", - "searchTags": "Search Tags" - }, - "tagsView": { - "inUse": { - "plural": "{count} workflows", - "singular": "{count} workflow" - }, - "notBeingUsed": "Not being used" - }, - "template": { - "buttons": { - "goBackButton": "Go back", - "useThisWorkflowButton": "Use this workflow" - }, - "details": { - "appsInTheWorkflow": "Apps in this workflow", - "appsInTheCollection": "This collection features", - "by": "by", - "categories": "Categories", - "created": "Created", - "details": "Details", - "times": "times", - "viewed": "Viewed" - } - }, - "templates": { - "allCategories": "All Categories", - "categoriesHeading": "Categories", - "collection": "Collection", - "collections": "Collections", - "collectionsNotFound": "Collection could not be found", - "endResult": "Share your own useful workflows through your n8n.io account", - "heading": "Workflow templates", - "newButton": "New blank workflow", - "noSearchResults": "Nothing found. Try adjusting your search to see more.", - "searchPlaceholder": "Search workflows", - "workflow": "Workflow", - "workflows": "Workflows", - "workflowsNotFound": "Workflow could not be found", - "connectionWarning": "⚠️ There was a problem fetching workflow templates. Check your internet connection." - }, - "textEdit": { - "edit": "Edit" - }, - "timeAgo": { - "daysAgo": "%s days ago", - "hoursAgo": "%s hours ago", - "inDays": "in %s days", - "inHours": "in %s hours", - "inMinutes": "in %s minutes", - "inMonths": "in %s months", - "inOneDay": "in 1 day", - "inOneHour": "in 1 hour", - "inOneMinute": "in 1 minute", - "inOneMonth": "in 1 month", - "inOneWeek": "in 1 week", - "inOneYear": "in 1 year", - "inWeeks": "in %s weeks", - "inYears": "in %s years", - "justNow": "Just now", - "minutesAgo": "%s minutes ago", - "monthsAgo": "%s months ago", - "oneDayAgo": "1 day ago", - "oneHourAgo": "1 hour ago", - "oneMinuteAgo": "1 minute ago", - "oneMonthAgo": "1 month ago", - "oneWeekAgo": "1 week ago", - "oneYearAgo": "1 year ago", - "rightNow": "Right now", - "weeksAgo": "%s weeks ago", - "yearsAgo": "%s years ago" - }, - "updatesPanel": { - "andIs": "and is", - "behindTheLatest": "behind the latest and greatest n8n", - "howToUpdateYourN8nVersion": "How to update your n8n version", - "version": "{numberOfVersions} version{howManySuffix}", - "weVeBeenBusy": "We’ve been busy ✨", - "youReOnVersion": "You’re on {currentVersionName}, which was released" - }, - "variableSelector": { - "context": "Context", - "currentNode": "Current Node", - "nodes": "Nodes", - "outputData": "Output Data", - "parameters": "Parameters", - "variableFilter": "Variable filter..." - }, - "variableSelectorItem": { - "empty": "--- EMPTY ---", - "selectItem": "Select Item" - }, - "versionCard": { - "breakingChanges": "Breaking changes", - "released": "Released", - "securityUpdate": "Security update", - "thisVersionHasASecurityIssue": "This version has a security issue.
It is listed here for completeness.", - "unknown": "unknown", - "version": "Version" - }, - "workflowActivator": { - "activateWorkflow": "Activate workflow", - "deactivateWorkflow": "Deactivate workflow", - "showError": { - "title": "Workflow could not be {newStateName}" - }, - "showMessage": { - "activeChangedNodesIssuesExistTrue": { - "message": "Please resolve outstanding issues before you activate it", - "title": "Problem activating workflow" - }, - "activeChangedWorkflowIdUndefined": { - "message": "Please save it before activating", - "title": "Problem activating workflow" - }, - "displayActivationError": { - "message": { - "catchBlock": "Sorry there was a problem requesting the error", - "errorDataNotUndefined": "The following error occurred on workflow activation:
{message}", - "errorDataUndefined": "Unknown error" - }, - "title": "Problem activating workflow" - } - }, - "theWorkflowIsSetToBeActiveBut": "The workflow is activated but could not be started.
Click to display error message.", - "thisWorkflowHasNoTriggerNodes": "This workflow has no trigger nodes that require activation" - }, - "workflowDetails": { - "active": "Active", - "addTag": "Add tag", - "showMessage": { - "message": "Please enter a name, or press 'esc' to go back to the old one", - "title": "Name missing" - }, - "chooseOrCreateATag": "Choose or create a tag" - }, - "workflowHelpers": { - "showMessage": { - "title": "Problem saving workflow" - } - }, - "workflowOpen": { - "active": "Active", - "confirmMessage": { - "cancelButtonText": "Leave without saving", - "confirmButtonText": "Save", - "headline": "Save changes before leaving?", - "message": "If you don't save, you will lose your changes." - }, - "created": "Created", - "name": "@:reusableBaseText.name", - "openWorkflow": "Open Workflow", - "filterWorkflows": "Filter by tags", - "searchWorkflows": "Search workflows...", - "showError": { - "title": "Problem loading workflows" - }, - "showMessage": { - "message": "This is the current workflow", - "title": "Workflow already open" - }, - "updated": "Updated", - "couldNotLoadActiveWorkflows": "Could not load active workflows" - }, - "workflowRun": { - "noActiveConnectionToTheServer": "Lost connection to the server", - "showError": { - "title": "Problem running workflow" - }, - "showMessage": { - "message": "Please fix them before executing", - "title": "Workflow has issues" - } - }, - "workflowSettings": { - "defaultTimezone": "Default - {defaultTimezoneValue}", - "defaultTimezoneNotValid": "Default Timezone not valid", - "errorWorkflow": "Error Workflow", - "helpTexts": { - "errorWorkflow": "A second workflow to run if the current one fails.
The second workflow should an 'Error Trigger' node.", - "executionTimeout": "How long the workflow should wait before timing out", - "executionTimeoutToggle": "Whether to cancel workflow execution after a defined time", - "saveDataErrorExecution": "Whether to save data of executions that fail", - "saveDataSuccessExecution": "Whether to save data of executions that finish successfully", - "saveExecutionProgress": "Whether to save data after each node execution. This allows you to resume from where execution stopped if there is an error, but may increase latency.", - "saveManualExecutions": "Whether to save data of executions that are started manually from the editor", - "timezone": "The timezone in which the workflow should run. Used by 'cron' node, for example." - }, - "hours": "hours", - "minutes": "minutes", - "noWorkflow": "- No Workflow -", - "save": "@:reusableBaseText.save", - "saveDataErrorExecution": "Save failed executions", - "saveDataErrorExecutionOptions": { - "defaultSave": "Default - {defaultValue}", - "doNotSave": "Do not save", - "save": "@:reusableBaseText.save" - }, - "saveDataSuccessExecution": "Save successful executions", - "saveDataSuccessExecutionOptions": { - "defaultSave": "Default - {defaultValue}", - "doNotSave": "Do not save", - "save": "@:reusableBaseText.save" - }, - "saveExecutionProgress": "Save execution progress", - "saveExecutionProgressOptions": { - "defaultSave": "Default - {defaultValue}", - "no": "No", - "yes": "Yes" - }, - "saveManualExecutions": "Save manual executions", - "saveManualOptions": { - "defaultSave": "Default - {defaultValue}", - "no": "No", - "yes": "Yes" - }, - "seconds": "seconds", - "selectOption": "Select Option", - "settingsFor": "Settings for {workflowName} (#{workflowId})", - "showError": { - "saveSettings1": { - "errorMessage": "Timeout is activated but set to 0", - "message": "There was a problem saving the settings", - "title": "Problem saving settings" - }, - "saveSettings2": { - "errorMessage": "Maximum Timeout is: {hours} hours, {minutes} minutes, {seconds} seconds", - "message": "The timeout is longer than allowed", - "title": "Problem saving settings" - }, - "saveSettings3": { - "title": "Problem saving settings" - } - }, - "showMessage": { - "saveSettings": { - "title": "Workflow settings saved" - } - }, - "timeoutAfter": "Timeout After", - "timeoutWorkflow": "Timeout Workflow", - "timezone": "Timezone" - }, - "activationModal": { - "workflowActivated": "Workflow activated", - "theseExecutionsWillNotShowUp": "These executions will not show up immediately in the editor,", - "butYouCanSeeThem": "but you can see them in the", - "executionList": "execution list", - "ifYouChooseTo": "if you choose to", - "saveExecutions": "save executions.", - "dontShowAgain": "Don't show again", - "yourTriggersWillNowFire": "Your triggers will now fire production executions automatically.", - "yourTriggerWillNowFire": "Your trigger will now fire production executions automatically.", - "yourWorkflowWillNowRegularlyCheck": "Your workflow will now regularly check {serviceName} for events and trigger executions for them.", - "yourWorkflowWillNowListenForEvents": "Your workflow will now listen for events from {serviceName} and trigger executions.", - "gotIt": "Got it" - }, - "workflowPreview": { - "showError": { - "previewError": { - "message": "Unable to preview workflow", - "title": "Preview error" - }, - "missingWorkflow": "Missing workflow", - "arrayEmpty": "Must have an array of nodes" - } - }, + "about.aboutN8n": "About n8n", + "about.close": "Close", + "about.license": "License", + "about.n8nLicense": "Sustainable Use License", + "about.n8nVersion": "n8n Version", + "about.sourceCode": "Source Code", + "activationModal.butYouCanSeeThem": "but you can see them in the", + "activationModal.dontShowAgain": "Don't show again", + "activationModal.executionList": "execution list", + "activationModal.gotIt": "Got it", + "activationModal.ifYouChooseTo": "if you choose to", + "activationModal.saveExecutions": "save executions.", + "activationModal.theseExecutionsWillNotShowUp": "These executions will not show up immediately in the editor,", + "activationModal.workflowActivated": "Workflow activated", + "activationModal.yourTriggerWillNowFire": "Your trigger will now fire production executions automatically.", + "activationModal.yourTriggersWillNowFire": "Your triggers will now fire production executions automatically.", + "activationModal.yourWorkflowWillNowListenForEvents": "Your workflow will now listen for events from {serviceName} and trigger executions.", + "activationModal.yourWorkflowWillNowRegularlyCheck": "Your workflow will now regularly check {serviceName} for events and trigger executions for them.", "auth.changePassword": "Change Password", "auth.changePassword.currentPassword": "Current Password", "auth.changePassword.error": "Problem changing the password", @@ -1107,22 +36,22 @@ "auth.role": "Role", "auth.roles.member": "Member", "auth.roles.owner": "Owner", - "auth.setup.setupOwner": "Set up owner account", - "auth.setup.skipOwnerSetupQuestion": "Skip owner account setup?", - "auth.setup.skipSetup": "Skip setup", - "auth.setup.goBack": "Go back", "auth.setup.confirmOwnerSetup": "Set up owner account?", - "auth.setup.setupConfirmation.oneWorkflowCount": "{count} existing workflow", - "auth.setup.setupConfirmation.oneCredentialCount": "{count} credential", - "auth.setup.setupConfirmation.workflowsCount": "{count} existing workflows", - "auth.setup.setupConfirmation.credentialsCount": "{count} credentials", - "auth.setup.setupConfirmation.concatEntities": "{workflows} and {credentials}", "auth.setup.confirmOwnerSetupMessage": "To give others access to your {entities}, you’ll need to share these account details with them. Or you can continue as before with no account, by going back and skipping this setup. More info", "auth.setup.createAccount": "Create account", - "auth.setup.skipSetupTemporarily": "Skip setup for now", + "auth.setup.goBack": "Go back", "auth.setup.next": "Next", "auth.setup.ownerAccountBenefits": "Setting up an owner account allows you to invite others, and prevents people using n8n without an account", "auth.setup.settingUpOwnerError": "Problem setting up owner", + "auth.setup.setupConfirmation.concatEntities": "{workflows} and {credentials}", + "auth.setup.setupConfirmation.credentialsCount": "{count} credentials", + "auth.setup.setupConfirmation.oneCredentialCount": "{count} credential", + "auth.setup.setupConfirmation.oneWorkflowCount": "{count} existing workflow", + "auth.setup.setupConfirmation.workflowsCount": "{count} existing workflows", + "auth.setup.setupOwner": "Set up owner account", + "auth.setup.skipOwnerSetupQuestion": "Skip owner account setup?", + "auth.setup.skipSetup": "Skip setup", + "auth.setup.skipSetupTemporarily": "Skip setup for now", "auth.signin": "Sign in", "auth.signin.error": "Problem logging in", "auth.signout": "Sign out", @@ -1132,9 +61,175 @@ "auth.signup.setupYourAccount": "Set up your account", "auth.signup.setupYourAccountError": "Problem setting up your account", "auth.signup.tokenValidationError": "Issue validating invite token", + "binaryDataDisplay.backToList": "Back to list", + "binaryDataDisplay.backToOverviewPage": "Back to overview page", + "binaryDataDisplay.noDataFoundToDisplay": "No data found to display", + "binaryDataDisplay.yourBrowserDoesNotSupport": "Your browser does not support the video element. Kindly update it to latest version.", + "codeEdit.edit": "Edit", + "collectionParameter.choose": "Choose...", + "collectionParameter.noProperties": "No properties", + "credentialEdit.credentialConfig.accountConnected": "Account connected", + "credentialEdit.credentialConfig.clickToCopy": "Click To Copy", + "credentialEdit.credentialConfig.connectionTestedSuccessfully": "Connection tested successfully", + "credentialEdit.credentialConfig.couldntConnectWithTheseSettings": "Couldn’t connect with these settings", + "credentialEdit.credentialConfig.needHelpFillingOutTheseFields": "Need help filling out these fields?", + "credentialEdit.credentialConfig.oAuthRedirectUrl": "OAuth Redirect URL", + "credentialEdit.credentialConfig.openDocs": "Open docs", + "credentialEdit.credentialConfig.pleaseCheckTheErrorsBelow": "Please check the errors below", + "credentialEdit.credentialConfig.reconnect": "reconnect", + "credentialEdit.credentialConfig.reconnectOAuth2Credential": "Reconnect OAuth2 Credential", + "credentialEdit.credentialConfig.redirectUrlCopiedToClipboard": "Redirect URL copied to clipboard", + "credentialEdit.credentialConfig.retry": "Retry", + "credentialEdit.credentialConfig.retryCredentialTest": "Retry credential test", + "credentialEdit.credentialConfig.retrying": "Retrying", + "credentialEdit.credentialConfig.subtitle": "In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL", + "credentialEdit.credentialConfig.theServiceYouReConnectingTo": "the service you're connecting to", + "credentialEdit.credentialEdit.confirmMessage.beforeClose1.cancelButtonText": "Keep Editing", + "credentialEdit.credentialEdit.confirmMessage.beforeClose1.confirmButtonText": "Close", + "credentialEdit.credentialEdit.confirmMessage.beforeClose1.headline": "Close without saving?", + "credentialEdit.credentialEdit.confirmMessage.beforeClose1.message": "Are you sure you want to throw away the changes you made to the {credentialDisplayName} credential?", + "credentialEdit.credentialEdit.confirmMessage.beforeClose2.cancelButtonText": "Keep Editing", + "credentialEdit.credentialEdit.confirmMessage.beforeClose2.confirmButtonText": "Close", + "credentialEdit.credentialEdit.confirmMessage.beforeClose2.headline": "Close without connecting?", + "credentialEdit.credentialEdit.confirmMessage.beforeClose2.message": "You need to connect your credential for it to work", + "credentialEdit.credentialEdit.confirmMessage.deleteCredential.cancelButtonText": "", + "credentialEdit.credentialEdit.confirmMessage.deleteCredential.confirmButtonText": "Yes, delete", + "credentialEdit.credentialEdit.confirmMessage.deleteCredential.headline": "Delete Credential?", + "credentialEdit.credentialEdit.confirmMessage.deleteCredential.message": "Are you sure you want to delete \"{savedCredentialName}\"?", + "credentialEdit.credentialEdit.connection": "Connection", + "credentialEdit.credentialEdit.couldNotFindCredentialOfType": "Could not find credential of type", + "credentialEdit.credentialEdit.couldNotFindCredentialWithId": "Could not find credential with ID", + "credentialEdit.credentialEdit.delete": "Delete", + "credentialEdit.credentialEdit.details": "Details", + "credentialEdit.credentialEdit.saving": "Saving", + "credentialEdit.credentialEdit.showError.createCredential.title": "Problem creating credential", + "credentialEdit.credentialEdit.showError.deleteCredential.title": "Problem deleting credential", + "credentialEdit.credentialEdit.showError.generateAuthorizationUrl.message": "There was a problem generating the authorization URL", + "credentialEdit.credentialEdit.showError.generateAuthorizationUrl.title": "OAuth Authorization Error", + "credentialEdit.credentialEdit.showError.loadCredential.title": "Problem loading credential", + "credentialEdit.credentialEdit.showError.updateCredential.title": "Problem updating credential", + "credentialEdit.credentialEdit.showMessage.title": "Credential deleted", + "credentialEdit.credentialEdit.testing": "Testing", + "credentialEdit.credentialInfo.allowUseBy": "Allow use by", + "credentialEdit.credentialInfo.created": "Created", + "credentialEdit.credentialInfo.id": "ID", + "credentialEdit.credentialInfo.lastModified": "Last modified", + "credentialEdit.oAuthButton.connectMyAccount": "Connect my account", + "credentialEdit.oAuthButton.signInWithGoogle": "Sign in with Google", + "credentialSelectModal.addNewCredential": "Add new credential", + "credentialSelectModal.continue": "Continue", + "credentialSelectModal.searchForApp": "Search for app...", + "credentialSelectModal.selectAnAppOrServiceToConnectTo": "Select an app or service to connect to", + "credentialsList.addNew": "Add New", + "credentialsList.confirmMessage.cancelButtonText": "", + "credentialsList.confirmMessage.confirmButtonText": "Yes, delete", + "credentialsList.confirmMessage.headline": "Delete Credential?", + "credentialsList.confirmMessage.message": "Are you sure you want to delete {credentialName}?", + "credentialsList.createNewCredential": "Create New Credential", + "credentialsList.created": "Created", + "credentialsList.credentials": "Credentials", + "credentialsList.deleteCredential": "Delete Credential", + "credentialsList.editCredential": "Edit Credential", + "credentialsList.errorLoadingCredentials": "Error loading credentials", + "credentialsList.name": "@:reusableBaseText.name", + "credentialsList.operations": "Operations", + "credentialsList.showError.deleteCredential.title": "Problem deleting credential", + "credentialsList.showMessage.title": "Credential deleted", + "credentialsList.type": "Type", + "credentialsList.updated": "Updated", + "credentialsList.yourSavedCredentials": "Your saved credentials", + "dataDisplay.needHelp": "Need help?", + "dataDisplay.nodeDocumentation": "Node Documentation", + "dataDisplay.openDocumentationFor": "Open {nodeTypeDisplayName} documentation", + "displayWithChange.cancelEdit": "Cancel Edit", + "displayWithChange.clickToChange": "Click to Change", + "displayWithChange.setValue": "Set Value", + "duplicateWorkflowDialog.cancel": "@:reusableBaseText.cancel", + "duplicateWorkflowDialog.chooseOrCreateATag": "Choose or create a tag", + "duplicateWorkflowDialog.duplicateWorkflow": "Duplicate Workflow", + "duplicateWorkflowDialog.enterWorkflowName": "Enter workflow name", + "duplicateWorkflowDialog.save": "@:reusableBaseText.save", + "duplicateWorkflowDialog.showMessage.message": "Please enter a name.", + "duplicateWorkflowDialog.showMessage.title": "Name missing", "error": "Error", "error.goBack": "Go back", "error.pageNotFound": "Oops, couldn’t find that", + "executionDetails.executionFailed": "Execution failed", + "executionDetails.executionId": "Execution ID", + "executionDetails.executionWaiting": "Execution waiting", + "executionDetails.executionWasSuccessful": "Execution was successful", + "executionDetails.of": "of", + "executionDetails.openWorkflow": "Open Workflow", + "executionDetails.readOnly.readOnly": "Read only", + "executionDetails.readOnly.youreViewingTheLogOf": "You're viewing the log of a previous execution. You cannot
\n\t\tmake changes since this execution already occurred. Make changes
\n\t\tto this workflow by clicking on its name on the left.", + "executionDetails.workflow": "workflow", + "executionsList.allWorkflows": "All Workflows", + "executionsList.anyStatus": "Any Status", + "executionsList.autoRefresh": "Auto refresh", + "executionsList.confirmMessage.cancelButtonText": "", + "executionsList.confirmMessage.confirmButtonText": "Yes, delete", + "executionsList.confirmMessage.headline": "Delete Executions?", + "executionsList.confirmMessage.message": "Are you sure that you want to delete the {numSelected} selected execution(s)?", + "executionsList.deleteSelected": "Delete Selected", + "executionsList.error": "Error", + "executionsList.filters": "Filters", + "executionsList.loadMore": "Load More", + "executionsList.mode": "Mode", + "executionsList.modes.error": "error", + "executionsList.modes.integrated": "integrated", + "executionsList.modes.manual": "manual", + "executionsList.modes.retry": "retry", + "executionsList.modes.trigger": "trigger", + "executionsList.modes.webhook": "webhook", + "executionsList.name": "@:reusableBaseText.name", + "executionsList.openPastExecution": "Open Past Execution", + "executionsList.retryExecution": "Retry execution", + "executionsList.retryOf": "Retry of", + "executionsList.retryWithCurrentlySavedWorkflow": "Retry with currently saved workflow", + "executionsList.retryWithOriginalworkflow": "Retry with original workflow", + "executionsList.running": "Running", + "executionsList.runningTime": "Running Time", + "executionsList.selectStatus": "Select Status", + "executionsList.selectWorkflow": "Select Workflow", + "executionsList.selected": "Selected", + "executionsList.showError.handleDeleteSelected.title": "Problem deleting executions", + "executionsList.showError.loadMore.title": "Problem loading executions", + "executionsList.showError.loadWorkflows.title": "Problem loading workflows", + "executionsList.showError.refreshData.title": "Problem loading data", + "executionsList.showError.retryExecution.title": "Problem with retry", + "executionsList.showError.stopExecution.title": "Problem stopping execution", + "executionsList.showMessage.handleDeleteSelected.title": "Execution deleted", + "executionsList.showMessage.retrySuccessfulFalse.title": "Retry unsuccessful", + "executionsList.showMessage.retrySuccessfulTrue.title": "Retry successful", + "executionsList.showMessage.stopExecution.message": "Execution ID {activeExecutionId}", + "executionsList.showMessage.stopExecution.title": "Execution stopped", + "executionsList.startedAtId": "Started At / ID", + "executionsList.status": "Status", + "executionsList.statusTooltipText.theWorkflowExecutionFailed": "The workflow execution failed.", + "executionsList.statusTooltipText.theWorkflowExecutionFailedButTheRetryWasSuccessful": "The workflow execution failed but the retry {entryRetrySuccessId} was successful.", + "executionsList.statusTooltipText.theWorkflowExecutionIsProbablyStillRunning": "The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ", + "executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndFailed": "The workflow execution was a retry of {entryRetryOf} and failed.
New retries have to be started from the original execution.", + "executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndItWasSuccessful": "The workflow execution was a retry of {entryRetryOf} and it was successful.", + "executionsList.statusTooltipText.theWorkflowExecutionWasSuccessful": "The worklow execution was successful.", + "executionsList.statusTooltipText.theWorkflowIsCurrentlyExecuting": "The worklow is currently executing.", + "executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely": "The workflow is waiting indefinitely for an incoming webhook call.", + "executionsList.statusTooltipText.theWorkflowIsWaitingTill": "The worklow is waiting till {waitDateDate} {waitDateTime}.", + "executionsList.stopExecution": "Stop Execution", + "executionsList.success": "Success", + "executionsList.successRetry": "Success retry", + "executionsList.unknown": "Unknown", + "executionsList.unsavedWorkflow": "[UNSAVED WORKFLOW]", + "executionsList.waiting": "Waiting", + "executionsList.workflowExecutions": "Workflow Executions", + "expressionEdit.editExpression": "Edit Expression", + "expressionEdit.expression": "Expression", + "expressionEdit.result": "Result", + "expressionEdit.variableSelector": "Variable Selector", + "fixedCollectionParameter.choose": "Choose...", + "fixedCollectionParameter.currentlyNoItemsExist": "Currently no items exist", + "fixedCollectionParameter.deleteItem": "Delete item", + "fixedCollectionParameter.moveDown": "Move down", + "fixedCollectionParameter.moveUp": "Move up", "forgotPassword": "Forgot my password", "forgotPassword.emailSentIfExists": "We’ve emailed {email} (if there’s a matching account)", "forgotPassword.getRecoveryLink": "Email me a recovery link", @@ -1144,8 +239,370 @@ "forgotPassword.returnToSignIn": "Back to sign in", "forgotPassword.sendingEmailError": "Problem sending email", "forgotPassword.smtpErrorContactAdministrator": "Please contact your administrator (problem with your SMTP setup)", + "genericHelpers.loading": "Loading", + "genericHelpers.min": "min", + "genericHelpers.sec": "sec", + "genericHelpers.showMessage.message": "This is a read-only version of the workflow. To make changes, either open the original workflow or save it under a new name.", + "genericHelpers.showMessage.title": "Workflow cannot be changed", + "mainSidebar.aboutN8n": "About n8n", + "mainSidebar.confirmMessage.workflowDelete.cancelButtonText": "", + "mainSidebar.confirmMessage.workflowDelete.confirmButtonText": "Yes, delete", + "mainSidebar.confirmMessage.workflowDelete.headline": "Delete Workflow?", + "mainSidebar.confirmMessage.workflowDelete.message": "Are you sure that you want to delete '{workflowName}'?", + "mainSidebar.confirmMessage.workflowNew.cancelButtonText": "Leave without saving", + "mainSidebar.confirmMessage.workflowNew.confirmButtonText": "Save", + "mainSidebar.confirmMessage.workflowNew.headline": "Save changes before leaving?", + "mainSidebar.confirmMessage.workflowNew.message": "If you don't save, you will lose your changes.", + "mainSidebar.credentials": "Credentials", + "mainSidebar.delete": "Delete", + "mainSidebar.download": "Download", + "mainSidebar.duplicate": "Duplicate", + "mainSidebar.executions": "Executions", + "mainSidebar.help": "Help", + "mainSidebar.helpMenuItems.course": "Course", + "mainSidebar.helpMenuItems.documentation": "Documentation", + "mainSidebar.helpMenuItems.forum": "Forum", + "mainSidebar.importFromFile": "Import from File", + "mainSidebar.importFromUrl": "Import from URL", + "mainSidebar.new": "New", + "mainSidebar.newTemplate": "New from template", + "mainSidebar.open": "Open", + "mainSidebar.prompt.cancel": "@:reusableBaseText.cancel", + "mainSidebar.prompt.import": "Import", + "mainSidebar.prompt.importWorkflowFromUrl": "Import Workflow from URL", + "mainSidebar.prompt.invalidUrl": "Invalid URL", + "mainSidebar.prompt.workflowUrl": "Workflow URL", + "mainSidebar.save": "@:reusableBaseText.save", + "mainSidebar.settings": "Settings", + "mainSidebar.showError.stopExecution.title": "Problem stopping execution", + "mainSidebar.showMessage.handleFileImport.message": "The file does not contain valid JSON data", + "mainSidebar.showMessage.handleFileImport.title": "Could not import file", + "mainSidebar.showMessage.handleSelect1.title": "Workflow deleted", + "mainSidebar.showMessage.handleSelect2.title": "Workflow created", + "mainSidebar.showMessage.handleSelect3.title": "Workflow created", + "mainSidebar.showMessage.stopExecution.title": "Execution stopped", + "mainSidebar.templates": "Templates", + "mainSidebar.workflows": "Workflows", + "multipleParameter.addItem": "Add item", + "multipleParameter.currentlyNoItemsExist": "Currently no items exist", + "multipleParameter.deleteItem": "Delete item", + "multipleParameter.moveDown": "Move down", + "multipleParameter.moveUp": "Move up", + "ndv.backToCanvas": "Back to canvas", + "ndv.backToCanvas.waitingForTriggerWarning": "Waiting for a Trigger node to execute. Close this view to see the Workflow Canvas.", + "ndv.execute.executeNode": "Execute node", + "ndv.execute.executing": "Executing", + "ndv.execute.fetchEvent": "Fetch Event", + "ndv.execute.listenForEvent": "Listen For Event", + "ndv.output": "Output", + "ndv.output.all": "all", + "ndv.output.branch": "Branch", + "ndv.output.emptyInput": "This input item is empty. {name} will still execute when it recieves an empty item.", + "ndv.output.emptyOutput": "This output item is empty.", + "ndv.output.executing": "Executing node...", + "ndv.output.item": "item", + "ndv.output.items": "items", + "ndv.output.noOutputData.message": "n8n stops executing the workflow when a node has no output data. You can change this default behaviour via", + "ndv.output.noOutputData.message.settings": "Settings", + "ndv.output.noOutputData.message.settingsOption": "> “Always Output Data”.", + "ndv.output.noOutputData.title": "No output data returned", + "ndv.output.noOutputDataInBranch": "No output data in this branch", + "ndv.output.of": " of ", + "ndv.output.pageSize": "Page Size", + "ndv.output.pollEventNodeHint": "Fetch an event to output data", + "ndv.output.run": "Run", + "ndv.output.runNodeHint": "Execute this node to output data", + "ndv.output.staleDataWarning": "Node parameters have changed.
Execute node again to refresh output.", + "ndv.output.tooMuchData.message": "The node contains {size} MB of data. Displaying it may cause problems.
If you do decide to display it, avoid the JSON view.", + "ndv.output.tooMuchData.showDataAnyway": "Show data anyway", + "ndv.output.tooMuchData.title": "Output data is huge!", + "ndv.output.triggerEventNodeHint": "Listen for an event to output data", + "ndv.title.cancel": "Cancel", + "ndv.title.rename": "Rename", + "ndv.title.renameNode": "Rename node", + "noTagsView.readyToOrganizeYourWorkflows": "Ready to organize your workflows?", + "noTagsView.withWorkflowTagsYouReFree": "With workflow tags, you're free to create the perfect tagging system for your flows", + "node.activateDeactivateNode": "Activate/Deactivate Node", + "node.deleteNode": "Delete Node", + "node.disabled": "Disabled", + "node.duplicateNode": "Duplicate Node", + "node.editNode": "Edit Node", + "node.executeNode": "Execute Node", + "node.issues": "Issues", + "node.nodeIsExecuting": "Node is executing", + "node.nodeIsWaitingTill": "Node is waiting until {date} {time}", + "node.theNodeIsWaitingIndefinitelyForAnIncomingWebhookCall": "The node is waiting for an incoming webhook call (indefinitely)", + "node.waitingForYouToCreateAnEventIn": "Waiting for you to create an event in {nodeType}", + "nodeBase.clickToAddNodeOrDragToConnect": "Click to add node
or drag to connect", + "nodeCreator.categoryNames.analytics": "Analytics", + "nodeCreator.categoryNames.communication": "Communication", + "nodeCreator.categoryNames.coreNodes": "Core Nodes", + "nodeCreator.categoryNames.customNodes": "Custom Nodes", + "nodeCreator.categoryNames.dataStorage": "Data & Storage", + "nodeCreator.categoryNames.development": "Development", + "nodeCreator.categoryNames.financeAccounting": "Finance & Accounting", + "nodeCreator.categoryNames.marketingContent": "Marketing & Content", + "nodeCreator.categoryNames.miscellaneous": "Miscellaneous", + "nodeCreator.categoryNames.productivity": "Productivity", + "nodeCreator.categoryNames.sales": "Sales", + "nodeCreator.categoryNames.suggestedNodes": "Suggested Nodes ✨", + "nodeCreator.categoryNames.utility": "Utility", + "nodeCreator.mainPanel.all": "All", + "nodeCreator.mainPanel.regular": "Regular", + "nodeCreator.mainPanel.trigger": "Trigger", + "nodeCreator.noResults.dontWorryYouCanProbablyDoItWithThe": "Don’t worry, you can probably do it with the", + "nodeCreator.noResults.httpRequest": "HTTP Request", + "nodeCreator.noResults.node": "node", + "nodeCreator.noResults.or": "or", + "nodeCreator.noResults.requestTheNode": "Request the node", + "nodeCreator.noResults.wantUsToMakeItFaster": "Want us to make it faster?", + "nodeCreator.noResults.weDidntMakeThatYet": "We didn't make that... yet", + "nodeCreator.noResults.webhook": "Webhook", + "nodeCreator.searchBar.searchNodes": "Search nodes...", + "nodeCreator.subcategoryDescriptions.dataTransformation": "Manipulate data fields, run code", + "nodeCreator.subcategoryDescriptions.files": "Work with CSV, XML, text, images etc.", + "nodeCreator.subcategoryDescriptions.flow": "Branches, core triggers, merge data", + "nodeCreator.subcategoryDescriptions.helpers": "HTTP Requests (API calls), date and time, scrape HTML", + "nodeCreator.subcategoryNames.dataTransformation": "Data Transformation", + "nodeCreator.subcategoryNames.files": "Files", + "nodeCreator.subcategoryNames.flow": "Flow", + "nodeCreator.subcategoryNames.helpers": "Helpers", + "nodeCredentials.createNew": "Create New", + "nodeCredentials.credentialFor": "Credential for {credentialType}", + "nodeCredentials.issues": "Issues", + "nodeCredentials.selectCredential": "Select Credential", + "nodeCredentials.showMessage.message": "Nodes that used credential \"{oldCredentialName}\" have been updated to use \"{newCredentialName}\"", + "nodeCredentials.showMessage.title": "Node credential updated", + "nodeCredentials.updateCredential": "Update Credential", + "nodeErrorView.cause": "Cause", + "nodeErrorView.copyToClipboard": "Copy to Clipboard", + "nodeErrorView.dataBelowMayContain": "Data below may contain sensitive information. Proceed with caution when sharing.", + "nodeErrorView.details": "Details", + "nodeErrorView.error": "ERROR", + "nodeErrorView.httpCode": "HTTP Code", + "nodeErrorView.showMessage.title": "Copied to clipboard", + "nodeErrorView.stack": "Stack", + "nodeErrorView.theErrorCauseIsTooLargeToBeDisplayed": "The error cause is too large to be displayed", + "nodeErrorView.time": "Time", + "nodeSettings.alwaysOutputData.description": "If active, will output a single, empty item when the output would have been empty. Use to prevent the workflow finishing on this node.", + "nodeSettings.alwaysOutputData.displayName": "Always Output Data", + "nodeSettings.clickOnTheQuestionMarkIcon": "Click the '?' icon to open this node on n8n.io", + "nodeSettings.continueOnFail.description": "If active, the workflow continues even if this node's execution fails. When this occurs, the node passes along input data from previous nodes - so your workflow should account for unexpected output data.", + "nodeSettings.continueOnFail.displayName": "Continue On Fail", + "nodeSettings.docs": "Docs", + "nodeSettings.executeOnce.description": "If active, the node executes only once, with data from the first item it receives", + "nodeSettings.executeOnce.displayName": "Execute Once", + "nodeSettings.maxTries.description": "Number of times to attempt to execute the node before failing the execution", + "nodeSettings.maxTries.displayName": "Max. Tries", + "nodeSettings.noDescriptionFound": "No description found", + "nodeSettings.nodeDescription": "Node Description", + "nodeSettings.notes.description": "Optional note to save with the node", + "nodeSettings.notes.displayName": "Notes", + "nodeSettings.notesInFlow.description": "If active, the note above will display in the flow as a subtitle", + "nodeSettings.notesInFlow.displayName": "Display note in flow?", + "nodeSettings.parameters": "Parameters", + "nodeSettings.retryOnFail.description": "If active, the node tries to execute again when it fails", + "nodeSettings.retryOnFail.displayName": "Retry On Fail", + "nodeSettings.theNodeIsNotValidAsItsTypeIsUnknown": "The node is not valid as its type ({nodeType}) is unknown", + "nodeSettings.thisNodeDoesNotHaveAnyParameters": "This node does not have any parameters", + "nodeSettings.waitBetweenTries.description": "How long to wait between each attempt (in milliseconds)", + "nodeSettings.waitBetweenTries.displayName": "Wait Between Tries (ms)", + "nodeView.addNode": "Add node", + "nodeView.confirmMessage.beforeRouteLeave.cancelButtonText": "Leave without saving", + "nodeView.confirmMessage.beforeRouteLeave.confirmButtonText": "Save", + "nodeView.confirmMessage.beforeRouteLeave.headline": "Save changes before leaving?", + "nodeView.confirmMessage.beforeRouteLeave.message": "If you don't save, you will lose your changes.", + "nodeView.confirmMessage.initView.cancelButtonText": "Leave without saving", + "nodeView.confirmMessage.initView.confirmButtonText": "Save", + "nodeView.confirmMessage.initView.headline": "Save changes before leaving?", + "nodeView.confirmMessage.initView.message": "If you don't save, you will lose your changes.", + "nodeView.confirmMessage.receivedCopyPasteData.cancelButtonText": "", + "nodeView.confirmMessage.receivedCopyPasteData.confirmButtonText": "Yes, import", + "nodeView.confirmMessage.receivedCopyPasteData.headline": "Import Workflow?", + "nodeView.confirmMessage.receivedCopyPasteData.message": "Workflow will be imported from
{plainTextData}", + "nodeView.couldntImportWorkflow": "Could not import workflow", + "nodeView.deletesTheCurrentExecutionData": "Deletes the current execution data", + "nodeView.executesTheWorkflowFromTheStartOrWebhookNode": "Executes the workflow from the 'start' or 'webhook' node", + "nodeView.itLooksLikeYouHaveBeenEditingSomething": "It looks like you made some edits. If you leave before saving, your changes will be lost.", + "nodeView.loadingTemplate": "Loading template", + "nodeView.moreInfo": "More info", + "nodeView.noNodesGivenToAdd": "No nodes to add specified", + "nodeView.prompt.cancel": "@:reusableBaseText.cancel", + "nodeView.prompt.invalidName": "Invalid Name", + "nodeView.prompt.newName": "New Name", + "nodeView.prompt.rename": "Rename", + "nodeView.prompt.renameNode": "Rename Node", + "nodeView.redirecting": "Redirecting", + "nodeView.refresh": "Refresh", + "nodeView.resetZoom": "Reset Zoom", + "nodeView.runButtonText.executeWorkflow": "Execute Workflow", + "nodeView.runButtonText.executingWorkflow": "Executing Workflow", + "nodeView.runButtonText.waitingForTriggerEvent": "Waiting for Trigger Event", + "nodeView.showError.getWorkflowDataFromUrl.title": "Problem loading workflow", + "nodeView.showError.importWorkflowData.title": "Problem importing workflow", + "nodeView.showError.mounted1.message": "There was a problem loading init data", + "nodeView.showError.mounted1.title": "Init Problem", + "nodeView.showError.mounted2.message": "There was a problem initializing the workflow", + "nodeView.showError.mounted2.title": "Init Problem", + "nodeView.showError.openExecution.title": "Problem loading execution", + "nodeView.showError.openWorkflow.title": "Problem opening workflow", + "nodeView.showError.stopExecution.title": "Problem stopping execution", + "nodeView.showError.stopWaitingForWebhook.title": "Problem deleting test webhook", + "nodeView.showMessage.addNodeButton.message": "'{nodeTypeName}' is an unknown node type", + "nodeView.showMessage.addNodeButton.title": "Could not create node", + "nodeView.showMessage.keyDown.title": "Workflow created", + "nodeView.showMessage.showMaxNodeTypeError.message": "Only {count} '{nodeTypeDataDisplayName}' node is allowed in a workflow | Only {count} '{nodeTypeDataDisplayName}' nodes are allowed in a workflow", + "nodeView.showMessage.showMaxNodeTypeError.title": "Could not create node", + "nodeView.showMessage.stopExecutionCatch.message": "It completed before it could be stopped", + "nodeView.showMessage.stopExecutionCatch.title": "Workflow finished executing", + "nodeView.showMessage.stopExecutionTry.title": "Execution stopped", + "nodeView.showMessage.stopWaitingForWebhook.title": "Webhook deleted", + "nodeView.stopCurrentExecution": "Stop current execution", + "nodeView.stopWaitingForWebhookCall": "Stop waiting for webhook call", + "nodeView.stoppingCurrentExecution": "Stopping current execution", + "nodeView.thereWasAProblemLoadingTheNodeParametersOfNode": "There was a problem loading the parameters of the node", + "nodeView.thisExecutionHasntFinishedYet": "This execution hasn't finished yet", + "nodeView.toSeeTheLatestStatus": "to see the latest status", + "nodeView.workflowTemplateWithIdCouldNotBeFound": "Workflow template with ID \"{templateId}\" could not be found", + "nodeView.workflowWithIdCouldNotBeFound": "Workflow with ID \"{workflowId}\" could not be found", + "nodeView.zoomIn": "Zoom In", + "nodeView.zoomOut": "Zoom Out", + "nodeView.zoomToFit": "Zoom to Fit", + "nodeWebhooks.clickToCopyWebhookUrls": "Click to copy webhook URLs", + "nodeWebhooks.clickToDisplayWebhookUrls": "Click to display webhook URLs", + "nodeWebhooks.clickToHideWebhookUrls": "Click to hide webhook URLs", + "nodeWebhooks.invalidExpression": "[INVALID EXPRESSION]", + "nodeWebhooks.productionUrl": "Production URL", + "nodeWebhooks.showMessage.title": "URL copied", + "nodeWebhooks.testUrl": "Test URL", + "nodeWebhooks.webhookUrls": "Webhook URLs", "openWorkflow.workflowImportError": "Could not import workflow", "openWorkflow.workflowNotFoundError": "Could not find workflow", + "parameterInput.addExpression": "Add Expression", + "parameterInput.error": "ERROR", + "parameterInput.issues": "Issues", + "parameterInput.loadingOptions": "Loading options...", + "parameterInput.openEditWindow": "Open Edit Window", + "parameterInput.parameter": "Parameter: \"{shortPath}\"", + "parameterInput.parameterHasExpression": "Parameter: \"{shortPath}\" has an expression", + "parameterInput.parameterHasIssues": "Parameter: \"{shortPath}\" has issues", + "parameterInput.parameterHasIssuesAndExpression": "Parameter: \"{shortPath}\" has issues and an expression", + "parameterInput.parameterOptions": "Parameter Options", + "parameterInput.refreshList": "Refresh List", + "parameterInput.removeExpression": "Remove Expression", + "parameterInput.resetValue": "Reset Value", + "parameterInput.select": "Select", + "parameterInput.selectDateAndTime": "Select date and time", + "parameterInputExpanded.openDocs": "Open docs", + "parameterInputExpanded.thisFieldIsRequired": "This field is required", + "parameterInputList.delete": "Delete", + "parameterInputList.deleteParameter": "Delete Parameter", + "parameterInputList.parameterOptions": "Parameter Options", + "personalizationModal.automationAgencyOrConsultant": "Automation agency/consultant", + "personalizationModal.automationConsulting": "Automation consulting", + "personalizationModal.cloudInfrastructure": "Cloud infrastructure", + "personalizationModal.continue": "Continue", + "personalizationModal.customerIntegrations": "Customer integrations", + "personalizationModal.customerSupport": "Customer support", + "personalizationModal.customizeN8n": "Customize n8n to you", + "personalizationModal.digitalAgencyOrConsultant": "Digital agency/consultant", + "personalizationModal.eCommerce": "eCommerce", + "personalizationModal.education": "Education", + "personalizationModal.errorWhileSubmittingResults": "Error while submitting results", + "personalizationModal.executiveTeam": "Executive team", + "personalizationModal.financeOrAccounting": "Finance / Accounting", + "personalizationModal.financeOrInsurance": "Finance / Insurance", + "personalizationModal.getStarted": "Get started", + "personalizationModal.government": "Government", + "personalizationModal.healthcare": "Healthcare", + "personalizationModal.howAreYourCodingSkills": "How are your coding skills?", + "personalizationModal.howBigIsYourCompany": "How big is your company?", + "personalizationModal.hr": "HR", + "personalizationModal.iCanCodeSomeUsefulThingsBut": "2. I can code some useful things, but I spend a lot of time stuck", + "personalizationModal.iCanDoAlmostAnythingIWant": "5. I can do almost anything I want, easily (pro coder)", + "personalizationModal.iCanFigureMostThingsOut": "4. I can figure most things out", + "personalizationModal.iGetStuckTooQuicklyToAchieveMuch": "1. I get stuck too quickly to achieve much", + "personalizationModal.iKnowEnoughToBeDangerousBut": "3. I know enough to be dangerous, but I'm no expert", + "personalizationModal.imNotUsingN8nForWork": "I'm not using n8n for work", + "personalizationModal.individualConsumers": "Customers are individual consumers", + "personalizationModal.it": "IT", + "personalizationModal.itEngineering": "IT / Engineering", + "personalizationModal.itSupport": "IT support", + "personalizationModal.largeBusinesses": "Customers are large businesses (500+ employees)", + "personalizationModal.legal": "Legal", + "personalizationModal.lessThan20People": "Less than 20 people", + "personalizationModal.lookOutForThingsMarked": "Look out for things marked with a ✨. They are personalized to make n8n more relevant to you.", + "personalizationModal.managedServiceProvider": "Managed service provider", + "personalizationModal.manufacturing": "Manufacturing", + "personalizationModal.marketing": "Marketing", + "personalizationModal.media": "Media", + "personalizationModal.mediumBusinesses": "Customers are medium businesses (20-499 employees)", + "personalizationModal.networkingOrCommunication": "Networking / Communication", + "personalizationModal.neverCoded": "0. Never coded", + "personalizationModal.notSureYet": "Not sure yet", + "personalizationModal.operations": "Operations", + "personalizationModal.other": "Other", + "personalizationModal.otherPleaseSpecify": "Other (please specify)", + "personalizationModal.people": "people", + "personalizationModal.physicalRetailOrServices": "Physical retail or services", + "personalizationModal.pleaseSpecifyYourCompanyFocus": "Please specify your company focus", + "personalizationModal.proCoder": "Pro coder", + "personalizationModal.product": "Product (e.g. fast prototyping)", + "personalizationModal.realEstateOrConstruction": "Real estate / Construction", + "personalizationModal.saas": "SaaS", + "personalizationModal.salesAndMarketing": "Sales and Marketing", + "personalizationModal.salesBizDev": "Sales / Bizdev", + "personalizationModal.salesBusinessDevelopment": "Sales / Business Development", + "personalizationModal.security": "Security", + "personalizationModal.select": "Select...", + "personalizationModal.smallBusinesses": "Customers are small businesses (under 20 employees)", + "personalizationModal.specifyYourAutomationGoal": "Please specify your automation goal", + "personalizationModal.specifyYourCompanysIndustry": "Specify your company's industry", + "personalizationModal.specifyYourWorkArea": "Specify your work area", + "personalizationModal.support": "Support", + "personalizationModal.systemsIntegration": "Systems Integration", + "personalizationModal.systemsIntegrator": "Systems Integrator", + "personalizationModal.telecoms": "Telecoms", + "personalizationModal.thanks": "Thanks!", + "personalizationModal.theseQuestionsHelpUs": "These questions help us tailor n8n to you", + "personalizationModal.whatAreYouLookingToAutomate": "What are you looking to automate?", + "personalizationModal.whatBestDescribesYourCompany": "What best describes your company?", + "personalizationModal.whatDoesYourCompanyFocusOn": "Which services does your company focus on?", + "personalizationModal.whatKindOfCustomersDoYouServe": "What kind of customers do you serve?", + "personalizationModal.whichIndustriesIsYourCompanyIn": "Which industries is your company in?", + "pushConnection.showMessage.title": "Workflow executed successfully", + "pushConnectionTracker.cannotConnectToServer": "You have a connection issue or the server is down.
n8n should reconnect automatically once the issue is resolved.", + "pushConnectionTracker.connectionLost": "Connection lost", + "reusableBaseText.cancel": "Cancel", + "reusableBaseText.name": "Name", + "reusableBaseText.save": "Save", + "reusableDynamicText.oauth2.clientId": "Client ID", + "reusableDynamicText.oauth2.clientSecret": "Client Secret", + "runData.binary": "Binary", + "runData.copyItemPath": "Copy Item Path", + "runData.copyParameterPath": "Copy Parameter Path", + "runData.copyToClipboard": "Copy to Clipboard", + "runData.copyValue": "Copy Value", + "runData.downloadBinaryData": "Download", + "runData.executeNode": "Execute Node", + "runData.executionTime": "Execution Time", + "runData.fileExtension": "File Extension", + "runData.fileName": "File Name", + "runData.items": "Items", + "runData.json": "JSON", + "runData.mimeType": "Mime Type", + "runData.ms": "ms", + "runData.noBinaryDataFound": "No binary data found", + "runData.noData": "No data", + "runData.noTextDataFound": "No text data found", + "runData.nodeReturnedALargeAmountOfData": "Node returned a large amount of data", + "runData.output": "Output", + "runData.showBinaryData": "View", + "runData.startTime": "Start Time", + "runData.table": "Table", + "saveButton.save": "@:reusableBaseText.save", + "saveButton.saved": "Saved", + "saveButton.saving": "Saving", "settings": "Settings", "settings.goBack": "Go back", "settings.personal": "Personal", @@ -1188,10 +645,203 @@ "settings.users.userInvitedError": "User could not be invited", "settings.users.userReinviteError": "Could not reinvite user", "settings.users.userToTransferTo": "User to transfer to", + "settings.users.usersEmailedError": "Couldn't send invite email", "settings.users.usersInvited": "Users invited", "settings.users.usersInvitedError": "Could not invite users", - "settings.users.usersEmailedError": "Couldn't send invite email", "settings.version": "Version", + "showMessage.cancel": "@:reusableBaseText.cancel", + "showMessage.ok": "OK", + "showMessage.showDetails": "Show Details", "startupError": "Error connecting to n8n", - "startupError.message": "Could not connect to server. Refresh to try again" + "startupError.message": "Could not connect to server. Refresh to try again", + "tagsDropdown.createTag": "Create tag \"{filter}\"", + "tagsDropdown.manageTags": "Manage tags", + "tagsDropdown.noMatchingTagsExist": "No matching tags exist", + "tagsDropdown.noTagsExist": "No tags exist", + "tagsDropdown.showError.message": "A problem occurred when trying to create the '{name}' tag", + "tagsDropdown.showError.title": "Could not create tag", + "tagsDropdown.typeToCreateATag": "Type to create a tag", + "tagsManager.couldNotDeleteTag": "Could not delete tag", + "tagsManager.done": "Done", + "tagsManager.manageTags": "Manage tags", + "tagsManager.showError.onCreate.message": "A problem occurred when trying to create the tag '{escapedName}'", + "tagsManager.showError.onCreate.title": "Could not create tag", + "tagsManager.showError.onDelete.message": "A problem occurred when trying to delete the tag '{escapedName}'", + "tagsManager.showError.onDelete.title": "Could not delete tag", + "tagsManager.showError.onUpdate.message": "A problem occurred when trying to update the tag '{escapedName}'", + "tagsManager.showError.onUpdate.title": "Could not update tag", + "tagsManager.showMessage.onDelete.title": "Tag deleted", + "tagsManager.showMessage.onUpdate.title": "Tag updated", + "tagsManager.tagNameCannotBeEmpty": "Tag name cannot be empty", + "tagsTable.areYouSureYouWantToDeleteThisTag": "Are you sure you want to delete this tag?", + "tagsTable.cancel": "@:reusableBaseText.cancel", + "tagsTable.createTag": "Create tag", + "tagsTable.deleteTag": "Delete tag", + "tagsTable.editTag": "Edit Tag", + "tagsTable.name": "@:reusableBaseText.name", + "tagsTable.noMatchingTagsExist": "No matching tags exist", + "tagsTable.saveChanges": "Save changes?", + "tagsTable.usage": "Usage", + "tagsTableHeader.addNew": "Add new", + "tagsTableHeader.searchTags": "Search Tags", + "tagsView.inUse": "{count} workflow | {count} workflows", + "tagsView.notBeingUsed": "Not being used", + "template.buttons.goBackButton": "Go back", + "template.buttons.useThisWorkflowButton": "Use this workflow", + "template.details.appsInTheCollection": "This collection features", + "template.details.appsInTheWorkflow": "Apps in this workflow", + "template.details.by": "by", + "template.details.categories": "Categories", + "template.details.created": "Created", + "template.details.details": "Details", + "template.details.times": "times", + "template.details.viewed": "Viewed", + "templates.allCategories": "All Categories", + "templates.categoriesHeading": "Categories", + "templates.collection": "Collection", + "templates.collections": "Collections", + "templates.collectionsNotFound": "Collection could not be found", + "templates.connectionWarning": "⚠️ There was a problem fetching workflow templates. Check your internet connection.", + "templates.endResult": "Share your own useful workflows through your n8n.io account", + "templates.heading": "Workflow templates", + "templates.newButton": "New blank workflow", + "templates.noSearchResults": "Nothing found. Try adjusting your search to see more.", + "templates.searchPlaceholder": "Search workflows", + "templates.workflow": "Workflow", + "templates.workflows": "Workflows", + "templates.workflowsNotFound": "Workflow could not be found", + "textEdit.edit": "Edit", + "timeAgo.daysAgo": "%s days ago", + "timeAgo.hoursAgo": "%s hours ago", + "timeAgo.inDays": "in %s days", + "timeAgo.inHours": "in %s hours", + "timeAgo.inMinutes": "in %s minutes", + "timeAgo.inMonths": "in %s months", + "timeAgo.inOneDay": "in 1 day", + "timeAgo.inOneHour": "in 1 hour", + "timeAgo.inOneMinute": "in 1 minute", + "timeAgo.inOneMonth": "in 1 month", + "timeAgo.inOneWeek": "in 1 week", + "timeAgo.inOneYear": "in 1 year", + "timeAgo.inWeeks": "in %s weeks", + "timeAgo.inYears": "in %s years", + "timeAgo.justNow": "Just now", + "timeAgo.minutesAgo": "%s minutes ago", + "timeAgo.monthsAgo": "%s months ago", + "timeAgo.oneDayAgo": "1 day ago", + "timeAgo.oneHourAgo": "1 hour ago", + "timeAgo.oneMinuteAgo": "1 minute ago", + "timeAgo.oneMonthAgo": "1 month ago", + "timeAgo.oneWeekAgo": "1 week ago", + "timeAgo.oneYearAgo": "1 year ago", + "timeAgo.rightNow": "Right now", + "timeAgo.weeksAgo": "%s weeks ago", + "timeAgo.yearsAgo": "%s years ago", + "updatesPanel.andIs": "and is", + "updatesPanel.behindTheLatest": "behind the latest and greatest n8n", + "updatesPanel.howToUpdateYourN8nVersion": "How to update your n8n version", + "updatesPanel.version": "{numberOfVersions} version{howManySuffix}", + "updatesPanel.weVeBeenBusy": "We’ve been busy ✨", + "updatesPanel.youReOnVersion": "You’re on {currentVersionName}, which was released", + "variableSelector.context": "Context", + "variableSelector.currentNode": "Current Node", + "variableSelector.nodes": "Nodes", + "variableSelector.outputData": "Output Data", + "variableSelector.parameters": "Parameters", + "variableSelector.variableFilter": "Variable filter...", + "variableSelectorItem.empty": "--- EMPTY ---", + "variableSelectorItem.selectItem": "Select Item", + "versionCard.breakingChanges": "Breaking changes", + "versionCard.released": "Released", + "versionCard.securityUpdate": "Security update", + "versionCard.thisVersionHasASecurityIssue": "This version has a security issue.
It is listed here for completeness.", + "versionCard.unknown": "unknown", + "versionCard.version": "Version", + "workflowActivator.activateWorkflow": "Activate workflow", + "workflowActivator.deactivateWorkflow": "Deactivate workflow", + "workflowActivator.showError.title": "Workflow could not be {newStateName}", + "workflowActivator.showMessage.activeChangedNodesIssuesExistTrue.message": "Please resolve outstanding issues before you activate it", + "workflowActivator.showMessage.activeChangedNodesIssuesExistTrue.title": "Problem activating workflow", + "workflowActivator.showMessage.activeChangedWorkflowIdUndefined.message": "Please save it before activating", + "workflowActivator.showMessage.activeChangedWorkflowIdUndefined.title": "Problem activating workflow", + "workflowActivator.showMessage.displayActivationError.message.catchBlock": "Sorry there was a problem requesting the error", + "workflowActivator.showMessage.displayActivationError.message.errorDataNotUndefined": "The following error occurred on workflow activation:
{message}", + "workflowActivator.showMessage.displayActivationError.message.errorDataUndefined": "Unknown error", + "workflowActivator.showMessage.displayActivationError.title": "Problem activating workflow", + "workflowActivator.theWorkflowIsSetToBeActiveBut": "The workflow is activated but could not be started.
Click to display error message.", + "workflowActivator.thisWorkflowHasNoTriggerNodes": "This workflow has no trigger nodes that require activation", + "workflowDetails.active": "Active", + "workflowDetails.addTag": "Add tag", + "workflowDetails.chooseOrCreateATag": "Choose or create a tag", + "workflowDetails.showMessage.message": "Please enter a name, or press 'esc' to go back to the old one", + "workflowDetails.showMessage.title": "Name missing", + "workflowHelpers.showMessage.title": "Problem saving workflow", + "workflowOpen.active": "Active", + "workflowOpen.confirmMessage.cancelButtonText": "Leave without saving", + "workflowOpen.confirmMessage.confirmButtonText": "Save", + "workflowOpen.confirmMessage.headline": "Save changes before leaving?", + "workflowOpen.confirmMessage.message": "If you don't save, you will lose your changes.", + "workflowOpen.couldNotLoadActiveWorkflows": "Could not load active workflows", + "workflowOpen.created": "Created", + "workflowOpen.filterWorkflows": "Filter by tags", + "workflowOpen.name": "@:reusableBaseText.name", + "workflowOpen.openWorkflow": "Open Workflow", + "workflowOpen.searchWorkflows": "Search workflows...", + "workflowOpen.showError.title": "Problem loading workflows", + "workflowOpen.showMessage.message": "This is the current workflow", + "workflowOpen.showMessage.title": "Workflow already open", + "workflowOpen.updated": "Updated", + "workflowPreview.showError.arrayEmpty": "Must have an array of nodes", + "workflowPreview.showError.missingWorkflow": "Missing workflow", + "workflowPreview.showError.previewError.message": "Unable to preview workflow", + "workflowPreview.showError.previewError.title": "Preview error", + "workflowRun.noActiveConnectionToTheServer": "Lost connection to the server", + "workflowRun.showError.title": "Problem running workflow", + "workflowRun.showMessage.message": "Please fix them before executing", + "workflowRun.showMessage.title": "Workflow has issues", + "workflowSettings.defaultTimezone": "Default - {defaultTimezoneValue}", + "workflowSettings.defaultTimezoneNotValid": "Default Timezone not valid", + "workflowSettings.errorWorkflow": "Error Workflow", + "workflowSettings.helpTexts.errorWorkflow": "A second workflow to run if the current one fails.
The second workflow should an 'Error Trigger' node.", + "workflowSettings.helpTexts.executionTimeout": "How long the workflow should wait before timing out", + "workflowSettings.helpTexts.executionTimeoutToggle": "Whether to cancel workflow execution after a defined time", + "workflowSettings.helpTexts.saveDataErrorExecution": "Whether to save data of executions that fail", + "workflowSettings.helpTexts.saveDataSuccessExecution": "Whether to save data of executions that finish successfully", + "workflowSettings.helpTexts.saveExecutionProgress": "Whether to save data after each node execution. This allows you to resume from where execution stopped if there is an error, but may increase latency.", + "workflowSettings.helpTexts.saveManualExecutions": "Whether to save data of executions that are started manually from the editor", + "workflowSettings.helpTexts.timezone": "The timezone in which the workflow should run. Used by 'cron' node, for example.", + "workflowSettings.hours": "hours", + "workflowSettings.minutes": "minutes", + "workflowSettings.noWorkflow": "- No Workflow -", + "workflowSettings.save": "@:reusableBaseText.save", + "workflowSettings.saveDataErrorExecution": "Save failed executions", + "workflowSettings.saveDataErrorExecutionOptions.defaultSave": "Default - {defaultValue}", + "workflowSettings.saveDataErrorExecutionOptions.doNotSave": "Do not save", + "workflowSettings.saveDataErrorExecutionOptions.save": "@:reusableBaseText.save", + "workflowSettings.saveDataSuccessExecution": "Save successful executions", + "workflowSettings.saveDataSuccessExecutionOptions.defaultSave": "Default - {defaultValue}", + "workflowSettings.saveDataSuccessExecutionOptions.doNotSave": "Do not save", + "workflowSettings.saveDataSuccessExecutionOptions.save": "@:reusableBaseText.save", + "workflowSettings.saveExecutionProgress": "Save execution progress", + "workflowSettings.saveExecutionProgressOptions.defaultSave": "Default - {defaultValue}", + "workflowSettings.saveExecutionProgressOptions.no": "No", + "workflowSettings.saveExecutionProgressOptions.yes": "Yes", + "workflowSettings.saveManualExecutions": "Save manual executions", + "workflowSettings.saveManualOptions.defaultSave": "Default - {defaultValue}", + "workflowSettings.saveManualOptions.no": "No", + "workflowSettings.saveManualOptions.yes": "Yes", + "workflowSettings.seconds": "seconds", + "workflowSettings.selectOption": "Select Option", + "workflowSettings.settingsFor": "Settings for {workflowName} (#{workflowId})", + "workflowSettings.showError.saveSettings1.errorMessage": "Timeout is activated but set to 0", + "workflowSettings.showError.saveSettings1.message": "There was a problem saving the settings", + "workflowSettings.showError.saveSettings1.title": "Problem saving settings", + "workflowSettings.showError.saveSettings2.errorMessage": "Maximum Timeout is: {hours} hours, {minutes} minutes, {seconds} seconds", + "workflowSettings.showError.saveSettings2.message": "The timeout is longer than allowed", + "workflowSettings.showError.saveSettings2.title": "Problem saving settings", + "workflowSettings.showError.saveSettings3.title": "Problem saving settings", + "workflowSettings.showMessage.saveSettings.title": "Workflow settings saved", + "workflowSettings.timeoutAfter": "Timeout After", + "workflowSettings.timeoutWorkflow": "Timeout Workflow", + "workflowSettings.timezone": "Timezone" } diff --git a/packages/editor-ui/src/plugins/icons.ts b/packages/editor-ui/src/plugins/icons.ts index 22908d518d..f7c86d9ec4 100644 --- a/packages/editor-ui/src/plugins/icons.ts +++ b/packages/editor-ui/src/plugins/icons.ts @@ -61,6 +61,7 @@ import { faPause, faPauseCircle, faPen, + faPencilAlt, faPlay, faPlayCircle, faPlus, @@ -158,6 +159,7 @@ addIcon(faNetworkWired); addIcon(faPause); addIcon(faPauseCircle); addIcon(faPen); +addIcon(faPencilAlt); addIcon(faPlay); addIcon(faPlayCircle); addIcon(faPlus); diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts index 418a22a33f..d465d9fce7 100644 --- a/packages/editor-ui/src/store.ts +++ b/packages/editor-ui/src/store.ts @@ -90,6 +90,7 @@ const state: IRootState = { }, sidebarMenuItems: [], instanceId: '', + nodeMetadata: {}, }; const modules = { @@ -328,6 +329,9 @@ export const store = new Vuex.Store({ if (state.lastSelectedNode === nameData.old) { state.lastSelectedNode = nameData.new; } + + Vue.set(state.nodeMetadata, nameData.new, state.nodeMetadata[nameData.old]); + Vue.delete(state.nodeMetadata, nameData.old); }, resetAllNodesIssues (state) { @@ -418,6 +422,8 @@ export const store = new Vuex.Store({ state.workflow.nodes.push(nodeData); }, removeNode (state, node: INodeUi) { + Vue.delete(state.nodeMetadata, node.name); + for (let i = 0; i < state.workflow.nodes.length; i++) { if (state.workflow.nodes[i].name === node.name) { state.workflow.nodes.splice(i, 1); @@ -470,6 +476,11 @@ export const store = new Vuex.Store({ state.stateIsDirty = true; Vue.set(node, 'parameters', updateInformation.value); + + if (!state.nodeMetadata[node.name]) { + Vue.set(state.nodeMetadata, node.name, {}); + } + Vue.set(state.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now()); }, // Node-Index @@ -666,6 +677,10 @@ export const store = new Vuex.Store({ return state.activeExecutions; }, + getParametersLastUpdated: (state): ((name: string) => number | undefined) => { + return (nodeName: string) => state.nodeMetadata[nodeName] && state.nodeMetadata[nodeName].parametersLastUpdatedAt; + }, + getBaseUrl: (state): string => { return state.baseUrl; }, diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index f30472ee79..18c4b83c73 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -33,7 +33,7 @@ > - +
@@ -170,7 +170,6 @@ import { import { mapGetters } from 'vuex'; import { - loadLanguage, addNodeTranslation, addHeaders, } from '@/plugins/i18n'; @@ -232,9 +231,6 @@ export default mixins( deep: true, }, - async defaultLocale (newLocale, oldLocale) { - loadLanguage(newLocale); - }, }, async beforeRouteLeave(to, from, next) { const result = this.$store.getters.getStateIsDirty; @@ -271,7 +267,7 @@ export default mixins( defaultLocale (): string { return this.$store.getters.defaultLocale; }, - englishLocale(): boolean { + isEnglishLocale(): boolean { return this.defaultLocale === 'en'; }, ...mapGetters(['nativelyNumberSuffixedDefaults']), @@ -348,6 +344,7 @@ export default mixins( pullConnActiveNodeName: null as string | null, pullConnActive: false, dropPrevented: false, + renamingActive: false, }; }, beforeDestroy () { @@ -378,7 +375,7 @@ export default mixins( type?: string, }) { const allNodeNamesOnCanvas = this.$store.getters.allNodes.map((n: INodeUi) => n.name); - originalName = this.englishLocale ? originalName : this.translateName(type, originalName); + originalName = this.isEnglishLocale ? originalName : this.translateName(type, originalName); if ( !allNodeNamesOnCanvas.includes(originalName) && @@ -388,7 +385,7 @@ export default mixins( } let natives: string[] = this.nativelyNumberSuffixedDefaults; - natives = this.englishLocale ? natives : natives.map(name => { + natives = this.isEnglishLocale ? natives : natives.map(name => { const type = name.toLowerCase().replace('_', ''); return this.translateName(type, name); }); @@ -1260,15 +1257,10 @@ export default mixins( const maxNodes = nodeTypeData.maxNodes; this.$showMessage({ title: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.title'), - message: this.$locale.baseText( - maxNodes === 1 - ? 'nodeView.showMessage.showMaxNodeTypeError.message.singular' - : 'nodeView.showMessage.showMaxNodeTypeError.message.plural', + message: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.message', { - interpolate: { - maxNodes: maxNodes!.toString(), - nodeTypeDataDisplayName: nodeTypeData.displayName, - }, + adjustToNumber: maxNodes, + interpolate: { nodeTypeDataDisplayName: nodeTypeData.displayName }, }, ), type: 'error', @@ -2244,6 +2236,13 @@ export default mixins( if (currentName === newName) { return; } + + const activeNodeName = this.activeNode && this.activeNode.name; + const isActive = activeNodeName === currentName; + if (isActive) { + this.renamingActive = true; + } + // Check if node-name is unique else find one that is newName = this.getUniqueNodeName({ originalName: newName, @@ -2271,6 +2270,11 @@ export default mixins( // Make sure that the node is selected again this.deselectAllNodes(); this.nodeSelectedByName(newName); + + if (isActive) { + this.$store.commit('setActiveNode', newName); + this.renamingActive = false; + } }, deleteEveryEndpoint () { // Check as it does not exist on first load @@ -2744,15 +2748,6 @@ export default mixins( try { await Promise.all(loadPromises); - - if (this.defaultLocale !== 'en') { - try { - const headers = await this.restApi().getNodeTranslationHeaders(); - addHeaders(headers, this.defaultLocale); - } catch (_) { - // no headers available - } - } } catch (error) { this.$showError( error, diff --git a/packages/nodes-base/nodes/Cron/Cron.node.ts b/packages/nodes-base/nodes/Cron/Cron.node.ts index 4a6cfa7d02..54782b08f5 100644 --- a/packages/nodes-base/nodes/Cron/Cron.node.ts +++ b/packages/nodes-base/nodes/Cron/Cron.node.ts @@ -22,7 +22,7 @@ export class Cron implements INodeType { displayName: 'Cron', name: 'cron', icon: 'fa:calendar', - group: ['trigger'], + group: ['trigger', 'schedule'], version: 1, description: 'Triggers the workflow at a specific time', eventTriggerDescription: '', diff --git a/packages/nodes-base/nodes/Interval/Interval.node.ts b/packages/nodes-base/nodes/Interval/Interval.node.ts index c71317bdd0..61b78455b6 100644 --- a/packages/nodes-base/nodes/Interval/Interval.node.ts +++ b/packages/nodes-base/nodes/Interval/Interval.node.ts @@ -12,7 +12,7 @@ export class Interval implements INodeType { displayName: 'Interval', name: 'interval', icon: 'fa:hourglass', - group: ['trigger'], + group: ['trigger', 'schedule'], version: 1, description: 'Triggers the workflow in a given interval', eventTriggerDescription: '',