mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
✨ Render node strings
This commit is contained in:
parent
2d8e158012
commit
7fc0395e95
|
@ -5,6 +5,7 @@
|
||||||
import {
|
import {
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
|
INodeTypeDescription,
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
INodeVersionedType,
|
INodeVersionedType,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
|
@ -18,7 +19,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
// polling nodes the polling times
|
// polling nodes the polling times
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
for (const nodeTypeData of Object.values(nodeTypes)) {
|
for (const nodeTypeData of Object.values(nodeTypes)) {
|
||||||
const nodeType = NodeHelpers.getVersionedTypeNode(nodeTypeData.type);
|
const nodeType = NodeHelpers.getVersionedNodeType(nodeTypeData.type);
|
||||||
const applyParameters = NodeHelpers.getSpecialNodeParameters(nodeType);
|
const applyParameters = NodeHelpers.getSpecialNodeParameters(nodeType);
|
||||||
|
|
||||||
if (applyParameters.length) {
|
if (applyParameters.length) {
|
||||||
|
@ -39,11 +40,26 @@ class NodeTypesClass implements INodeTypes {
|
||||||
return this.nodeTypes[nodeType].type;
|
return this.nodeTypes[nodeType].type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWithPath(
|
||||||
|
nodeTypeName: string,
|
||||||
|
version: number,
|
||||||
|
): { description: INodeTypeDescription } & { sourcePath: string } {
|
||||||
|
const nodeType = this.nodeTypes[nodeTypeName];
|
||||||
|
|
||||||
|
if (!nodeType) {
|
||||||
|
throw new Error(`Unknown node type: ${nodeTypeName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { description } = NodeHelpers.getVersionedNodeType(nodeType.type, version);
|
||||||
|
|
||||||
|
return { description: { ...description }, sourcePath: nodeType.sourcePath };
|
||||||
|
}
|
||||||
|
|
||||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||||
if (this.nodeTypes[nodeType] === undefined) {
|
if (this.nodeTypes[nodeType] === undefined) {
|
||||||
throw new Error(`The node-type "${nodeType}" is not known!`);
|
throw new Error(`The node-type "${nodeType}" is not known!`);
|
||||||
}
|
}
|
||||||
return NodeHelpers.getVersionedTypeNode(this.nodeTypes[nodeType].type, version);
|
return NodeHelpers.getVersionedNodeType(this.nodeTypes[nodeType].type, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable no-restricted-syntax */
|
/* eslint-disable no-restricted-syntax */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable import/no-dynamic-require */
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync, existsSync } from 'fs';
|
||||||
import { dirname as pathDirname, join as pathJoin, resolve as pathResolve } from 'path';
|
import { dirname as pathDirname, join as pathJoin, resolve as pathResolve } from 'path';
|
||||||
import { FindManyOptions, getConnectionManager, In, IsNull, LessThanOrEqual, Not } from 'typeorm';
|
import { FindManyOptions, getConnectionManager, In, IsNull, LessThanOrEqual, Not } from 'typeorm';
|
||||||
import * as bodyParser from 'body-parser';
|
import * as bodyParser from 'body-parser';
|
||||||
|
@ -144,6 +145,7 @@ import { InternalHooksManager } from './InternalHooksManager';
|
||||||
import { TagEntity } from './databases/entities/TagEntity';
|
import { TagEntity } from './databases/entities/TagEntity';
|
||||||
import { WorkflowEntity } from './databases/entities/WorkflowEntity';
|
import { WorkflowEntity } from './databases/entities/WorkflowEntity';
|
||||||
import { NameRequest } from './WorkflowHelpers';
|
import { NameRequest } from './WorkflowHelpers';
|
||||||
|
import { getTranslationPath } from './TranslationHelpers';
|
||||||
|
|
||||||
require('body-parser-xml')(bodyParser);
|
require('body-parser-xml')(bodyParser);
|
||||||
|
|
||||||
|
@ -1152,13 +1154,13 @@ class App {
|
||||||
|
|
||||||
if (onlyLatest) {
|
if (onlyLatest) {
|
||||||
allNodes.forEach((nodeData) => {
|
allNodes.forEach((nodeData) => {
|
||||||
const nodeType = NodeHelpers.getVersionedTypeNode(nodeData);
|
const nodeType = NodeHelpers.getVersionedNodeType(nodeData);
|
||||||
const nodeInfo: INodeTypeDescription = getNodeDescription(nodeType);
|
const nodeInfo: INodeTypeDescription = getNodeDescription(nodeType);
|
||||||
returnData.push(nodeInfo);
|
returnData.push(nodeInfo);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
allNodes.forEach((nodeData) => {
|
allNodes.forEach((nodeData) => {
|
||||||
const allNodeTypes = NodeHelpers.getVersionedTypeNodeAll(nodeData);
|
const allNodeTypes = NodeHelpers.getVersionedNodeTypeAll(nodeData);
|
||||||
allNodeTypes.forEach((element) => {
|
allNodeTypes.forEach((element) => {
|
||||||
const nodeInfo: INodeTypeDescription = getNodeDescription(element);
|
const nodeInfo: INodeTypeDescription = getNodeDescription(element);
|
||||||
returnData.push(nodeInfo);
|
returnData.push(nodeInfo);
|
||||||
|
@ -1179,15 +1181,28 @@ class App {
|
||||||
const nodeInfos = _.get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[];
|
const nodeInfos = _.get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[];
|
||||||
const nodeTypes = NodeTypes();
|
const nodeTypes = NodeTypes();
|
||||||
|
|
||||||
const returnData: INodeTypeDescription[] = [];
|
const language = config.get('defaultLocale') ?? req.headers['accept-language'] ?? 'en';
|
||||||
nodeInfos.forEach((nodeInfo) => {
|
|
||||||
const nodeType = nodeTypes.getByNameAndVersion(nodeInfo.name, nodeInfo.version);
|
|
||||||
if (nodeType?.description) {
|
|
||||||
returnData.push(nodeType.description);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return returnData;
|
if (language === 'en') {
|
||||||
|
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
|
||||||
|
const { description } = nodeTypes.getByNameAndVersion(name, version);
|
||||||
|
if (description) acc.push(description);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add node translations where available
|
||||||
|
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
|
||||||
|
const { description, sourcePath } = nodeTypes.getWithPath(name, version);
|
||||||
|
const mainTranslationPath = getTranslationPath(sourcePath, language);
|
||||||
|
|
||||||
|
if (description && existsSync(mainTranslationPath)) {
|
||||||
|
description.translation = require(mainTranslationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description) acc.push(description);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
8
packages/cli/src/TranslationHelpers.ts
Normal file
8
packages/cli/src/TranslationHelpers.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { join, dirname } from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the path to the translation file for a node.
|
||||||
|
*/
|
||||||
|
export function getTranslationPath(nodeSourcePath: string, language: string): string {
|
||||||
|
return join(dirname(nodeSourcePath), 'translations', `${language}.js`);
|
||||||
|
}
|
|
@ -726,7 +726,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
async init(nodeTypes: INodeTypeData): Promise<void> {}
|
async init(nodeTypes: INodeTypeData): Promise<void> {}
|
||||||
|
|
||||||
getAll(): INodeType[] {
|
getAll(): INodeType[] {
|
||||||
return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedTypeNode(data.type));
|
return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedNodeType(data.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(nodeType: string): INodeType {
|
getByName(nodeType: string): INodeType {
|
||||||
|
@ -734,7 +734,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||||
return NodeHelpers.getVersionedTypeNode(this.nodeTypes[nodeType].type, version);
|
return NodeHelpers.getVersionedNodeType(this.nodeTypes[nodeType].type, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -584,6 +584,7 @@ export interface IRootState {
|
||||||
activeActions: string[];
|
activeActions: string[];
|
||||||
activeNode: string | null;
|
activeNode: string | null;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
credentialTextRenderKeys: { nodeType: string; credentialType: string; } | null;
|
||||||
defaultLocale: string;
|
defaultLocale: string;
|
||||||
endpointWebhook: string;
|
endpointWebhook: string;
|
||||||
endpointWebhookTest: string;
|
endpointWebhookTest: string;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="dialogVisible">
|
<div v-if="dialogVisible">
|
||||||
<el-dialog :visible="dialogVisible" append-to-body :close-on-click-modal="false" width="80%" :title="`Edit ${parameter.displayName}`" :before-close="closeDialog">
|
<el-dialog :visible="dialogVisible" append-to-body :close-on-click-modal="false" width="80%" :title="`${$baseText('codeEdit.edit')} ${$nodeText.topParameterDisplayName(parameter)}`" :before-close="closeDialog">
|
||||||
<div class="ignore-key-press">
|
<div class="ignore-key-press">
|
||||||
<n8n-input-label :label="parameter.displayName">
|
<n8n-input-label :label="$nodeText.topParameterDisplayName(parameter)">
|
||||||
<div :class="$style.editor" @keydown.stop>
|
<div :class="$style.editor" @keydown.stop>
|
||||||
<prism-editor :lineNumbers="true" :code="value" :readonly="isReadOnly" @change="valueChanged" language="js"></prism-editor>
|
<prism-editor :lineNumbers="true" :code="value" :readonly="isReadOnly" @change="valueChanged" language="js"></prism-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="item in parameterOptions"
|
v-for="item in parameterOptions"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
:label="item.displayName"
|
:label="$nodeText.collectionOptionDisplayName(parameter, item)"
|
||||||
:value="item.name">
|
:value="item.name">
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
@ -67,7 +67,8 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getPlaceholderText (): string {
|
getPlaceholderText (): string {
|
||||||
return this.parameter.placeholder ? this.parameter.placeholder : this.$baseText('collectionParameter.choose');
|
const placeholder = this.$nodeText.placeholder(this.parameter);
|
||||||
|
return placeholder ? placeholder : this.$baseText('collectionParameter.choose');
|
||||||
},
|
},
|
||||||
getProperties (): INodeProperties[] {
|
getProperties (): INodeProperties[] {
|
||||||
const returnProperties = [];
|
const returnProperties = [];
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ICredentialType } from 'n8n-workflow';
|
import { ICredentialType, INodeTypeDescription } from 'n8n-workflow';
|
||||||
import { getAppNameFromCredType } from '../helpers';
|
import { getAppNameFromCredType } from '../helpers';
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
@ -81,10 +81,11 @@ import CopyInput from '../CopyInput.vue';
|
||||||
import CredentialInputs from './CredentialInputs.vue';
|
import CredentialInputs from './CredentialInputs.vue';
|
||||||
import OauthButton from './OauthButton.vue';
|
import OauthButton from './OauthButton.vue';
|
||||||
import { renderText } from '../mixins/renderText';
|
import { renderText } from '../mixins/renderText';
|
||||||
|
import { restApi } from '@/components/mixins/restApi';
|
||||||
|
import { addNodeTranslation } from '@/i18n';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default mixins(renderText).extend({
|
export default mixins(renderText, restApi).extend({
|
||||||
name: 'CredentialConfig',
|
name: 'CredentialConfig',
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
Banner,
|
||||||
|
@ -94,6 +95,7 @@ export default mixins(renderText).extend({
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
credentialType: {
|
credentialType: {
|
||||||
|
type: Object,
|
||||||
},
|
},
|
||||||
credentialProperties: {
|
credentialProperties: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -126,6 +128,10 @@ export default mixins(renderText).extend({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
async beforeMount() {
|
||||||
|
await this.findCredentialTextRenderKeys();
|
||||||
|
await this.addNodeTranslationForCredential();
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
appName(): string {
|
appName(): string {
|
||||||
if (!this.credentialType) {
|
if (!this.credentialType) {
|
||||||
|
@ -136,7 +142,7 @@ export default mixins(renderText).extend({
|
||||||
(this.credentialType as ICredentialType).displayName,
|
(this.credentialType as ICredentialType).displayName,
|
||||||
);
|
);
|
||||||
|
|
||||||
return appName || "the service you're connecting to";
|
return appName || this.$baseText('credentialEdit.credentialConfig.theServiceYouReConnectingTo');
|
||||||
},
|
},
|
||||||
credentialTypeName(): string {
|
credentialTypeName(): string {
|
||||||
return (this.credentialType as ICredentialType).name;
|
return (this.credentialType as ICredentialType).name;
|
||||||
|
@ -170,6 +176,62 @@ export default mixins(renderText).extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* Find the keys needed by the mixin to render credential text, and place them in the Vuex store.
|
||||||
|
*/
|
||||||
|
async findCredentialTextRenderKeys() {
|
||||||
|
const nodeTypes = await this.restApi().getNodeTypes();
|
||||||
|
|
||||||
|
// credential type name → node type name
|
||||||
|
const map = nodeTypes.reduce<Record<string, string>>((acc, cur) => {
|
||||||
|
if (!cur.credentials) return acc;
|
||||||
|
|
||||||
|
cur.credentials.forEach(cred => {
|
||||||
|
if (acc[cred.name]) return;
|
||||||
|
acc[cred.name] = cur.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const renderKeys = {
|
||||||
|
nodeType: map[this.credentialType.name],
|
||||||
|
credentialType: this.credentialType.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$store.commit('setCredentialTextRenderKeys', renderKeys);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to the translation object the node translation
|
||||||
|
* for the credential being viewed.
|
||||||
|
*/
|
||||||
|
async addNodeTranslationForCredential() {
|
||||||
|
// TODO i18n: Check if node translation has already been added (via NodeView)
|
||||||
|
|
||||||
|
const { nodeType }: { nodeType: string } = this.$store.getters.credentialTextRenderKeys;
|
||||||
|
const version = await this.getCurrentNodeVersion(nodeType);
|
||||||
|
|
||||||
|
const nodeToBeFetched = [{ name: nodeType, version }];
|
||||||
|
|
||||||
|
const nodesInfo = await this.restApi().getNodesInformation(nodeToBeFetched);
|
||||||
|
const nodeInfo = nodesInfo.pop();
|
||||||
|
|
||||||
|
if (nodeInfo && nodeInfo.translation) {
|
||||||
|
addNodeTranslation(nodeInfo.translation, this.$store.getters.defaultLocale);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current version for a node type.
|
||||||
|
*/
|
||||||
|
async getCurrentNodeVersion(targetNodeType: string) {
|
||||||
|
const { allNodeTypes }: { allNodeTypes: INodeTypeDescription[] } = this.$store.getters;
|
||||||
|
const found = allNodeTypes.find(nodeType => nodeType.name === targetNodeType);
|
||||||
|
|
||||||
|
return found ? found.version : 1;
|
||||||
|
},
|
||||||
|
|
||||||
onDataChange (event: { name: string; value: string | number | boolean | Date | null }): void {
|
onDataChange (event: { name: string; value: string | number | boolean | Date | null }): void {
|
||||||
this.$emit('change', event);
|
this.$emit('change', event);
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="item in parameterOptions"
|
v-for="item in parameterOptions"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
:label="item.displayName"
|
:label="$nodeText.collectionOptionDisplayName(parameter, item)"
|
||||||
:value="item.name">
|
:value="item.name">
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
@ -85,7 +85,8 @@ export default mixins(genericHelpers)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getPlaceholderText (): string {
|
getPlaceholderText (): string {
|
||||||
return this.parameter.placeholder ? this.parameter.placeholder : this.$baseText('fixedCollectionParameter.choose');
|
const placeholder = this.$nodeText.placeholder(this.parameter);
|
||||||
|
return placeholder ? placeholder : this.$baseText('fixedCollectionParameter.choose');
|
||||||
},
|
},
|
||||||
getProperties (): INodePropertyCollection[] {
|
getProperties (): INodePropertyCollection[] {
|
||||||
const returnProperties = [];
|
const returnProperties = [];
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div @keydown.stop class="duplicate-parameter">
|
<div @keydown.stop class="duplicate-parameter">
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:label="parameter.displayName"
|
:label="$nodeText.topParameterDisplayName(parameter)"
|
||||||
:tooltipText="parameter.description"
|
:tooltipText="$nodeText.topParameterDescription(parameter)"
|
||||||
:underline="true"
|
:underline="true"
|
||||||
:labelHoverableOnly="true"
|
:labelHoverableOnly="true"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -64,7 +64,14 @@ export default mixins(genericHelpers)
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
addButtonText (): string {
|
addButtonText (): string {
|
||||||
return (this.parameter.typeOptions && this.parameter.typeOptions.multipleValueButtonText) ? this.parameter.typeOptions.multipleValueButtonText : 'Add item';
|
if (
|
||||||
|
!this.parameter.typeOptions &&
|
||||||
|
!this.parameter.typeOptions.multipleValueButtonText
|
||||||
|
) {
|
||||||
|
return this.$baseText('multipleParameter.addItem');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$nodeText.multipleValueButtonText(this.parameter);
|
||||||
},
|
},
|
||||||
hideDelete (): boolean {
|
hideDelete (): boolean {
|
||||||
return this.parameter.options.length === 1;
|
return this.parameter.options.length === 1;
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:placeholder="isValueExpression?'':parameter.placeholder"
|
:placeholder="isValueExpression ? '' : getPlaceholder()"
|
||||||
>
|
>
|
||||||
<div slot="suffix" class="expand-input-icon-container">
|
<div slot="suffix" class="expand-input-icon-container">
|
||||||
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" icon="external-link-alt" class="edit-window-button clickable" :title="$baseText('parameterInput.openEditWindow')" @click="displayEditDialog()" />
|
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" icon="external-link-alt" class="edit-window-button clickable" :title="$baseText('parameterInput.openEditWindow')" @click="displayEditDialog()" />
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
:value="displayValue"
|
:value="displayValue"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:placeholder="parameter.placeholder?parameter.placeholder:$baseText('parameterInput.selectDateAndTime')"
|
:placeholder="parameter.placeholder ? getPlaceholder() : $baseText('parameterInput.selectDateAndTime')"
|
||||||
:picker-options="dateTimePickerOptions"
|
:picker-options="dateTimePickerOptions"
|
||||||
@change="valueChanged"
|
@change="valueChanged"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
|
@ -124,11 +124,13 @@
|
||||||
v-for="option in parameterOptions"
|
v-for="option in parameterOptions"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
:label="option.name"
|
:label="getOptionsOptionDisplayName(option)"
|
||||||
>
|
>
|
||||||
<div class="list-option">
|
<div class="list-option">
|
||||||
<div class="option-headline">{{ option.name }}</div>
|
<div class="option-headline">
|
||||||
<div v-if="option.description" class="option-description" v-html="option.description"></div>
|
{{ getOptionsOptionDisplayName(option) }}
|
||||||
|
</div>
|
||||||
|
<div v-if="option.description" class="option-description" v-html="getOptionsOptionDescription(option)"></div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
@ -148,10 +150,10 @@
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
>
|
>
|
||||||
<n8n-option v-for="option in parameterOptions" :value="option.value" :key="option.value" :label="option.name" >
|
<n8n-option v-for="option in parameterOptions" :value="option.value" :key="option.value" :label="getOptionsOptionDisplayName(option)">
|
||||||
<div class="list-option">
|
<div class="list-option">
|
||||||
<div class="option-headline">{{ option.name }}</div>
|
<div class="option-headline">{{ getOptionsOptionDisplayName(option) }}</div>
|
||||||
<div v-if="option.description" class="option-description" v-html="option.description"></div>
|
<div v-if="option.description" class="option-description" v-html="getOptionsOptionDescription(option)"></div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
@ -240,6 +242,7 @@ export default mixins(
|
||||||
'value',
|
'value',
|
||||||
'hideIssues', // boolean
|
'hideIssues', // boolean
|
||||||
'errorHighlight',
|
'errorHighlight',
|
||||||
|
'isForCredential', // boolean
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -255,14 +258,14 @@ export default mixins(
|
||||||
dateTimePickerOptions: {
|
dateTimePickerOptions: {
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
{
|
{
|
||||||
text: 'Today',
|
text: 'Today', // TODO
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
onClick (picker: any) {
|
onClick (picker: any) {
|
||||||
picker.$emit('pick', new Date());
|
picker.$emit('pick', new Date());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Yesterday',
|
text: 'Yesterday', // TODO
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
onClick (picker: any) {
|
onClick (picker: any) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
@ -271,7 +274,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'A week ago',
|
text: 'A week ago', // TODO
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
onClick (picker: any) {
|
onClick (picker: any) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
@ -325,20 +328,26 @@ export default mixins(
|
||||||
return this.$store.getters.activeNode;
|
return this.$store.getters.activeNode;
|
||||||
},
|
},
|
||||||
displayTitle (): string {
|
displayTitle (): string {
|
||||||
let title = `Parameter: "${this.shortPath}"`;
|
const interpolation = { interpolate: { shortPath: this.shortPath } };
|
||||||
if (this.getIssues.length) {
|
|
||||||
title += ` has issues`;
|
if (this.getIssues.length && this.isValueExpression) {
|
||||||
if (this.isValueExpression === true) {
|
return this.$baseText(
|
||||||
title += ` and expression`;
|
'parameterInput.parameterHasIssuesAndExpression',
|
||||||
}
|
interpolation,
|
||||||
title += `!`;
|
);
|
||||||
} else {
|
} else if (this.getIssues.length && !this.isValueExpression) {
|
||||||
if (this.isValueExpression === true) {
|
return this.$baseText(
|
||||||
title += ` has expression`;
|
'parameterInput.parameterHasIssues',
|
||||||
}
|
interpolation,
|
||||||
|
);
|
||||||
|
} else if (!this.getIssues.length && this.isValueExpression) {
|
||||||
|
return this.$baseText(
|
||||||
|
'parameterInput.parameterHasExpression',
|
||||||
|
interpolation,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return title;
|
return this.$baseText('parameterInput.parameter', interpolation);
|
||||||
},
|
},
|
||||||
displayValue (): string | number | boolean | null {
|
displayValue (): string | number | boolean | null {
|
||||||
if (this.remoteParameterOptionsLoading === true) {
|
if (this.remoteParameterOptionsLoading === true) {
|
||||||
|
@ -346,7 +355,7 @@ export default mixins(
|
||||||
// to user that the data is loading. If not it would
|
// to user that the data is loading. If not it would
|
||||||
// display the user the key instead of the value it
|
// display the user the key instead of the value it
|
||||||
// represents
|
// represents
|
||||||
return 'Loading options...';
|
return this.$baseText('parameterInput.loadingOptions');
|
||||||
}
|
}
|
||||||
|
|
||||||
let returnValue;
|
let returnValue;
|
||||||
|
@ -415,7 +424,7 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
computedValue = this.resolveExpression(this.value) as NodeParameterValue;
|
computedValue = this.resolveExpression(this.value) as NodeParameterValue;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
computedValue = `[ERROR: ${error.message}]`;
|
computedValue = `[${this.$baseText('parameterInput.error')}}: ${error.message}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to convert it into the corret type
|
// Try to convert it into the corret type
|
||||||
|
@ -559,6 +568,22 @@ export default mixins(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getPlaceholder(): string {
|
||||||
|
return this.isForCredential
|
||||||
|
? this.$credText.placeholder(this.parameter)
|
||||||
|
: this.$nodeText.placeholder(this.parameter);
|
||||||
|
},
|
||||||
|
getOptionsOptionDisplayName(option: { value: string; name: string }): string {
|
||||||
|
return this.isForCredential
|
||||||
|
? this.$credText.optionsOptionDisplayName(this.parameter, option)
|
||||||
|
: this.$nodeText.optionsOptionDisplayName(this.parameter, option);
|
||||||
|
},
|
||||||
|
getOptionsOptionDescription(option: { value: string; description: string }): string {
|
||||||
|
return this.isForCredential
|
||||||
|
? this.$credText.optionsOptionDescription(this.parameter, option)
|
||||||
|
: this.$nodeText.optionsOptionDescription(this.parameter, option);
|
||||||
|
},
|
||||||
|
|
||||||
async loadRemoteParameterOptions () {
|
async loadRemoteParameterOptions () {
|
||||||
if (this.node === null || this.remoteMethod === undefined || this.remoteParameterOptionsLoading) {
|
if (this.node === null || this.remoteMethod === undefined || this.remoteParameterOptionsLoading) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:label="parameter.displayName"
|
:label="$credText.topParameterDisplayName(parameter)"
|
||||||
:tooltipText="parameter.description"
|
:tooltipText="$credText.topParameterDescription(parameter)"
|
||||||
:required="parameter.required"
|
:required="parameter.required"
|
||||||
:showTooltip="focused"
|
:showTooltip="focused"
|
||||||
>
|
>
|
||||||
|
@ -13,6 +13,7 @@
|
||||||
:displayOptions="true"
|
:displayOptions="true"
|
||||||
:documentationUrl="documentationUrl"
|
:documentationUrl="documentationUrl"
|
||||||
:errorHighlight="showRequiredErrors"
|
:errorHighlight="showRequiredErrors"
|
||||||
|
:isForCredential="true"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@textInput="valueChanged"
|
@textInput="valueChanged"
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
/>
|
/>
|
||||||
<div class="errors" v-if="showRequiredErrors">
|
<div class="errors" v-if="showRequiredErrors">
|
||||||
{{ $baseText('parameterInputExpanded.thisFieldIsRequired') }} <a v-if="documentationUrl" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">Open docs</a>
|
{{ $baseText('parameterInputExpanded.thisFieldIsRequired') }} <a v-if="documentationUrl" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">{{ $baseText('parameterInputExpanded.openDocs') }}</a>
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
</template>
|
</template>
|
||||||
|
@ -29,8 +30,10 @@
|
||||||
import { IUpdateInformation } from '@/Interface';
|
import { IUpdateInformation } from '@/Interface';
|
||||||
import ParameterInput from './ParameterInput.vue';
|
import ParameterInput from './ParameterInput.vue';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'ParameterInputExpanded',
|
name: 'ParameterInputExpanded',
|
||||||
components: {
|
components: {
|
||||||
ParameterInput,
|
ParameterInput,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:label="parameter.displayName"
|
:label="$nodeText.topParameterDisplayName(parameter)"
|
||||||
:tooltipText="parameter.description"
|
:tooltipText="$nodeText.topParameterDescription(parameter)"
|
||||||
:showTooltip="focused"
|
:showTooltip="focused"
|
||||||
:bold="false"
|
:bold="false"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -27,8 +27,10 @@ import {
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
|
|
||||||
import ParameterInput from '@/components/ParameterInput.vue';
|
import ParameterInput from '@/components/ParameterInput.vue';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue
|
export default mixins(renderText)
|
||||||
.extend({
|
.extend({
|
||||||
name: 'ParameterInputFull',
|
name: 'ParameterInputFull',
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<div v-else-if="parameter.type === 'notice'" class="parameter-item parameter-notice">
|
<div v-else-if="parameter.type === 'notice'" class="parameter-item parameter-notice">
|
||||||
<n8n-text size="small">
|
<n8n-text size="small">
|
||||||
<span v-html="parameter.displayName"></span>
|
<span v-html="$nodeText.topParameterDisplayName(parameter)"></span>
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:label="parameter.displayName"
|
:label="$nodeText.topParameterDisplayName(parameter)"
|
||||||
:tooltipText="parameter.description"
|
:tooltipText="$nodeText.topParameterDescription(parameter)"
|
||||||
size="small"
|
size="small"
|
||||||
:underline="true"
|
:underline="true"
|
||||||
:labelHoverableOnly="true"
|
:labelHoverableOnly="true"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="dialogVisible">
|
<div v-if="dialogVisible">
|
||||||
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`Edit ${parameter.displayName}`" :before-close="closeDialog">
|
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`${$baseText('textEdit.edit')} ${$nodeText.topParameterDisplayName(parameter)}`" :before-close="closeDialog">
|
||||||
|
|
||||||
<div class="ignore-key-press">
|
<div class="ignore-key-press">
|
||||||
<n8n-input-label :label="parameter.displayName">
|
<n8n-input-label :label="$nodeText.topParameterDisplayName(parameter)">
|
||||||
<div @keydown.stop @keydown.esc="closeDialog()">
|
<div @keydown.stop @keydown.esc="closeDialog()">
|
||||||
<n8n-input v-model="tempValue" type="textarea" ref="inputField" :value="value" :placeholder="parameter.placeholder" @change="valueChanged" @keydown.stop="noOp" :rows="15" />
|
<n8n-input v-model="tempValue" type="textarea" ref="inputField" :value="value" :placeholder="$nodeText.placeholder(parameter)" @change="valueChanged" @keydown.stop="noOp" :rows="15" />
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,9 +16,10 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
|
|
||||||
name: 'TextEdit',
|
name: 'TextEdit',
|
||||||
props: [
|
props: [
|
||||||
'dialogVisible',
|
'dialogVisible',
|
||||||
|
|
|
@ -1,191 +1,211 @@
|
||||||
|
/* tslint:disable: variable-name */
|
||||||
|
|
||||||
// import { TranslationPath } from '@/Interface';
|
// import { TranslationPath } from '@/Interface';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export const renderText = Vue.extend({
|
const REUSABLE_TEXT_KEY = 'reusableText';
|
||||||
computed: {
|
const CREDENTIALS_MODAL_KEY = 'credentialsModal';
|
||||||
/**
|
const NODE_VIEW_KEY = 'nodeView';
|
||||||
* Node type for the active node in `NodeView.vue`.
|
|
||||||
*/
|
|
||||||
activeNodeType (): string {
|
|
||||||
return this.$store.getters.activeNode.type;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
export const renderText = Vue.extend({
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Render a string of base text, i.e. a string with a **fixed path** to the value in the locale object. Allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
* Render a string of base text, i.e. a string with a fixed path to the localized value in the base text object. Optionally allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
||||||
* ```js
|
|
||||||
* $baseText('fixed.path.to.localized.value');
|
|
||||||
* $baseText('fixed.path.to.localized.value', { interpolate: { var: arg } });
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
$baseText(
|
$baseText(
|
||||||
key: string,
|
key: string, options?: { interpolate: { [key: string]: string } },
|
||||||
options?: { interpolate: { [key: string]: string } },
|
|
||||||
): string {
|
): string {
|
||||||
return this.$t(key, options && options.interpolate).toString();
|
return this.$t(key, options && options.interpolate).toString();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a node- or credentials-specific string.
|
* Render a string of dynamic text, i.e. a string with a constructed path to the localized value in the node text object, either in the credentials modal (`$credText`) or in the node view (`$nodeView`). **Private method**, to be called only from the two namespaces within this mixin.
|
||||||
* Called in-mixin by node- or credentials-specific methods,
|
|
||||||
* which are called directly in Vue templates.
|
|
||||||
*/
|
*/
|
||||||
translateSpecific(
|
__render(
|
||||||
{ key, fallback }: { key: string, fallback: string },
|
{ key, fallback }: { key: string, fallback: string },
|
||||||
): string {
|
) {
|
||||||
return this.$te(key) ? this.$t(key).toString() : fallback;
|
return this.$te(key) ? this.$t(key).toString() : fallback;
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// -----------------------------------------
|
computed: {
|
||||||
// node-specific methods
|
$credText () {
|
||||||
// -----------------------------------------
|
const { credentialTextRenderKeys: keys } = this.$store.getters;
|
||||||
|
const nodeType = keys ? keys.nodeType : '';
|
||||||
|
const credentialType = keys ? keys.credentialType : '';
|
||||||
|
const credentialPrefix = `${nodeType}.${CREDENTIALS_MODAL_KEY}.${credentialType}`;
|
||||||
|
const context = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a top-level node parameter name, i.e. leftmost parameter in `NodeView.vue`.
|
* Display name for a top-level parameter in the credentials modal.
|
||||||
*/
|
*/
|
||||||
$translateNodeParameterName(
|
topParameterDisplayName(
|
||||||
{ name: parameterName, displayName }: { name: string; displayName: string; },
|
{ name: parameterName, displayName }: { name: string; displayName: string; },
|
||||||
) {
|
) {
|
||||||
return this.translateSpecific({
|
if (['clientId', 'clientSecret'].includes(parameterName)) {
|
||||||
key: `${this.activeNodeType}.parameters.${parameterName}.displayName`,
|
return context.__render({
|
||||||
|
key: `${REUSABLE_TEXT_KEY}.oauth2.${parameterName}`,
|
||||||
|
fallback: displayName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.__render({
|
||||||
|
key: `${credentialPrefix}.${parameterName}.displayName`,
|
||||||
fallback: displayName,
|
fallback: displayName,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a top-level parameter description for a node or for credentials.
|
* Description for a top-level parameter in the credentials modal.
|
||||||
*/
|
*/
|
||||||
$translateDescription(
|
topParameterDescription(
|
||||||
{ name: parameterName, description }: { name: string; description: string; },
|
{ name: parameterName, description }: { name: string; description: string; },
|
||||||
) {
|
) {
|
||||||
return this.translateSpecific({
|
return context.__render({
|
||||||
key: `${this.activeNodeType}.parameters.${parameterName}.description`,
|
key: `${credentialPrefix}.${parameterName}.description`,
|
||||||
fallback: description,
|
fallback: description,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the name for an option in a `collection` or `fixed collection` parameter,
|
* Display name for an option inside an `options` or `multiOptions` parameter in the credentials modal.
|
||||||
* e.g. an option name in an "Additional Options" fixed collection.
|
|
||||||
*/
|
*/
|
||||||
$translateCollectionOptionName(
|
optionsOptionDisplayName(
|
||||||
{ name: parameterName }: { name: string; },
|
{ name: parameterName }: { name: string; },
|
||||||
{ name: optionName, displayName }: { name: string; displayName: string; },
|
{ value: optionName, name: displayName }: { value: string; name: string; },
|
||||||
) {
|
) {
|
||||||
return this.translateSpecific({
|
return context.__render({
|
||||||
key: `${this.activeNodeType}.parameters.${parameterName}.options.${optionName}.displayName`,
|
key: `${credentialPrefix}.${parameterName}.options.${optionName}.displayName`,
|
||||||
fallback: displayName,
|
fallback: displayName,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the label for a button that adds another field-input pair to a collection.
|
* Description for an option inside an `options` or `multiOptions` parameter in the credentials modal.
|
||||||
*/
|
*/
|
||||||
$translateMultipleValueButtonText(
|
optionsOptionDescription(
|
||||||
{ name: parameterName, typeOptions: { multipleValueButtonText } }:
|
{ name: parameterName }: { name: string; },
|
||||||
{ name: string, typeOptions: { multipleValueButtonText: string } },
|
{ value: optionName, description }: { value: string; description: string; },
|
||||||
) {
|
) {
|
||||||
return this.translateSpecific({
|
return context.__render({
|
||||||
key: `${this.activeNodeType}.parameters.${parameterName}.multipleValueButtonText`,
|
key: `${credentialPrefix}.${parameterName}.options.${optionName}.description`,
|
||||||
|
fallback: description,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder for a `string` or `collection` or `fixedCollection` parameter in the credentials modal.
|
||||||
|
* - For a `string` parameter, the placeholder is unselectable greyed-out sample text.
|
||||||
|
* - For a `collection` or `fixedCollection` parameter, the placeholder is the button text.
|
||||||
|
*/
|
||||||
|
placeholder(
|
||||||
|
{ name: parameterName, displayName }: { name: string; displayName: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${credentialPrefix}.${parameterName}.placeholder`,
|
||||||
|
fallback: displayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
$nodeText () {
|
||||||
|
const nodePrefix = `${this.$store.getters.activeNode.type}.${NODE_VIEW_KEY}`;
|
||||||
|
const context = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Display name for a top-level parameter in the node view.
|
||||||
|
*/
|
||||||
|
topParameterDisplayName(
|
||||||
|
{ name: parameterName, displayName }: { name: string; displayName: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.displayName`,
|
||||||
|
fallback: displayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description for a top-level parameter in the node view in the node view.
|
||||||
|
*/
|
||||||
|
topParameterDescription(
|
||||||
|
{ name: parameterName, description }: { name: string; description: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.description`,
|
||||||
|
fallback: description,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display name for an option inside a `collection` or `fixedCollection` parameter in the node view.
|
||||||
|
*/
|
||||||
|
collectionOptionDisplayName(
|
||||||
|
{ name: parameterName }: { name: string; },
|
||||||
|
{ name: optionName, displayName }: { name: string; displayName: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.options.${optionName}.displayName`,
|
||||||
|
fallback: displayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display name for an option inside an `options` or `multiOptions` parameter in the node view.
|
||||||
|
*/
|
||||||
|
optionsOptionDisplayName(
|
||||||
|
{ name: parameterName }: { name: string; },
|
||||||
|
{ value: optionName, name: displayName }: { value: string; name: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.options.${optionName}.displayName`,
|
||||||
|
fallback: displayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description for an option inside an `options` or `multiOptions` parameter in the node view.
|
||||||
|
*/
|
||||||
|
optionsOptionDescription(
|
||||||
|
{ name: parameterName }: { name: string; },
|
||||||
|
{ value: optionName, description }: { value: string; description: string; },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.options.${optionName}.description`,
|
||||||
|
fallback: description,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text for a button to add another option inside a `collection` or `fixedCollection` parameter having`multipleValues: true` in the node view.
|
||||||
|
*/
|
||||||
|
multipleValueButtonText(
|
||||||
|
{ name: parameterName, typeOptions: { multipleValueButtonText } }:
|
||||||
|
{ name: string; typeOptions: { multipleValueButtonText: string; } },
|
||||||
|
) {
|
||||||
|
return context.__render({
|
||||||
|
key: `${nodePrefix}.${parameterName}.multipleValueButtonText`,
|
||||||
fallback: multipleValueButtonText,
|
fallback: multipleValueButtonText,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// creds-specific methods
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a credentials property name, i.e. leftmost parameter in `CredentialsEdit.vue`.
|
* Placeholder for a `string` or `collection` or `fixedCollection` parameter in the node view.
|
||||||
|
* - For a `string` parameter, the placeholder is unselectable greyed-out sample text.
|
||||||
|
* - For a `collection` or `fixedCollection` parameter, the placeholder is the button text.
|
||||||
*/
|
*/
|
||||||
$translateCredentialsPropertyName(
|
placeholder(
|
||||||
{ name: parameterName, displayName }: { name: string; displayName: string; },
|
|
||||||
{ nodeType, credentialsName }: { nodeType: string, credentialsName: string; },
|
|
||||||
) {
|
|
||||||
if (['clientId', 'clientSecret'].includes(parameterName)) {
|
|
||||||
return this.$t(`oauth2.${parameterName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.translateSpecific({
|
|
||||||
key: `${nodeType}.credentials.${credentialsName}.${parameterName}.displayName`,
|
|
||||||
fallback: displayName,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate a credentials property description, i.e. label tooltip in `CredentialsEdit.vue`.
|
|
||||||
*/
|
|
||||||
$translateCredentialsPropertyDescription(
|
|
||||||
{ name: parameterName, description }: { name: string; description: string; },
|
|
||||||
{ nodeType, credentialsName }: { nodeType: string, credentialsName: string; },
|
|
||||||
) {
|
|
||||||
return this.translateSpecific({
|
|
||||||
key: `${nodeType}.credentials.${credentialsName}.${parameterName}.description`,
|
|
||||||
fallback: description,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// node- and creds-specific methods
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the placeholder inside the input field for a string-type parameter.
|
|
||||||
*/
|
|
||||||
$translatePlaceholder(
|
|
||||||
{ name: parameterName, placeholder }: { name: string; placeholder: string; },
|
{ name: parameterName, placeholder }: { name: string; placeholder: string; },
|
||||||
isCredential = false,
|
|
||||||
{ nodeType, credentialsName } = { nodeType: '', credentialsName: '' },
|
|
||||||
) {
|
) {
|
||||||
const key = isCredential
|
return context.__render({
|
||||||
? `${nodeType}.credentials.${credentialsName}.placeholder`
|
key: `${nodePrefix}.${parameterName}.placeholder`,
|
||||||
: `${this.activeNodeType}.parameters.${parameterName}.placeholder`;
|
|
||||||
|
|
||||||
return this.translateSpecific({
|
|
||||||
key,
|
|
||||||
fallback: placeholder,
|
fallback: placeholder,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
};
|
||||||
/**
|
|
||||||
* Translate the name for an option in an `options` parameter,
|
|
||||||
* e.g. an option name in a "Resource" or "Operation" dropdown menu.
|
|
||||||
*/
|
|
||||||
$translateOptionsOptionName(
|
|
||||||
{ name: parameterName }: { name: string },
|
|
||||||
{ value: optionName, name: displayName }: { value: string; name: string; },
|
|
||||||
isCredential = false,
|
|
||||||
{ nodeType, credentialsName } = { nodeType: '', credentialsName: '' },
|
|
||||||
) {
|
|
||||||
const key = isCredential
|
|
||||||
? `${nodeType}.credentials.${credentialsName}.options.${optionName}.displayName`
|
|
||||||
: `${this.activeNodeType}.parameters.${parameterName}.options.${optionName}.displayName`;
|
|
||||||
|
|
||||||
return this.translateSpecific({
|
|
||||||
key,
|
|
||||||
fallback: displayName,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the description for an option in an `options` parameter,
|
|
||||||
* e.g. an option name in a "Resource" or "Operation" dropdown menu.
|
|
||||||
*/
|
|
||||||
$translateOptionsOptionDescription(
|
|
||||||
{ name: parameterName }: { name: string },
|
|
||||||
{ value: optionName, description }: { value: string; description: string; },
|
|
||||||
isCredential = false,
|
|
||||||
{ nodeType, credentialsName } = { nodeType: '', credentialsName: '' },
|
|
||||||
) {
|
|
||||||
const key = isCredential
|
|
||||||
? `${nodeType}.credentials.${credentialsName}.options.${optionName}.description`
|
|
||||||
: `${this.activeNodeType}.parameters.${parameterName}.options.${optionName}.description`;
|
|
||||||
|
|
||||||
return this.translateSpecific({
|
|
||||||
key,
|
|
||||||
fallback: description,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,24 +2,27 @@ import Vue from 'vue';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import englishBaseText from './locales/en';
|
import englishBaseText from './locales/en';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
|
// TODO i18n: Remove next line
|
||||||
console.log('About to initialize i18n'); // eslint-disable-line no-console
|
console.log('About to initialize i18n'); // eslint-disable-line no-console
|
||||||
|
|
||||||
export const i18n = new VueI18n({
|
export const i18n = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: 'en',
|
||||||
messages: englishBaseText,
|
messages: { en: englishBaseText },
|
||||||
silentTranslationWarn: true,
|
silentTranslationWarn: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadedLanguages = ['en'];
|
const loadedLanguages = ['en'];
|
||||||
|
|
||||||
function setLanguage(language: string): string {
|
function setLanguage(language: string) {
|
||||||
i18n.locale = language;
|
i18n.locale = language;
|
||||||
axios.defaults.headers.common['Accept-Language'] = language;
|
axios.defaults.headers.common['Accept-Language'] = language;
|
||||||
document!.querySelector('html')!.setAttribute('lang', language);
|
document!.querySelector('html')!.setAttribute('lang', language);
|
||||||
|
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,21 +39,30 @@ export async function loadLanguage(language?: string) {
|
||||||
return Promise.resolve(setLanguage(language));
|
return Promise.resolve(setLanguage(language));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { default: { [language]: messages }} = require(`./locales/${language}`);
|
const baseText = require(`./locales/${language}`).default; // TODO i18n: `path.join()`
|
||||||
i18n.setLocaleMessage(language, messages);
|
console.log(baseText);
|
||||||
|
i18n.setLocaleMessage(language, baseText);
|
||||||
loadedLanguages.push(language);
|
loadedLanguages.push(language);
|
||||||
|
|
||||||
return setLanguage(language);
|
return setLanguage(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addNodeTranslations(translations: { [key: string]: string | object }) {
|
export function addNodeTranslation(
|
||||||
const lang = Object.keys(translations)[0];
|
nodeTranslation: { [key: string]: object },
|
||||||
const messages = translations[lang];
|
language: string,
|
||||||
|
) {
|
||||||
const newNodesBase = {
|
const newNodesBase = {
|
||||||
'n8n-nodes-base': Object.assign(
|
'n8n-nodes-base': Object.assign(
|
||||||
i18n.messages[lang]['n8n-nodes-base'],
|
i18n.messages[language]['n8n-nodes-base'] || {},
|
||||||
messages,
|
nodeTranslation,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
i18n.setLocaleMessage(lang, Object.assign(i18n.messages[lang], newNodesBase));
|
|
||||||
|
// TODO i18n: Remove next line
|
||||||
|
console.log('newNodesBase', newNodesBase); // eslint-disable-line no-console
|
||||||
|
|
||||||
|
i18n.setLocaleMessage(
|
||||||
|
language,
|
||||||
|
Object.assign(i18n.messages[language], newNodesBase),
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -1,5 +1,16 @@
|
||||||
export default {
|
export default {
|
||||||
de: {
|
reusableText: {
|
||||||
|
oauth2: {
|
||||||
|
clientId: '🇩🇪 Client ID',
|
||||||
|
clientSecret: '🇩🇪 Client Secret',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textEdit: {
|
||||||
|
edit: '🇩🇪 Edit',
|
||||||
|
},
|
||||||
|
codeEdit: {
|
||||||
|
edit: '🇩🇪 Edit',
|
||||||
|
},
|
||||||
versionCard: {
|
versionCard: {
|
||||||
thisVersionHasASecurityIssue: '🇩🇪 This version has a security issue.<br/>It is listed here for completeness.',
|
thisVersionHasASecurityIssue: '🇩🇪 This version has a security issue.<br/>It is listed here for completeness.',
|
||||||
released: '🇩🇪 Released',
|
released: '🇩🇪 Released',
|
||||||
|
@ -145,9 +156,10 @@ export default {
|
||||||
reconnectOAuth2Credential: '🇩🇪 Reconnect OAuth2 Credential',
|
reconnectOAuth2Credential: '🇩🇪 Reconnect OAuth2 Credential',
|
||||||
connectionTestedSuccessfully: '🇩🇪 Connection tested successfully',
|
connectionTestedSuccessfully: '🇩🇪 Connection tested successfully',
|
||||||
oAuthRedirectUrl: '🇩🇪 OAuth Redirect URL',
|
oAuthRedirectUrl: '🇩🇪 OAuth Redirect URL',
|
||||||
clickToCopy: '🇩🇪 ClickToCopy',
|
clickToCopy: '🇩🇪 Click To Copy',
|
||||||
subtitle: '🇩🇪 In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL',
|
subtitle: '🇩🇪 In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL',
|
||||||
redirectUrlCopiedToClipboard: '🇩🇪 Redirect URL copied to clipboard',
|
redirectUrlCopiedToClipboard: '🇩🇪 Redirect URL copied to clipboard',
|
||||||
|
theServiceYouReConnectingTo: "🇩🇪 the service you're connecting to",
|
||||||
},
|
},
|
||||||
oAuthButton: {
|
oAuthButton: {
|
||||||
signInWithGoogle: '🇩🇪 Sign in with Google',
|
signInWithGoogle: '🇩🇪 Sign in with Google',
|
||||||
|
@ -398,6 +410,7 @@ export default {
|
||||||
workflows: '🇩🇪 Workflows',
|
workflows: '🇩🇪 Workflows',
|
||||||
},
|
},
|
||||||
multipleParameter: {
|
multipleParameter: {
|
||||||
|
addItem: '🇩🇪 Add item',
|
||||||
currentlyNoItemsExist: '🇩🇪 Currently no items exist',
|
currentlyNoItemsExist: '🇩🇪 Currently no items exist',
|
||||||
moveUp: '🇩🇪 Move up',
|
moveUp: '🇩🇪 Move up',
|
||||||
moveDown: '🇩🇪 Move down',
|
moveDown: '🇩🇪 Move down',
|
||||||
|
@ -619,6 +632,7 @@ export default {
|
||||||
withWorkflowTagsYouReFree: '🇩🇪 With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
withWorkflowTagsYouReFree: '🇩🇪 With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
||||||
},
|
},
|
||||||
parameterInput: {
|
parameterInput: {
|
||||||
|
loadingOptions: '🇩🇪 Loading options...',
|
||||||
addExpression: '🇩🇪 Add Expression',
|
addExpression: '🇩🇪 Add Expression',
|
||||||
removeExpression: '🇩🇪 Remove Expression',
|
removeExpression: '🇩🇪 Remove Expression',
|
||||||
refreshList: '🇩🇪 Refresh List',
|
refreshList: '🇩🇪 Refresh List',
|
||||||
|
@ -627,9 +641,15 @@ export default {
|
||||||
openEditWindow: '🇩🇪 Open Edit Window',
|
openEditWindow: '🇩🇪 Open Edit Window',
|
||||||
issues: '🇩🇪 Issues',
|
issues: '🇩🇪 Issues',
|
||||||
parameterOptions: '🇩🇪 Parameter Options',
|
parameterOptions: '🇩🇪 Parameter Options',
|
||||||
|
parameterHasIssuesAndExpression: '🇩🇪 Parameter: "{shortPath}" has issues and expression!',
|
||||||
|
parameterHasIssues: '🇩🇪 Parameter: "{shortPath}" has issues!',
|
||||||
|
parameterHasExpression: '🇩🇪 Parameter: "{shortPath}" has expression!',
|
||||||
|
parameter: '🇩🇪 Parameter: "{shortPath}"',
|
||||||
|
error: '🇩🇪 ERROR',
|
||||||
},
|
},
|
||||||
parameterInputExpanded: {
|
parameterInputExpanded: {
|
||||||
thisFieldIsRequired: '🇩🇪 This field is required.',
|
thisFieldIsRequired: '🇩🇪 This field is required.',
|
||||||
|
openDocs: '🇩🇪 Open docs',
|
||||||
},
|
},
|
||||||
parameterInputList: {
|
parameterInputList: {
|
||||||
delete: '🇩🇪 Delete',
|
delete: '🇩🇪 Delete',
|
||||||
|
@ -933,7 +953,4 @@ export default {
|
||||||
timeoutWorkflow: '🇩🇪 Timeout Workflow',
|
timeoutWorkflow: '🇩🇪 Timeout Workflow',
|
||||||
timezone: '🇩🇪 Timezone',
|
timezone: '🇩🇪 Timezone',
|
||||||
},
|
},
|
||||||
|
|
||||||
'n8n-nodes-base': {}, // required for node translation
|
|
||||||
},
|
|
||||||
};
|
};
|
|
@ -1,5 +1,10 @@
|
||||||
export default {
|
export default {
|
||||||
en: {
|
textEdit: {
|
||||||
|
edit: 'Edit',
|
||||||
|
},
|
||||||
|
codeEdit: {
|
||||||
|
edit: 'Edit',
|
||||||
|
},
|
||||||
versionCard: {
|
versionCard: {
|
||||||
thisVersionHasASecurityIssue: 'This version has a security issue.<br/>It is listed here for completeness.',
|
thisVersionHasASecurityIssue: 'This version has a security issue.<br/>It is listed here for completeness.',
|
||||||
released: 'Released',
|
released: 'Released',
|
||||||
|
@ -145,9 +150,10 @@ export default {
|
||||||
reconnectOAuth2Credential: 'Reconnect OAuth2 Credential',
|
reconnectOAuth2Credential: 'Reconnect OAuth2 Credential',
|
||||||
connectionTestedSuccessfully: 'Connection tested successfully',
|
connectionTestedSuccessfully: 'Connection tested successfully',
|
||||||
oAuthRedirectUrl: 'OAuth Redirect URL',
|
oAuthRedirectUrl: 'OAuth Redirect URL',
|
||||||
clickToCopy: 'ClickToCopy',
|
clickToCopy: 'Click To Copy',
|
||||||
subtitle: 'In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL',
|
subtitle: 'In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL',
|
||||||
redirectUrlCopiedToClipboard: 'Redirect URL copied to clipboard',
|
redirectUrlCopiedToClipboard: 'Redirect URL copied to clipboard',
|
||||||
|
theServiceYouReConnectingTo: "the service you're connecting to",
|
||||||
},
|
},
|
||||||
oAuthButton: {
|
oAuthButton: {
|
||||||
signInWithGoogle: 'Sign in with Google',
|
signInWithGoogle: 'Sign in with Google',
|
||||||
|
@ -398,6 +404,7 @@ export default {
|
||||||
workflows: 'Workflows',
|
workflows: 'Workflows',
|
||||||
},
|
},
|
||||||
multipleParameter: {
|
multipleParameter: {
|
||||||
|
addItem: 'Add item',
|
||||||
currentlyNoItemsExist: 'Currently no items exist',
|
currentlyNoItemsExist: 'Currently no items exist',
|
||||||
moveUp: 'Move up',
|
moveUp: 'Move up',
|
||||||
moveDown: 'Move down',
|
moveDown: 'Move down',
|
||||||
|
@ -619,6 +626,7 @@ export default {
|
||||||
withWorkflowTagsYouReFree: 'With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
withWorkflowTagsYouReFree: 'With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
||||||
},
|
},
|
||||||
parameterInput: {
|
parameterInput: {
|
||||||
|
loadingOptions: 'Loading options...',
|
||||||
addExpression: 'Add Expression',
|
addExpression: 'Add Expression',
|
||||||
removeExpression: 'Remove Expression',
|
removeExpression: 'Remove Expression',
|
||||||
refreshList: 'Refresh List',
|
refreshList: 'Refresh List',
|
||||||
|
@ -627,9 +635,15 @@ export default {
|
||||||
openEditWindow: 'Open Edit Window',
|
openEditWindow: 'Open Edit Window',
|
||||||
issues: 'Issues',
|
issues: 'Issues',
|
||||||
parameterOptions: 'Parameter Options',
|
parameterOptions: 'Parameter Options',
|
||||||
|
parameterHasIssuesAndExpression: 'Parameter: "{shortPath}" has issues and expression!',
|
||||||
|
parameterHasIssues: 'Parameter: "{shortPath}" has issues!',
|
||||||
|
parameterHasExpression: 'Parameter: "{shortPath}" has expression!',
|
||||||
|
parameter: 'Parameter: "{shortPath}"',
|
||||||
|
error: 'ERROR',
|
||||||
},
|
},
|
||||||
parameterInputExpanded: {
|
parameterInputExpanded: {
|
||||||
thisFieldIsRequired: 'This field is required.',
|
thisFieldIsRequired: 'This field is required.',
|
||||||
|
openDocs: 'Open docs',
|
||||||
},
|
},
|
||||||
parameterInputList: {
|
parameterInputList: {
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
@ -933,7 +947,4 @@ export default {
|
||||||
timeoutWorkflow: 'Timeout Workflow',
|
timeoutWorkflow: 'Timeout Workflow',
|
||||||
timezone: 'Timezone',
|
timezone: 'Timezone',
|
||||||
},
|
},
|
||||||
|
|
||||||
'n8n-nodes-base': {}, // required for node translation
|
|
||||||
},
|
|
||||||
};
|
};
|
|
@ -48,6 +48,7 @@ const state: IRootState = {
|
||||||
activeNode: null,
|
activeNode: null,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
baseUrl: process.env.VUE_APP_URL_BASE_API ? process.env.VUE_APP_URL_BASE_API : (window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH),
|
baseUrl: process.env.VUE_APP_URL_BASE_API ? process.env.VUE_APP_URL_BASE_API : (window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH),
|
||||||
|
credentialTextRenderKeys: null,
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
endpointWebhook: 'webhook',
|
endpointWebhook: 'webhook',
|
||||||
endpointWebhookTest: 'webhook-test',
|
endpointWebhookTest: 'webhook-test',
|
||||||
|
@ -559,6 +560,9 @@ export const store = new Vuex.Store({
|
||||||
setActiveNode (state, nodeName: string) {
|
setActiveNode (state, nodeName: string) {
|
||||||
state.activeNode = nodeName;
|
state.activeNode = nodeName;
|
||||||
},
|
},
|
||||||
|
setCredentialTextRenderKeys (state, renderKeys: { nodeType: string; credentialType: string; }) {
|
||||||
|
state.credentialTextRenderKeys = renderKeys;
|
||||||
|
},
|
||||||
|
|
||||||
setLastSelectedNode (state, nodeName: string) {
|
setLastSelectedNode (state, nodeName: string) {
|
||||||
state.lastSelectedNode = nodeName;
|
state.lastSelectedNode = nodeName;
|
||||||
|
@ -653,6 +657,10 @@ export const store = new Vuex.Store({
|
||||||
return state.activeExecutions;
|
return state.activeExecutions;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
credentialTextRenderKeys: (state): object | null => {
|
||||||
|
return state.credentialTextRenderKeys;
|
||||||
|
},
|
||||||
|
|
||||||
getBaseUrl: (state): string => {
|
getBaseUrl: (state): string => {
|
||||||
return state.baseUrl;
|
return state.baseUrl;
|
||||||
},
|
},
|
||||||
|
|
|
@ -170,7 +170,7 @@ import {
|
||||||
IExecutionsSummary,
|
IExecutionsSummary,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { loadLanguage } from '@/i18n';
|
import { loadLanguage, addNodeTranslation } from '@/i18n';
|
||||||
|
|
||||||
const NODE_SIZE = 100;
|
const NODE_SIZE = 100;
|
||||||
const DEFAULT_START_POSITION_X = 250;
|
const DEFAULT_START_POSITION_X = 250;
|
||||||
|
@ -2384,8 +2384,16 @@ export default mixins(
|
||||||
if (nodesToBeFetched.length > 0) {
|
if (nodesToBeFetched.length > 0) {
|
||||||
// Only call API if node information is actually missing
|
// Only call API if node information is actually missing
|
||||||
this.startLoading();
|
this.startLoading();
|
||||||
const nodeInfo = await this.restApi().getNodesInformation(nodesToBeFetched);
|
|
||||||
this.$store.commit('updateNodeTypes', nodeInfo);
|
const nodesInfo = await this.restApi().getNodesInformation(nodesToBeFetched);
|
||||||
|
|
||||||
|
nodesInfo.forEach(nodeInfo => {
|
||||||
|
if (nodeInfo.translation) {
|
||||||
|
addNodeTranslation(nodeInfo.translation, this.$store.getters.defaultLocale);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$store.commit('updateNodeTypes', nodesInfo);
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
22
packages/nodes-base/nodes/Bitwarden/translations/de.ts
Normal file
22
packages/nodes-base/nodes/Bitwarden/translations/de.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
module.exports = {
|
||||||
|
bitwarden: {
|
||||||
|
credentialsModal: {
|
||||||
|
bitwardenApi: {
|
||||||
|
environment: {
|
||||||
|
displayName: '🇩🇪 Environment',
|
||||||
|
description: '🇩🇪 Description for environment',
|
||||||
|
options: {
|
||||||
|
cloudHosted: {
|
||||||
|
displayName: '🇩🇪 Cloud-hosted',
|
||||||
|
description: '🇩🇪 Description for cloud-hosted',
|
||||||
|
},
|
||||||
|
selfHosted: {
|
||||||
|
displayName: '🇩🇪 Self-hosted',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodeView: {},
|
||||||
|
},
|
||||||
|
};
|
165
packages/nodes-base/nodes/Github/translations/de.ts
Normal file
165
packages/nodes-base/nodes/Github/translations/de.ts
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
module.exports = {
|
||||||
|
github: {
|
||||||
|
credentialsModal: {
|
||||||
|
githubOAuth2Api: {
|
||||||
|
server: {
|
||||||
|
displayName: '🇩🇪 Github Server',
|
||||||
|
description: '🇩🇪 The server to connect to. Only has to be set if Github Enterprise is used.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
githubApi: {
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodeView: {
|
||||||
|
/**
|
||||||
|
* Examples of `options` parameters.
|
||||||
|
*/
|
||||||
|
authentication: {
|
||||||
|
displayName: '🇩🇪 Authentication',
|
||||||
|
options: {
|
||||||
|
accessToken: {
|
||||||
|
displayName: '🇩🇪 Access Token',
|
||||||
|
},
|
||||||
|
oAuth2: {
|
||||||
|
displayName: '🇩🇪 OAuth2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
displayName: '🇩🇪 Resource',
|
||||||
|
description: '🇩🇪 The resource to operate on.',
|
||||||
|
options: {
|
||||||
|
issue: {
|
||||||
|
displayName: '🇩🇪 Issue',
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
displayName: '🇩🇪 File',
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
displayName: '🇩🇪 Repository',
|
||||||
|
},
|
||||||
|
release: {
|
||||||
|
displayName: '🇩🇪 Release',
|
||||||
|
},
|
||||||
|
review: {
|
||||||
|
displayName: '🇩🇪 Review',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
displayName: '🇩🇪 User',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
operation: {
|
||||||
|
displayName: '🇩🇪 Operation',
|
||||||
|
options: {
|
||||||
|
create: {
|
||||||
|
displayName: '🇩🇪 Create',
|
||||||
|
description: '🇩🇪 Create a new issue.',
|
||||||
|
},
|
||||||
|
get: {
|
||||||
|
displayName: '🇩🇪 Get',
|
||||||
|
description: '🇩🇪 Get the data of a single issue.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examples of `string` parameters.
|
||||||
|
*/
|
||||||
|
owner: {
|
||||||
|
displayName: '🇩🇪 Repository Owner',
|
||||||
|
placeholder: '🇩🇪 n8n-io',
|
||||||
|
description: '🇩🇪 Owner of the repository.',
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
displayName: '🇩🇪 Repository Name',
|
||||||
|
placeholder: '🇩🇪 n8n',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
displayName: '🇩🇪 Title',
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
displayName: '🇩🇪 Body',
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examples of `collection` parameters.
|
||||||
|
* `multipleValueButtonText` is the button label.
|
||||||
|
*/
|
||||||
|
labels: {
|
||||||
|
displayName: '🇩🇪 Labels',
|
||||||
|
multipleValueButtonText: '🇩🇪 Add Label',
|
||||||
|
},
|
||||||
|
assignees: {
|
||||||
|
displayName: '🇩🇪 Assignees',
|
||||||
|
multipleValueButtonText: '🇩🇪 Add Assignee',
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examples of fields in `collection` parameters.
|
||||||
|
* Note: Same level of nesting as `collection`.
|
||||||
|
*/
|
||||||
|
label: {
|
||||||
|
displayName: '🇩🇪 Label',
|
||||||
|
description: '🇩🇪 Label to add to issue.',
|
||||||
|
},
|
||||||
|
assignee: {
|
||||||
|
displayName: '🇩🇪 Assignee',
|
||||||
|
description: '🇩🇪 User to assign issue to.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example of a `fixedCollection` parameter.
|
||||||
|
* `placeholder` is the button label.
|
||||||
|
*/
|
||||||
|
additionalParameters: {
|
||||||
|
displayName: '🇩🇪 Additional Fields',
|
||||||
|
placeholder: '🇩🇪 Add Field',
|
||||||
|
options: {
|
||||||
|
author: {
|
||||||
|
displayName: '🇩🇪 Author',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
displayName: '🇩🇪 Branch',
|
||||||
|
},
|
||||||
|
committer: {
|
||||||
|
displayName: '🇩🇪 Committer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example of a field in a `fixedCollection` parameter.
|
||||||
|
* Note: Same level of nesting as `fixedCollection`.
|
||||||
|
*/
|
||||||
|
committer: {
|
||||||
|
displayName: '🇩🇪 Commit-Macher',
|
||||||
|
description: '🇩🇪 Beschreibung',
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examples of options in a field in a `fixedCollection` parameter.
|
||||||
|
* Note: Same level of nesting as `fixedCollection`.
|
||||||
|
*/
|
||||||
|
name: {
|
||||||
|
displayName: '🇩🇪 Name',
|
||||||
|
description: '🇩🇪 The name of the author of the commit.',
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
displayName: '🇩🇪 Email',
|
||||||
|
description: '🇩🇪 The email of the author of the commit.'
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -817,6 +817,7 @@ export interface INodeTypeDescription extends INodeTypeBaseDescription {
|
||||||
deactivate?: INodeHookDescription[];
|
deactivate?: INodeHookDescription[];
|
||||||
};
|
};
|
||||||
webhooks?: IWebhookDescription[];
|
webhooks?: IWebhookDescription[];
|
||||||
|
translation?: { [key: string]: object };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeHookDescription {
|
export interface INodeHookDescription {
|
||||||
|
|
|
@ -1390,7 +1390,7 @@ export function mergeNodeProperties(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVersionedTypeNode(
|
export function getVersionedNodeType(
|
||||||
object: INodeVersionedType | INodeType,
|
object: INodeVersionedType | INodeType,
|
||||||
version?: number,
|
version?: number,
|
||||||
): INodeType {
|
): INodeType {
|
||||||
|
@ -1400,7 +1400,7 @@ export function getVersionedTypeNode(
|
||||||
return object as INodeType;
|
return object as INodeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVersionedTypeNodeAll(object: INodeVersionedType | INodeType): INodeType[] {
|
export function getVersionedNodeTypeAll(object: INodeVersionedType | INodeType): INodeType[] {
|
||||||
if (isNodeTypeVersioned(object)) {
|
if (isNodeTypeVersioned(object)) {
|
||||||
return Object.values((object as INodeVersionedType).nodeVersions).map((element) => {
|
return Object.values((object as INodeVersionedType).nodeVersions).map((element) => {
|
||||||
element.description.name = object.description.name;
|
element.description.name = object.description.name;
|
||||||
|
|
|
@ -99,7 +99,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
async init(nodeTypes: INodeTypeData): Promise<void> {}
|
async init(nodeTypes: INodeTypeData): Promise<void> {}
|
||||||
|
|
||||||
getAll(): INodeType[] {
|
getAll(): INodeType[] {
|
||||||
return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedTypeNode(data.type));
|
return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedNodeType(data.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(nodeType: string): INodeType {
|
getByName(nodeType: string): INodeType {
|
||||||
|
@ -107,7 +107,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||||
return NodeHelpers.getVersionedTypeNode(this.nodeTypes[nodeType].type, version);
|
return NodeHelpers.getVersionedNodeType(this.nodeTypes[nodeType].type, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue