mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
✨ Render header strings
This commit is contained in:
parent
f1eef04ad2
commit
99963b04a5
|
@ -1207,6 +1207,22 @@ class App {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Returns node information based on node names and versions
|
||||||
|
this.app.get(
|
||||||
|
`/${this.restEndpoint}/node-translation-headers`,
|
||||||
|
ResponseHelper.send(
|
||||||
|
async (req: express.Request, res: express.Response): Promise<object | void> => {
|
||||||
|
const packagesPath = pathJoin(__dirname, '..', '..', '..');
|
||||||
|
const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers');
|
||||||
|
try {
|
||||||
|
return require(headersPath);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send('Failed to find headers file');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Node-Types
|
// Node-Types
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.1",
|
||||||
"@types/jest": "^26.0.13",
|
"@types/jest": "^26.0.13",
|
||||||
|
"@types/lodash.camelcase": "^4.3.6",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/lodash.set": "^4.3.6",
|
"@types/lodash.set": "^4.3.6",
|
||||||
"@types/node": "14.17.27",
|
"@types/node": "14.17.27",
|
||||||
|
@ -69,15 +70,16 @@
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.4.1",
|
||||||
"jshint": "^2.9.7",
|
"jshint": "^2.9.7",
|
||||||
"jsplumb": "2.15.4",
|
"jsplumb": "2.15.4",
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"n8n-workflow": "~0.76.0",
|
"n8n-workflow": "~0.76.0",
|
||||||
"sass": "^1.26.5",
|
|
||||||
"normalize-wheel": "^1.0.1",
|
"normalize-wheel": "^1.0.1",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
"quill": "^2.0.0-dev.3",
|
"quill": "^2.0.0-dev.3",
|
||||||
"quill-autoformat": "^0.1.1",
|
"quill-autoformat": "^0.1.1",
|
||||||
|
"sass": "^1.26.5",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"string-template-parser": "^1.2.6",
|
"string-template-parser": "^1.2.6",
|
||||||
"ts-jest": "^26.3.0",
|
"ts-jest": "^26.3.0",
|
||||||
|
|
|
@ -130,6 +130,7 @@ export interface IRestApi {
|
||||||
getPastExecutions(filter: object, limit: number, lastId?: string | number, firstId?: string | number): Promise<IExecutionsListResponse>;
|
getPastExecutions(filter: object, limit: number, lastId?: string | number, firstId?: string | number): Promise<IExecutionsListResponse>;
|
||||||
stopCurrentExecution(executionId: string): Promise<IExecutionsStopData>;
|
stopCurrentExecution(executionId: string): Promise<IExecutionsStopData>;
|
||||||
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
||||||
|
getNodeTranslationHeaders(): Promise<INodeTranslationHeaders>;
|
||||||
getNodeTypes(onlyLatest?: boolean): Promise<INodeTypeDescription[]>;
|
getNodeTypes(onlyLatest?: boolean): Promise<INodeTypeDescription[]>;
|
||||||
getNodesInformation(nodeInfos: INodeTypeNameVersion[]): Promise<INodeTypeDescription[]>;
|
getNodesInformation(nodeInfos: INodeTypeNameVersion[]): Promise<INodeTypeDescription[]>;
|
||||||
getNodeParameterOptions(nodeTypeAndVersion: INodeTypeNameVersion, path: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
getNodeParameterOptions(nodeTypeAndVersion: INodeTypeNameVersion, path: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
||||||
|
@ -147,6 +148,15 @@ export interface IRestApi {
|
||||||
getTimezones(): Promise<IDataObject>;
|
getTimezones(): Promise<IDataObject>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface INodeTranslationHeaders {
|
||||||
|
data: {
|
||||||
|
[key: string]: {
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface IBinaryDisplayData {
|
export interface IBinaryDisplayData {
|
||||||
index: number;
|
index: number;
|
||||||
key: string;
|
key: string;
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8" :class="$style.accessLabel">
|
<el-col :span="8" :class="$style.accessLabel">
|
||||||
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.allowUseBy') }}</n8n-text>
|
<n8n-text :compact="true" :bold="true">
|
||||||
|
{{ $baseText('credentialEdit.credentialInfo.allowUseBy') }}
|
||||||
|
</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
<div
|
<div
|
||||||
|
@ -11,7 +13,10 @@
|
||||||
:class="$style.valueLabel"
|
:class="$style.valueLabel"
|
||||||
>
|
>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
:label="node.displayName"
|
:label="$headerText({
|
||||||
|
key: `headers.${shortNodeType(node)}.displayName`,
|
||||||
|
fallback: node.displayName,
|
||||||
|
})"
|
||||||
:value="!!nodeAccess[node.name]"
|
:value="!!nodeAccess[node.name]"
|
||||||
@change="(val) => onNodeAccessChange(node.name, val)"
|
@change="(val) => onNodeAccessChange(node.name, val)"
|
||||||
/>
|
/>
|
||||||
|
@ -20,7 +25,9 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.created') }}</n8n-text>
|
<n8n-text :compact="true" :bold="true">
|
||||||
|
{{ $baseText('credentialEdit.credentialInfo.created') }}
|
||||||
|
</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"><TimeAgo :date="currentCredential.createdAt" :capitalize="true" /></n8n-text>
|
<n8n-text :compact="true"><TimeAgo :date="currentCredential.createdAt" :capitalize="true" /></n8n-text>
|
||||||
|
@ -28,7 +35,9 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.lastModified') }}</n8n-text>
|
<n8n-text :compact="true" :bold="true">
|
||||||
|
{{ $baseText('credentialEdit.credentialInfo.lastModified') }}
|
||||||
|
</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"><TimeAgo :date="currentCredential.updatedAt" :capitalize="true" /></n8n-text>
|
<n8n-text :compact="true"><TimeAgo :date="currentCredential.updatedAt" :capitalize="true" /></n8n-text>
|
||||||
|
@ -36,10 +45,12 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.id') }}</n8n-text>
|
<n8n-text :compact="true" :bold="true">
|
||||||
|
{{ $baseText('credentialEdit.credentialInfo.id') }}
|
||||||
|
</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true">{{currentCredential.id}}</n8n-text>
|
<n8n-text :compact="true">{{ currentCredential.id }}</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,6 +62,7 @@ import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
import TimeAgo from '../TimeAgo.vue';
|
import TimeAgo from '../TimeAgo.vue';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { INodeTypeDescription } from 'n8n-workflow';
|
||||||
|
|
||||||
export default mixins(renderText).extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'CredentialInfo',
|
name: 'CredentialInfo',
|
||||||
|
@ -65,6 +77,9 @@ export default mixins(renderText).extend({
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
shortNodeType(nodeType: INodeTypeDescription) {
|
||||||
|
return nodeType.name.replace('n8n-nodes-base.', '');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<template >
|
<template>
|
||||||
<span class="static-text-wrapper">
|
<span class="static-text-wrapper">
|
||||||
<span v-show="!editActive" :title="$baseText('displayWithChange.clickToChange')">
|
<span v-show="!editActive" :title="$baseText('displayWithChange.clickToChange')">
|
||||||
<span class="static-text" @mousedown="startEdit">{{currentValue}}</span>
|
<span class="static-text" @mousedown="startEdit">{{currentValue}}</span>
|
||||||
|
@ -33,6 +33,15 @@ export default mixins(genericHelpers).extend({
|
||||||
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
|
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.keyName === 'name' && this.node.type.startsWith('n8n-nodes-base.')) {
|
||||||
|
const shortNodeType = this.node.type.replace('n8n-nodes-base.', '');
|
||||||
|
|
||||||
|
return this.$headerText({
|
||||||
|
key: `headers.${shortNodeType}.displayName`,
|
||||||
|
fallback: getDescendantProp(this.node, this.keyName),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return getDescendantProp(this.node, this.keyName);
|
return getDescendantProp(this.node, this.keyName);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -114,7 +114,12 @@
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="mode" :label="$baseText('executionsList.mode')" width="100" align="center"></el-table-column>
|
<el-table-column property="mode" :label="$baseText('executionsList.mode')" width="100" align="center">
|
||||||
|
<!-- TODO i18n <template slot-scope="scope">
|
||||||
|
{{convertToDisplayDate(scope.row.startedAt)}}<br />
|
||||||
|
<small v-if="scope.row.id">ID: {{scope.row.id}}</small>
|
||||||
|
</template> -->
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$baseText('executionsList.runningTime')" width="150" align="center">
|
<el-table-column :label="$baseText('executionsList.runningTime')" width="150" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.stoppedAt === undefined">
|
<span v-if="scope.row.stoppedAt === undefined">
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
<template functional>
|
<template>
|
||||||
<div :class="$style.category">
|
<div :class="$style.category">
|
||||||
<span :class="$style.name">{{ props.item.category }}</span>
|
<span :class="$style.name">
|
||||||
|
{{ $baseText(`nodeCreator.categoryNames.${categoryName}`) }}
|
||||||
|
</span>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:class="$style.arrow"
|
:class="$style.arrow"
|
||||||
icon="chevron-down"
|
icon="chevron-down"
|
||||||
v-if="props.item.properties.expanded"
|
v-if="item.properties.expanded"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon :class="$style.arrow" icon="chevron-up" v-else />
|
<font-awesome-icon :class="$style.arrow" icon="chevron-up" v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
import Vue from 'vue';
|
||||||
|
import camelcase from 'lodash.camelcase';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
props: ['item'],
|
props: ['item'],
|
||||||
};
|
computed: {
|
||||||
|
categoryName() {
|
||||||
|
return camelcase(this.item.category);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
/>
|
/>
|
||||||
<div class="type-selector">
|
<div class="type-selector">
|
||||||
<el-tabs v-model="selectedType" stretch>
|
<el-tabs v-model="selectedType" stretch>
|
||||||
<el-tab-pane label="All" :name="ALL_NODE_FILTER"></el-tab-pane>
|
<el-tab-pane :label="$baseText('nodeCreator.mainPanel.all')" :name="ALL_NODE_FILTER"></el-tab-pane>
|
||||||
<el-tab-pane label="Regular" :name="REGULAR_NODE_FILTER"></el-tab-pane>
|
<el-tab-pane :label="$baseText('nodeCreator.mainPanel.regular')" :name="REGULAR_NODE_FILTER"></el-tab-pane>
|
||||||
<el-tab-pane label="Trigger" :name="TRIGGER_NODE_FILTER"></el-tab-pane>
|
<el-tab-pane :label="$baseText('nodeCreator.mainPanel.trigger')" :name="TRIGGER_NODE_FILTER"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="searchFilter.length === 0" class="scrollable">
|
<div v-if="searchFilter.length === 0" class="scrollable">
|
||||||
|
@ -55,9 +55,10 @@ import { INodeCreateElement, INodeItemProps, ISubcategoryItemProps } from '@/Int
|
||||||
import { ALL_NODE_FILTER, CORE_NODES_CATEGORY, REGULAR_NODE_FILTER, TRIGGER_NODE_FILTER } from '@/constants';
|
import { ALL_NODE_FILTER, CORE_NODES_CATEGORY, REGULAR_NODE_FILTER, TRIGGER_NODE_FILTER } from '@/constants';
|
||||||
import SlideTransition from '../transitions/SlideTransition.vue';
|
import SlideTransition from '../transitions/SlideTransition.vue';
|
||||||
import { matchesNodeType, matchesSelectType } from './helpers';
|
import { matchesNodeType, matchesSelectType } from './helpers';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
|
|
||||||
export default mixins(externalHooks).extend({
|
export default mixins(externalHooks, renderText).extend({
|
||||||
name: 'NodeCreateList',
|
name: 'NodeCreateList',
|
||||||
components: {
|
components: {
|
||||||
ItemIterator,
|
ItemIterator,
|
||||||
|
|
|
@ -4,27 +4,31 @@
|
||||||
<NoResultsIcon />
|
<NoResultsIcon />
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div>We didn't make that... yet</div>
|
<div>
|
||||||
|
{{ $baseText('nodeCreator.noResults.weDidntMakeThatYet') }}
|
||||||
|
</div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
Don’t worry, you can probably do it with the
|
{{ $baseText('nodeCreator.noResults.dontWorryYouCanProbablyDoItWithThe') }}
|
||||||
<a @click="selectHttpRequest">HTTP Request</a> or
|
<a @click="selectHttpRequest">{{ $baseText('nodeCreator.noResults.httpRequest') }}</a> or
|
||||||
<a @click="selectWebhook">Webhook</a> node
|
<a @click="selectWebhook">{{ $baseText('nodeCreator.noResults.webhook') }}</a> {{ $baseText('nodeCreator.noResults.node') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="request">
|
<div class="request">
|
||||||
<div>Want us to make it faster?</div>
|
<div>
|
||||||
|
{{ $baseText('nodeCreator.noResults.wantUsToMakeItFaster') }}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
:href="REQUEST_NODE_FORM_URL"
|
:href="REQUEST_NODE_FORM_URL"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<span>Request the node</span>
|
<span>{{ $baseText('nodeCreator.noResults.requestTheNode') }}</span>
|
||||||
<span>
|
<span>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
class="external"
|
class="external"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
title="Request the node"
|
:title="$baseText('nodeCreator.noResults.requestTheNode')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -37,10 +41,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { HTTP_REQUEST_NODE_TYPE, REQUEST_NODE_FORM_URL, WEBHOOK_NODE_TYPE } from '@/constants';
|
import { HTTP_REQUEST_NODE_TYPE, REQUEST_NODE_FORM_URL, WEBHOOK_NODE_TYPE } from '@/constants';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
import NoResultsIcon from './NoResultsIcon.vue';
|
import NoResultsIcon from './NoResultsIcon.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'NoResults',
|
name: 'NoResults',
|
||||||
components: {
|
components: {
|
||||||
NoResultsIcon,
|
NoResultsIcon,
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
<template functional>
|
<template>
|
||||||
<div :class="{[$style['node-item']]: true, [$style.bordered]: props.bordered}">
|
<div :class="{[$style['node-item']]: true, [$style.bordered]: bordered}">
|
||||||
<NodeIcon :class="$style['node-icon']" :nodeType="props.nodeType" />
|
<NodeIcon :class="$style['node-icon']" :nodeType="nodeType" />
|
||||||
<div>
|
<div>
|
||||||
<div :class="$style.details">
|
<div :class="$style.details">
|
||||||
<span :class="$style.name">{{props.nodeType.displayName}}</span>
|
<span :class="$style.name">
|
||||||
|
{{ $headerText({
|
||||||
|
key: `headers.${shortNodeType}.displayName`,
|
||||||
|
fallback: nodeType.displayName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
<span :class="$style['trigger-icon']">
|
<span :class="$style['trigger-icon']">
|
||||||
<TriggerIcon v-if="$options.isTrigger(props.nodeType)" />
|
<TriggerIcon v-if="$options.isTrigger(nodeType)" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.description">
|
<div :class="$style.description">
|
||||||
{{props.nodeType.description}}
|
{{ $headerText({
|
||||||
|
key: `headers.${shortNodeType}.description`,
|
||||||
|
fallback: nodeType.description,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,20 +33,30 @@ import { INodeTypeDescription } from 'n8n-workflow';
|
||||||
import NodeIcon from '../NodeIcon.vue';
|
import NodeIcon from '../NodeIcon.vue';
|
||||||
import TriggerIcon from '../TriggerIcon.vue';
|
import TriggerIcon from '../TriggerIcon.vue';
|
||||||
|
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
Vue.component('NodeIcon', NodeIcon);
|
Vue.component('NodeIcon', NodeIcon);
|
||||||
Vue.component('TriggerIcon', TriggerIcon);
|
Vue.component('TriggerIcon', TriggerIcon);
|
||||||
|
|
||||||
export default {
|
export default mixins(renderText).extend({
|
||||||
|
name: 'NodeItem',
|
||||||
props: [
|
props: [
|
||||||
'active',
|
'active',
|
||||||
'filter',
|
'filter',
|
||||||
'nodeType',
|
'nodeType',
|
||||||
'bordered',
|
'bordered',
|
||||||
],
|
],
|
||||||
|
computed: {
|
||||||
|
shortNodeType() {
|
||||||
|
return this.nodeType.name.replace('n8n-nodes-base.', '');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
isTrigger (nodeType: INodeTypeDescription): boolean {
|
isTrigger (nodeType: INodeTypeDescription): boolean {
|
||||||
return nodeType.group.includes('trigger');
|
return nodeType.group.includes('trigger');
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<input
|
<input
|
||||||
placeholder="Search nodes..."
|
:placeholder="$baseText('nodeCreator.searchBar.searchNodes')"
|
||||||
ref="input"
|
ref="input"
|
||||||
:value="value"
|
:value="value"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
|
@ -22,8 +22,9 @@
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import { externalHooks } from '@/components/mixins/externalHooks';
|
import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
export default mixins(externalHooks).extend({
|
export default mixins(externalHooks, renderText).extend({
|
||||||
name: "SearchBar",
|
name: "SearchBar",
|
||||||
props: ["value", "eventBus"],
|
props: ["value", "eventBus"],
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<template functional>
|
<template>
|
||||||
<div :class="$style.subcategory">
|
<div :class="$style.subcategory">
|
||||||
<div :class="$style.details">
|
<div :class="$style.details">
|
||||||
<div :class="$style.title">{{ props.item.properties.subcategory }}</div>
|
<div :class="$style.title">
|
||||||
<div v-if="props.item.properties.description" :class="$style.description">
|
{{ $baseText(`nodeCreator.subcategoryNames.${subcategoryName}`) }}
|
||||||
{{ props.item.properties.description }}
|
</div>
|
||||||
|
<div v-if="item.properties.description" :class="$style.description">
|
||||||
|
{{ $baseText(`nodeCreator.subcategoryDescriptions.${subcategoryDescription}`) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.action">
|
<div :class="$style.action">
|
||||||
|
@ -13,9 +15,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
import camelcase from 'lodash.camelcase';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
props: ['item'],
|
props: ['item'],
|
||||||
};
|
computed: {
|
||||||
|
subcategoryName() {
|
||||||
|
return camelcase(this.item.properties.subcategory);
|
||||||
|
},
|
||||||
|
subcategoryDescription() {
|
||||||
|
const firstWord = this.item.properties.description.split(' ').shift() || '';
|
||||||
|
return firstWord.toLowerCase().replace(/,/g, '');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
<div class="clickable" @click="onBackArrowClick">
|
<div class="clickable" @click="onBackArrowClick">
|
||||||
<font-awesome-icon class="back-arrow" icon="arrow-left" />
|
<font-awesome-icon class="back-arrow" icon="arrow-left" />
|
||||||
</div>
|
</div>
|
||||||
<span>{{ title }}</span>
|
<span>
|
||||||
|
{{ $baseText(`nodeCreator.subcategoryNames.${subcategoryName}`) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="scrollable">
|
<div class="scrollable">
|
||||||
|
@ -18,17 +20,26 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import camelcase from 'lodash.camelcase';
|
||||||
import { INodeCreateElement } from '@/Interface';
|
import { INodeCreateElement } from '@/Interface';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
import ItemIterator from './ItemIterator.vue';
|
import ItemIterator from './ItemIterator.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
name: 'SubcategoryPanel',
|
name: 'SubcategoryPanel',
|
||||||
components: {
|
components: {
|
||||||
ItemIterator,
|
ItemIterator,
|
||||||
},
|
},
|
||||||
props: ['title', 'elements', 'activeIndex'],
|
props: ['title', 'elements', 'activeIndex'],
|
||||||
|
computed: {
|
||||||
|
subcategoryName() {
|
||||||
|
return camelcase(this.title);
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
selected(element: INodeCreateElement) {
|
selected(element: INodeCreateElement) {
|
||||||
this.$emit('selected', element);
|
this.$emit('selected', element);
|
||||||
|
|
|
@ -95,9 +95,26 @@ export default mixins(
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
nodeTypeName(): string {
|
||||||
|
if (this.nodeType) {
|
||||||
|
const shortNodeType = this.nodeType.name.replace('n8n-nodes-base.', '');
|
||||||
|
|
||||||
|
return this.$headerText({
|
||||||
|
key: `headers.${shortNodeType}.displayName`,
|
||||||
|
fallback: this.nodeType.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
nodeTypeDescription (): string {
|
nodeTypeDescription (): string {
|
||||||
if (this.nodeType && this.nodeType.description) {
|
if (this.nodeType && this.nodeType.description) {
|
||||||
return this.nodeType.description;
|
const shortNodeType = this.nodeType.name.replace('n8n-nodes-base.', '');
|
||||||
|
|
||||||
|
return this.$headerText({
|
||||||
|
key: `headers.${shortNodeType}.description`,
|
||||||
|
fallback: this.nodeType.description,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return this.$baseText('nodeSettings.noDescriptionFound');
|
return this.$baseText('nodeSettings.noDescriptionFound');
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,19 @@ export const renderText = Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* Render a string of dynamic header text, used in the nodes panel and in the node view.
|
||||||
|
*/
|
||||||
|
$headerText(arg: { key: string; fallback: string; }) {
|
||||||
|
return this.__render(arg);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a string of dynamic text, i.e. a string with a constructed path to the localized value in the node text object - in the credentials modal (`$credText`), in the node view (`$nodeText`), or in the headers (`$headerText`) in the nodes panel and node view. _Private method_, to be called only from within this mixin.
|
||||||
|
*
|
||||||
|
* Unlike in `$baseText`, the fallback has to be set manually for dynamic text.
|
||||||
*/
|
*/
|
||||||
__render(
|
__render(
|
||||||
{ key, fallback }: { key: string, fallback: string },
|
{ key, fallback }: { key: string; fallback: string; },
|
||||||
) {
|
) {
|
||||||
return this.$te(key) ? this.$t(key).toString() : fallback;
|
return this.$te(key) ? this.$t(key).toString() : fallback;
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
IWorkflowShortResponse,
|
IWorkflowShortResponse,
|
||||||
IRestApi,
|
IRestApi,
|
||||||
IWorkflowDataUpdate,
|
IWorkflowDataUpdate,
|
||||||
|
INodeTranslationHeaders,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
@ -78,6 +79,10 @@ export const restApi = Vue.extend({
|
||||||
return self.restApi().makeRestApiRequest('POST', `/executions-current/${executionId}/stop`);
|
return self.restApi().makeRestApiRequest('POST', `/executions-current/${executionId}/stop`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getNodeTranslationHeaders: (): Promise<INodeTranslationHeaders> => {
|
||||||
|
return self.restApi().makeRestApiRequest('GET', '/node-translation-headers');
|
||||||
|
},
|
||||||
|
|
||||||
// Returns all node-types
|
// Returns all node-types
|
||||||
getNodeTypes: (onlyLatest = false): Promise<INodeTypeDescription[]> => {
|
getNodeTypes: (onlyLatest = false): Promise<INodeTypeDescription[]> => {
|
||||||
return self.restApi().makeRestApiRequest('GET', `/node-types`, {onlyLatest});
|
return self.restApi().makeRestApiRequest('GET', `/node-types`, {onlyLatest});
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const CUSTOM_NODES_CATEGORY = 'Custom Nodes';
|
||||||
export const SUBCATEGORY_DESCRIPTIONS: {
|
export const SUBCATEGORY_DESCRIPTIONS: {
|
||||||
[category: string]: { [subcategory: string]: string };
|
[category: string]: { [subcategory: string]: string };
|
||||||
} = {
|
} = {
|
||||||
'Core Nodes': {
|
'Core Nodes': { // this - all subkeys are set from codex
|
||||||
Flow: 'Branches, core triggers, merge data',
|
Flow: 'Branches, core triggers, merge data',
|
||||||
Files: 'Work with CSV, XML, text, images etc.',
|
Files: 'Work with CSV, XML, text, images etc.',
|
||||||
'Data Transformation': 'Manipulate data fields, run code',
|
'Data Transformation': 'Manipulate data fields, run code',
|
||||||
|
|
|
@ -2,7 +2,7 @@ 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';
|
import { INodeTranslationHeaders } from '@/Interface';
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ function setLanguage(language: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadLanguage(language?: string) {
|
export async function loadLanguage(language?: string) {
|
||||||
|
// TODO i18n: Remove next line
|
||||||
console.log(`loadLanguage called with ${language}`); // eslint-disable-line no-console
|
console.log(`loadLanguage called with ${language}`); // eslint-disable-line no-console
|
||||||
|
|
||||||
if (!language) return Promise.resolve();
|
if (!language) return Promise.resolve();
|
||||||
|
@ -39,11 +40,11 @@ export async function loadLanguage(language?: string) {
|
||||||
return Promise.resolve(setLanguage(language));
|
return Promise.resolve(setLanguage(language));
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseText = require(`./locales/${language}`).default; // TODO i18n: `path.join()`
|
const baseText = require(`./locales/${language}`).default;
|
||||||
i18n.setLocaleMessage(language, baseText);
|
i18n.setLocaleMessage(language, baseText);
|
||||||
loadedLanguages.push(language);
|
loadedLanguages.push(language);
|
||||||
|
|
||||||
return setLanguage(language);
|
setLanguage(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addNodeTranslation(
|
export function addNodeTranslation(
|
||||||
|
@ -65,3 +66,16 @@ export function addNodeTranslation(
|
||||||
Object.assign(i18n.messages[language], newNodesBase),
|
Object.assign(i18n.messages[language], newNodesBase),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addHeaders(
|
||||||
|
headers: INodeTranslationHeaders,
|
||||||
|
language: string,
|
||||||
|
) {
|
||||||
|
i18n.setLocaleMessage(
|
||||||
|
language,
|
||||||
|
Object.assign(i18n.messages[language], { headers }),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO i18n: Remove next line
|
||||||
|
console.log(i18n.messages.de.headers); // eslint-disable-line no-console
|
||||||
|
}
|
|
@ -5,6 +5,52 @@ export default {
|
||||||
clientSecret: '🇩🇪 Client Secret',
|
clientSecret: '🇩🇪 Client Secret',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
nodeCreator: {
|
||||||
|
categoryNames: {
|
||||||
|
coreNodes: '🇩🇪 Core Nodes',
|
||||||
|
customNodes: '🇩🇪 Custom Nodes',
|
||||||
|
suggestedNodes: '🇩🇪 Suggested Nodes ✨',
|
||||||
|
analytics: '🇩🇪 Analytics',
|
||||||
|
communication: '🇩🇪 Communication',
|
||||||
|
dataStorage: '🇩🇪 Data & Storage',
|
||||||
|
development: '🇩🇪 Development',
|
||||||
|
financeAccounting: '🇩🇪 Finance & Accounting',
|
||||||
|
marketingContent: '🇩🇪 Marketing & Content',
|
||||||
|
productivity: '🇩🇪 Productivity',
|
||||||
|
sales: '🇩🇪 Sales',
|
||||||
|
utility: '🇩🇪 Utility',
|
||||||
|
miscellaneous: '🇩🇪 Miscellaneous',
|
||||||
|
},
|
||||||
|
subcategoryNames: {
|
||||||
|
dataTransformation: '🇩🇪 Data Transformation',
|
||||||
|
flow: '🇩🇪 Flow',
|
||||||
|
files: '🇩🇪 Files',
|
||||||
|
helpers: '🇩🇪 Helpers',
|
||||||
|
},
|
||||||
|
subcategoryDescriptions: {
|
||||||
|
manipulate: '🇩🇪 Manipulate data fields, run code',
|
||||||
|
branches: '🇩🇪 Branches, core triggers, merge data',
|
||||||
|
work: '🇩🇪 Work with CSV, XML, text, images etc.',
|
||||||
|
http: '🇩🇪 HTTP Requests (API calls), date and time, scrape HTML',
|
||||||
|
},
|
||||||
|
mainPanel: {
|
||||||
|
all: '🇩🇪 All',
|
||||||
|
regular: '🇩🇪 Regular',
|
||||||
|
trigger: '🇩🇪 Trigger',
|
||||||
|
},
|
||||||
|
searchBar: {
|
||||||
|
searchNodes: '🇩🇪 Search nodes...',
|
||||||
|
},
|
||||||
|
noResults: {
|
||||||
|
weDidntMakeThatYet: "🇩🇪 We didn't make that... yet",
|
||||||
|
dontWorryYouCanProbablyDoItWithThe: '🇩🇪 Don’t worry, you can probably do it with the {httpRequest} or {webhook} node',
|
||||||
|
httpRequest: '🇩🇪 HTTP Request',
|
||||||
|
webhook: '🇩🇪 Webhook',
|
||||||
|
node: '🇩🇪 node',
|
||||||
|
wantUsToMakeItFaster: '🇩🇪 Want us to make it faster?',
|
||||||
|
requestTheNode: '🇩🇪 Request the node',
|
||||||
|
},
|
||||||
|
},
|
||||||
textEdit: {
|
textEdit: {
|
||||||
edit: '🇩🇪 Edit',
|
edit: '🇩🇪 Edit',
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,50 @@
|
||||||
export default {
|
export default {
|
||||||
|
nodeCreator: {
|
||||||
|
categoryNames: {
|
||||||
|
coreNodes: 'Core Nodes',
|
||||||
|
customNodes: 'Custom Nodes',
|
||||||
|
suggestedNodes: 'Suggested Nodes ✨',
|
||||||
|
analytics: 'Analytics',
|
||||||
|
communication: 'Communication',
|
||||||
|
dataStorage: 'Data & Storage',
|
||||||
|
development: 'Development',
|
||||||
|
financeAccounting: 'Finance & Accounting',
|
||||||
|
marketingContent: 'Marketing & Content',
|
||||||
|
productivity: 'Productivity',
|
||||||
|
sales: 'Sales',
|
||||||
|
utility: 'Utility',
|
||||||
|
miscellaneous: 'Miscellaneous',
|
||||||
|
},
|
||||||
|
subcategoryNames: {
|
||||||
|
dataTransformation: 'Data Transformation',
|
||||||
|
flow: 'Flow',
|
||||||
|
files: 'Files',
|
||||||
|
helpers: 'Helpers',
|
||||||
|
},
|
||||||
|
subcategoryDescriptions: {
|
||||||
|
manipulate: 'Manipulate data fields, run code',
|
||||||
|
branches: 'Branches, core triggers, merge data',
|
||||||
|
work: 'Work with CSV, XML, text, images etc.',
|
||||||
|
http: 'HTTP Requests (API calls), date and time, scrape HTML',
|
||||||
|
},
|
||||||
|
mainPanel: {
|
||||||
|
all: 'All',
|
||||||
|
regular: 'Regular',
|
||||||
|
trigger: 'Trigger',
|
||||||
|
},
|
||||||
|
searchBar: {
|
||||||
|
searchNodes: 'Search nodes...',
|
||||||
|
},
|
||||||
|
noResults: {
|
||||||
|
weDidntMakeThatYet: "We didn't make that... yet",
|
||||||
|
dontWorryYouCanProbablyDoItWithThe: 'Don’t worry, you can probably do it with the {httpRequest} or {webhook} node',
|
||||||
|
httpRequest: 'HTTP Request',
|
||||||
|
webhook: 'Webhook',
|
||||||
|
node: 'node',
|
||||||
|
wantUsToMakeItFaster: 'Want us to make it faster?',
|
||||||
|
requestTheNode: 'Request the node',
|
||||||
|
},
|
||||||
|
},
|
||||||
textEdit: {
|
textEdit: {
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
},
|
},
|
||||||
|
|
|
@ -170,7 +170,11 @@ import {
|
||||||
IExecutionsSummary,
|
IExecutionsSummary,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { loadLanguage, addNodeTranslation } from '@/i18n';
|
import {
|
||||||
|
loadLanguage,
|
||||||
|
addNodeTranslation,
|
||||||
|
addHeaders,
|
||||||
|
} from '@/i18n';
|
||||||
|
|
||||||
const NODE_SIZE = 100;
|
const NODE_SIZE = 100;
|
||||||
const DEFAULT_START_POSITION_X = 250;
|
const DEFAULT_START_POSITION_X = 250;
|
||||||
|
@ -245,9 +249,13 @@ export default mixins(
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultLocale (newLocale, oldLocale) {
|
async defaultLocale (newLocale, oldLocale) {
|
||||||
|
// TODO i18n: Remove next line
|
||||||
console.log(`Switching locale from ${oldLocale} to ${newLocale}`); // eslint-disable-line no-console
|
console.log(`Switching locale from ${oldLocale} to ${newLocale}`); // eslint-disable-line no-console
|
||||||
loadLanguage(newLocale);
|
loadLanguage(newLocale);
|
||||||
|
|
||||||
|
const headers = await this.restApi().getNodeTranslationHeaders();
|
||||||
|
addHeaders(headers, this.$store.getters.defaultLocale);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async beforeRouteLeave(to, from, next) {
|
async beforeRouteLeave(to, from, next) {
|
||||||
|
|
|
@ -1,11 +1,141 @@
|
||||||
const { src, dest } = require('gulp');
|
const { existsSync, promises: { writeFile } } = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { task, src, dest } = require('gulp');
|
||||||
|
|
||||||
|
const ALLOWED_HEADER_KEYS = ['displayName', 'description'];
|
||||||
|
const PURPLE_ANSI_COLOR_CODE = 35;
|
||||||
|
|
||||||
|
task('build:icons', copyIcons);
|
||||||
|
|
||||||
function copyIcons() {
|
function copyIcons() {
|
||||||
src('nodes/**/*.{png,svg}')
|
src('nodes/**/*.{png,svg}').pipe(dest('dist/nodes'))
|
||||||
.pipe(dest('dist/nodes'))
|
|
||||||
|
|
||||||
return src('credentials/**/*.{png,svg}')
|
return src('credentials/**/*.{png,svg}').pipe(dest('dist/credentials'));
|
||||||
.pipe(dest('dist/credentials'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.default = copyIcons;
|
task('build:translations', writeHeadersAndTranslations);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write all node translation headers at `/dist/nodes/headers.js` and write
|
||||||
|
* each node translation at `/dist/nodes/<node>/translations/<language>.js`
|
||||||
|
*/
|
||||||
|
function writeHeadersAndTranslations(done) {
|
||||||
|
checkLocale();
|
||||||
|
|
||||||
|
const paths = getTranslationPaths();
|
||||||
|
const { headers, translations } = getHeadersAndTranslations(paths);
|
||||||
|
|
||||||
|
const headersDestinationPath = path.join(__dirname, 'dist', 'nodes', 'headers.js');
|
||||||
|
|
||||||
|
writeDestinationFile(headersDestinationPath, headers);
|
||||||
|
|
||||||
|
log('Headers translation file written to:');
|
||||||
|
log(headersDestinationPath, { bulletpoint: true });
|
||||||
|
|
||||||
|
translations.forEach(t => {
|
||||||
|
writeDestinationFile(t.destinationPath, t.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
log('Main translation files written to:');
|
||||||
|
translations.forEach(t => log(t.destinationPath, { bulletpoint: true }))
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTranslationPaths() {
|
||||||
|
const destinationPaths = require('./package.json').n8n.nodes;
|
||||||
|
const { N8N_DEFAULT_LOCALE: locale } = process.env;
|
||||||
|
const seen = {};
|
||||||
|
|
||||||
|
return destinationPaths.reduce((acc, cur) => {
|
||||||
|
const sourcePath = path.join(
|
||||||
|
__dirname,
|
||||||
|
cur.split('/').slice(1, -1).join('/'),
|
||||||
|
'translations',
|
||||||
|
`${locale}.ts`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existsSync(sourcePath) && !seen[sourcePath]) {
|
||||||
|
seen[sourcePath] = true;
|
||||||
|
|
||||||
|
const destinationPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
cur.split('/').slice(0, -1).join('/'),
|
||||||
|
'translations',
|
||||||
|
`${locale}.js`,
|
||||||
|
);
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
source: sourcePath,
|
||||||
|
destination: destinationPath,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeadersAndTranslations(paths) {
|
||||||
|
return paths.reduce((acc, cur) => {
|
||||||
|
const translation = require(cur.source);
|
||||||
|
const nodeType = Object.keys(translation).pop();
|
||||||
|
const { header } = translation[nodeType];
|
||||||
|
|
||||||
|
if (isValidHeader(header, ALLOWED_HEADER_KEYS)) {
|
||||||
|
acc.headers[nodeType] = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.translations.push({
|
||||||
|
destinationPath: cur.destination,
|
||||||
|
content: translation,
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, { headers: {}, translations: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// helpers
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
function isValidHeader(header, allowedHeaderKeys) {
|
||||||
|
if (!header) return false;
|
||||||
|
|
||||||
|
const headerKeys = Object.keys(header);
|
||||||
|
|
||||||
|
return headerKeys.length > 0 &&
|
||||||
|
headerKeys.every(key => allowedHeaderKeys.includes(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLocale() {
|
||||||
|
const { N8N_DEFAULT_LOCALE: locale } = process.env;
|
||||||
|
|
||||||
|
log(`Default locale set to: ${colorize(PURPLE_ANSI_COLOR_CODE, locale || 'en')}`);
|
||||||
|
|
||||||
|
if (!locale || locale === 'en') {
|
||||||
|
log('No translation required - Skipping translations build...');
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeDestinationFile(destinationPath, data) {
|
||||||
|
writeFile(
|
||||||
|
destinationPath,
|
||||||
|
`module.exports = ${JSON.stringify(data, null, 2)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const log = (string, { bulletpoint } = { bulletpoint: false }) => {
|
||||||
|
if (bulletpoint) {
|
||||||
|
process.stdout.write(
|
||||||
|
colorize(PURPLE_ANSI_COLOR_CODE, `- ${string}\n`),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
process.stdout.write(`${string}\n`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const colorize = (ansiColorCode, string) =>
|
||||||
|
['\033[', ansiColorCode, 'm', string, '\033[0m'].join('')
|
|
@ -1,5 +1,9 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
bitwarden: {
|
bitwarden: {
|
||||||
|
header: {
|
||||||
|
displayName: '🇩🇪 Bitwarden',
|
||||||
|
description: '🇩🇪 Consume Bitwarden API',
|
||||||
|
},
|
||||||
credentialsModal: {
|
credentialsModal: {
|
||||||
bitwardenApi: {
|
bitwardenApi: {
|
||||||
environment: {
|
environment: {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
github: {
|
github: {
|
||||||
|
header: {
|
||||||
|
displayName: '🇩🇪 GitHub',
|
||||||
|
description: '🇩🇪 Consume GitHub API',
|
||||||
|
},
|
||||||
credentialsModal: {
|
credentialsModal: {
|
||||||
githubOAuth2Api: {
|
githubOAuth2Api: {
|
||||||
server: {
|
server: {
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run watch",
|
"dev": "npm run watch",
|
||||||
"build": "tsc && gulp",
|
"build": "tsc && gulp build:icons && gulp build:translations",
|
||||||
|
"build:translations": "gulp build:translations",
|
||||||
"format": "cd ../.. && node_modules/prettier/bin-prettier.js packages/nodes-base/**/**.ts --write",
|
"format": "cd ../.. && node_modules/prettier/bin-prettier.js packages/nodes-base/**/**.ts --write",
|
||||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
||||||
|
|
Loading…
Reference in a new issue