diff --git a/packages/cli/src/NodeTypes.ts b/packages/cli/src/NodeTypes.ts index 094ca41cec..5cc9e956ab 100644 --- a/packages/cli/src/NodeTypes.ts +++ b/packages/cli/src/NodeTypes.ts @@ -40,6 +40,10 @@ class NodeTypesClass implements INodeTypes { return this.nodeTypes[nodeType].type; } + /** + * Variant of `getByNameAndVersion` that includes the node's source path, + * to be used for locating the node's `/translations` dir. + */ getWithPath( nodeTypeName: string, version: number, diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 41e5cf663c..e72e455811 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -145,7 +145,7 @@ import { InternalHooksManager } from './InternalHooksManager'; import { TagEntity } from './databases/entities/TagEntity'; import { WorkflowEntity } from './databases/entities/WorkflowEntity'; import { NameRequest } from './WorkflowHelpers'; -import { getTranslationPath } from './TranslationHelpers'; +import { getExpectedNodeTranslationPath } from './TranslationHelpers'; require('body-parser-xml')(bodyParser); @@ -1186,23 +1186,26 @@ class App { if (language === 'en') { return nodeInfos.reduce((acc, { name, version }) => { const { description } = nodeTypes.getByNameAndVersion(name, version); - if (description) acc.push(description); + acc.push(description); return acc; }, []); } - // add node translations where available - return nodeInfos.reduce((acc, { name, version }) => { - const { description, sourcePath } = nodeTypes.getWithPath(name, version); - const mainTranslationPath = getTranslationPath(sourcePath, language); + const nodeTypesWithTranslations: INodeTypeDescription[] = []; - if (description && existsSync(mainTranslationPath)) { - description.translation = require(mainTranslationPath); + for (const { name, version } of nodeInfos) { + const { description, sourcePath } = nodeTypes.getWithPath(name, version); + // eslint-disable-next-line no-await-in-loop + const nodeTranslationPath = await getExpectedNodeTranslationPath(sourcePath, language); + + if (existsSync(nodeTranslationPath)) { + description.translation = require(nodeTranslationPath); } - if (description) acc.push(description); - return acc; - }, []); + nodeTypesWithTranslations.push(description); + } + + return nodeTypesWithTranslations; }, ), ); diff --git a/packages/cli/src/TranslationHelpers.ts b/packages/cli/src/TranslationHelpers.ts index 00cc7bcbae..9ebbc577a3 100644 --- a/packages/cli/src/TranslationHelpers.ts +++ b/packages/cli/src/TranslationHelpers.ts @@ -1,8 +1,39 @@ import { join, dirname } from 'path'; +import { readdir } from 'fs/promises'; +import { Dirent } from 'fs'; -/** - * 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`); +const ALLOWED_VERSIONED_DIRNAME_LENGTH = [2, 3]; // v1, v10 + +function isVersionedDirname(dirent: Dirent) { + if (!dirent.isDirectory()) return false; + + return ( + ALLOWED_VERSIONED_DIRNAME_LENGTH.includes(dirent.name.length) && + dirent.name.toLowerCase().startsWith('v') + ); +} + +async function getMaxVersion(from: string) { + const entries = await readdir(from, { withFileTypes: true }); + + const dirnames = entries.reduce((acc, cur) => { + if (isVersionedDirname(cur)) acc.push(cur.name); + return acc; + }, []); + + if (!dirnames.length) return null; + + return Math.max(...dirnames.map((d) => parseInt(d.charAt(1), 10))); +} + +export async function getExpectedNodeTranslationPath( + nodeSourcePath: string, + language: string, +): Promise { + const nodeDir = dirname(nodeSourcePath); + const maxVersion = await getMaxVersion(nodeDir); + + return maxVersion + ? join(nodeDir, `v${maxVersion}`, 'translations', `${language}.js`) + : join(nodeDir, 'translations', `${language}.js`); } diff --git a/packages/nodes-base/nodes/Mattermost/v1/translations/de.ts b/packages/nodes-base/nodes/Mattermost/v1/translations/de.ts new file mode 100644 index 0000000000..daf924fc9d --- /dev/null +++ b/packages/nodes-base/nodes/Mattermost/v1/translations/de.ts @@ -0,0 +1,35 @@ +module.exports = { + mattermost: { + header: { + displayName: '🇩🇪 Mattermost', + description: '🇩🇪 Consume Mattermost API', + }, + credentialsModal: { + mattermostApi: { + accessToken: { + displayName: '🇩🇪 Access token', + }, + }, + }, + nodeView: { + resource: { + displayName: '🇩🇪 Resource', + description: '🇩🇪 The resource to operate on.', + options: { + channel: { + displayName: '🇩🇪 Channel', + }, + message: { + displayName: '🇩🇪 Message', + }, + reaction: { + displayName: '🇩🇪 Reaction', + }, + user: { + displayName: '🇩🇪 User', + }, + }, + }, + }, + }, +};