address merge conflict

This commit is contained in:
Mutasem 2022-04-11 10:58:41 +02:00
commit d16d913708
8 changed files with 820 additions and 1238 deletions

View file

@ -1503,10 +1503,13 @@ class App {
async (req: express.Request, res: express.Response): Promise<object | void> => { async (req: express.Request, res: express.Response): Promise<object | void> => {
const packagesPath = pathJoin(__dirname, '..', '..', '..'); const packagesPath = pathJoin(__dirname, '..', '..', '..');
const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers'); const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers');
if (!existsSync(`${headersPath}.js`)) return;
try { try {
return require(headersPath); return require(headersPath);
} catch (error) { } catch (error) {
res.status(500).send('Failed to find headers file'); res.status(500).send('Failed to load headers file');
} }
}, },
), ),

View file

@ -28,10 +28,13 @@ import { showMessage } from './components/mixins/showMessage';
import { IUser } from './Interface'; import { IUser } from './Interface';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { userHelpers } from './components/mixins/userHelpers'; import { userHelpers } from './components/mixins/userHelpers';
import { addHeaders, loadLanguage } from './plugins/i18n';
import { restApi } from '@/components/mixins/restApi';
export default mixins( export default mixins(
showMessage, showMessage,
userHelpers, userHelpers,
restApi,
).extend({ ).extend({
name: 'App', name: 'App',
components: { components: {
@ -42,6 +45,9 @@ export default mixins(
computed: { computed: {
...mapGetters('settings', ['isHiringBannerEnabled', 'isTemplatesEnabled', 'isTemplatesEndpointReachable', 'isUserManagementEnabled', 'showSetupPage']), ...mapGetters('settings', ['isHiringBannerEnabled', 'isTemplatesEnabled', 'isTemplatesEndpointReachable', 'isUserManagementEnabled', 'showSetupPage']),
...mapGetters('users', ['currentUser']), ...mapGetters('users', ['currentUser']),
defaultLocale (): string {
return this.$store.getters.defaultLocale;
},
}, },
data() { data() {
return { return {
@ -79,7 +85,7 @@ export default mixins(
} }
}, },
logHiringBanner() { logHiringBanner() {
if (!this.isHiringBannerEnabled && this.$route.name !== VIEWS.DEMO) { if (this.isHiringBannerEnabled && this.$route.name !== VIEWS.DEMO) {
console.log(HIRING_BANNER); // eslint-disable-line no-console console.log(HIRING_BANNER); // eslint-disable-line no-console
} }
}, },
@ -143,8 +149,8 @@ export default mixins(
}, },
}, },
async mounted() { async mounted() {
this.logHiringBanner();
await this.initialize(); await this.initialize();
this.logHiringBanner();
this.authenticate(); this.authenticate();
this.redirectIfNecessary(); this.redirectIfNecessary();
@ -152,6 +158,11 @@ export default mixins(
this.trackPage(); this.trackPage();
this.$externalHooks().run('app.mount'); this.$externalHooks().run('app.mount');
if (this.defaultLocale !== 'en') {
const headers = await this.restApi().getNodeTranslationHeaders();
if (headers) addHeaders(headers, this.defaultLocale);
}
}, },
watch: { watch: {
$route(route) { $route(route) {
@ -160,6 +171,9 @@ export default mixins(
this.trackPage(); this.trackPage();
}, },
defaultLocale(newLocale) {
loadLanguage(newLocale);
},
}, },
}); });
</script> </script>

View file

@ -57,15 +57,7 @@ export default Vue.extend({
}, },
rows(): ITagRow[] { rows(): ITagRow[] {
const getUsage = (count: number | undefined) => count && count > 0 const getUsage = (count: number | undefined) => count && count > 0
? this.$locale.baseText( ? this.$locale.baseText('tagsView.inUse', { adjustToNumber: count })
count > 1 ?
'tagsView.inUse.plural' : 'tagsView.inUse.singular',
{
interpolate: {
count: count.toString(),
},
},
)
: this.$locale.baseText('tagsView.notBeingUsed'); : this.$locale.baseText('tagsView.notBeingUsed');
const disabled = this.isCreateEnabled || this.$data.updateId || this.$data.deleteId; const disabled = this.isCreateEnabled || this.$data.updateId || this.$data.deleteId;

View file

@ -2,16 +2,24 @@
## Base text ## Base text
### Interpolation ### Pluralization
Certain base text strings use [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) to allow for a variable to be passed in, signalled by curly braces: Certain base text strings accept [singular and plural versions](https://kazupon.github.io/vue-i18n/guide/pluralization.html) separated by a `|` character:
```json ```json
{ {
"stopExecution": { "tagsView.inUse": "{count} workflow | {count} workflows",
"message": "The execution with the ID {activeExecutionId} got stopped!", }
"title": "Execution stopped" ```
}
### Interpolation
Certain base text strings use [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) to allow for a variable between curly braces:
```json
{
"stopExecution.message": "The execution with the ID {activeExecutionId} got stopped!",
"stopExecution.title": "Execution stopped"
} }
``` ```
@ -19,10 +27,8 @@ When translating a string containing an interpolated variable, leave the variabl
```json ```json
{ {
"stopExecution": { "stopExecution.message": "Die Ausführung mit der ID {activeExecutionId} wurde gestoppt",
"message": "Die Ausführung mit der ID {activeExecutionId} wurde gestoppt", "stopExecution.title": "Execution stopped"
"title": "Execution stopped"
}
} }
``` ```
@ -32,18 +38,12 @@ As a convenience, the base text file may contain the special key `reusableBaseTe
```json ```json
{ {
"reusableBaseText": { "reusableBaseText.save": "🇩🇪 Save",
"save": "🇩🇪 Save", "duplicateWorkflowDialog.enterWorkflowName": "🇩🇪 Enter workflow name",
}, "duplicateWorkflowDialog.save": "@:reusableBaseText.save",
"duplicateWorkflowDialog": { "saveButton.save": "@:reusableBaseText.save",
"enterWorkflowName": "🇩🇪 Enter workflow name", "saveButton.saving": "🇩🇪 Saving",
"save": "@:reusableBaseText.save", "saveButton.saved": "🇩🇪 Saved",
},
"saveButton": {
"save": "@:reusableBaseText.save",
"saving": "🇩🇪 Saving",
"saved": "🇩🇪 Saved",
},
} }
``` ```
@ -92,23 +92,27 @@ Currently only the keys `oauth.clientId` and `oauth.clientSecret` are supported
```json ```json
{ {
"reusableDynamicText": { "reusableDynamicText.oauth2.clientId": "🇩🇪 Client ID",
"oauth2": { "reusableDynamicText.oauth2.clientSecret": "🇩🇪 Client Secret",
"clientId": "🇩🇪 Client ID",
"clientSecret": "🇩🇪 Client Secret",
}
}
} }
``` ```
### Special cases ### Special cases
`eventTriggerDescription` is a dynamic node property that is not part of node parameters. To translate it, set the `eventTriggerDescription` key at the root level of the `nodeView` property in the node translation file. `eventTriggerDescription` and `activationMessage` are dynamic node properties that are not part of node parameters. To translate them, set the key at the root level of the `nodeView` property in the node translation file.
Webhook node:
```json ```json
{ {
"nodeView": { "nodeView.eventTriggerDescription": "🇩🇪 Waiting for you to call the Test URL",
"eventTriggerDescription": "🇩🇪 Waiting for you to call the Test URL" }
} ```
Cron node:
```json
{
"nodeView.activationMessage": "🇩🇪 'Your cron trigger will now trigger executions on the schedule you have defined."
} }
``` ```

View file

@ -6,6 +6,7 @@ n8n allows for internalization of the majority of UI text:
- base text, e.g. menu display items in the left-hand sidebar menu, - base text, e.g. menu display items in the left-hand sidebar menu,
- node text, e.g. parameter display names and placeholders in the node view, - node text, e.g. parameter display names and placeholders in the node view,
- credential text, e.g. parameter display names and placeholders in the credential modal,
- header text, e.g. node display names and descriptions at various spots. - header text, e.g. node display names and descriptions at various spots.
Currently, n8n does _not_ allow for internalization of: Currently, n8n does _not_ allow for internalization of:
@ -55,12 +56,10 @@ Base text is rendered with no dependencies, i.e. base text is fixed and does not
The base text file for each locale is located at `/packages/editor-ui/src/plugins/i18n/locales/` and is named `{localeIdentifier}.json`. Keys in the base text file can be Vue component dirs, Vue component names, and references to symbols in those Vue components. These keys are added by the team as the UI is modified or expanded. The base text file for each locale is located at `/packages/editor-ui/src/plugins/i18n/locales/` and is named `{localeIdentifier}.json`. Keys in the base text file can be Vue component dirs, Vue component names, and references to symbols in those Vue components. These keys are added by the team as the UI is modified or expanded.
```json ```json
"nodeCreator": { {
"categoryNames": { "nodeCreator.categoryNames.analytics": "🇩🇪 Analytics",
"analytics": "🇩🇪 Analytics", "nodeCreator.categoryNames.communication": "🇩🇪 Communication",
"communication": "🇩🇪 Communication", "nodeCreator.categoryNames.coreNodes": "🇩🇪 Core Nodes"
"coreNodes": "🇩🇪 Core Nodes"
}
} }
``` ```
@ -98,9 +97,9 @@ A credential translation file is placed at `/nodes-base/credentials/translations
``` ```
credentials credentials
└── translations └── translations
└── de └── de
├── githubApi.json ├── githubApi.json
└── githubOAuth2Api.json └── githubOAuth2Api.json
``` ```
Every credential must have its own credential translation file. Every credential must have its own credential translation file.
@ -123,9 +122,9 @@ GitHub
├── GitHub.node.ts ├── GitHub.node.ts
├── GitHubTrigger.node.ts ├── GitHubTrigger.node.ts
└── translations └── translations
└── de └── de
├── github.json ├── github.json
└── githubTrigger.json └── githubTrigger.json
``` ```
Every node must have its own node translation file. Every node must have its own node translation file.
@ -184,16 +183,10 @@ The object for each node credential parameter allows for the keys `displayName`,
```json ```json
{ {
"server": { "server.displayName": "🇩🇪 Github Server",
"displayName": "🇩🇪 Github Server", "server.description": "🇩🇪 The server to connect to. Only has to be set if Github Enterprise is used.",
"description": "🇩🇪 The server to connect to. Only has to be set if Github Enterprise is used.", "user.placeholder": "🇩🇪 Hans",
}, "accessToken.placeholder": "🇩🇪 123",
"user": {
"placeholder": "🇩🇪 Hans",
},
"accessToken": {
"placeholder": "🇩🇪 123",
},
} }
``` ```
@ -224,10 +217,8 @@ export class Github implements INodeType {
```json ```json
{ {
"header": { "header.displayName": "🇩🇪 GitHub",
"displayName": "🇩🇪 GitHub", "header.description": "🇩🇪 Consume GitHub API",
"description": "🇩🇪 Consume GitHub API",
},
} }
``` ```
@ -264,11 +255,7 @@ export class Github implements INodeType {
```json ```json
{ {
"nodeView": { "nodeView.resource.displayName": "🇩🇪 Resource",
"resource": {
"displayName": "🇩🇪 Resource",
},
},
} }
``` ```
@ -291,13 +278,9 @@ Allowed keys: `displayName`, `description`, `placeholder`
```json ```json
{ {
"nodeView": { "nodeView.owner.displayName": "🇩🇪 Repository Owner",
"owner": { "nodeView.owner.placeholder": "🇩🇪 n8n-io",
"displayName": "🇩🇪 Repository Owner", "nodeView.owner.description": "🇩🇪 Owner of the repository",
"placeholder": "🇩🇪 n8n-io",
"description": "🇩🇪 Owner of the repository",
},
},
} }
``` ```
@ -333,20 +316,10 @@ Allowed subkeys: `options.{optionName}.displayName` and `options.{optionName}.de
```json ```json
{ {
"nodeView": { "nodeView.resource.displayName": "🇩🇪 Resource",
"resource": { "nodeView.resource.description": "🇩🇪 Resource to operate on",
"displayName": "🇩🇪 Resource", "nodeView.resource.options.file.displayName": "🇩🇪 File",
"description": "🇩🇪 Resource to operate on", "nodeView.resource.options.issue.displayName": "🇩🇪 Issue",
"options": {
"file": {
"displayName": "🇩🇪 File",
},
"issue": {
"displayName": "🇩🇪 Issue",
},
},
},
},
} }
``` ```
@ -394,19 +367,11 @@ Example of `collection` parameter:
```json ```json
{ {
"nodeView": { "nodeView.labels.displayName": "🇩🇪 Labels",
"labels": { "nodeView.labels.multipleValueButtonText": "🇩🇪 Add Label",
"displayName": "🇩🇪 Labels", "nodeView.labels.options.label.displayName": "🇩🇪 Label",
"multipleValueButtonText": "🇩🇪 Add Label", "nodeView.labels.options.label.description": "🇩🇪 Label to add to issue",
"options": { "nodeView.labels.options.label.placeholder": "🇩🇪 Some placeholder"
"label": {
"displayName": "🇩🇪 Label",
"description": "🇩🇪 Label to add to issue",
"placeholder": "🇩🇪 Some placeholder"
}
}
}
}
} }
``` ```
@ -461,29 +426,15 @@ Example of `fixedCollection` parameter:
```json ```json
{ {
"nodeView": { "nodeView.additionalParameters.displayName": "🇩🇪 Additional Parameters",
"additionalParameters": { "nodeView.additionalParameters.placeholder": "🇩🇪 Add Field",
"displayName": "🇩🇪 Additional Parameters", "nodeView.additionalParameters.options.author.displayName": "🇩🇪 Author",
"placeholder": "🇩🇪 Add Field", "nodeView.additionalParameters.options.author.values.name.displayName": "🇩🇪 Name",
"options": { "nodeView.additionalParameters.options.author.values.name.description": "🇩🇪 Name of the author of the commit",
"author": { "nodeView.additionalParameters.options.author.values.name.placeholder": "🇩🇪 Jan",
"displayName": "🇩🇪 Author", "nodeView.additionalParameters.options.author.values.email.displayName": "🇩🇪 Email",
"values": { "nodeView.additionalParameters.options.author.values.email.description": "🇩🇪 Email of the author of the commit",
"name": { "nodeView.additionalParameters.options.author.values.email.placeholder": "🇩🇪 jan@n8n.io",
"displayName": "🇩🇪 Name",
"description": "🇩🇪 Name of the author of the commit",
"placeholder": "🇩🇪 Jan"
},
"email": {
"displayName": "🇩🇪 Email",
"description": "🇩🇪 Email of the author of the commit",
"placeholder": "🇩🇪 jan@n8n.io"
}
}
},
}
}
}
} }
``` ```

View file

@ -63,8 +63,12 @@ export class I18nClass {
*/ */
baseText( baseText(
key: string, key: string,
options?: { interpolate: { [key: string]: string } }, options?: { adjustToNumber: number; interpolate: { [key: string]: string } },
): string { ): string {
if (options && options.adjustToNumber) {
return this.i18n.tc(key, options.adjustToNumber, options && options.interpolate).toString();
}
return this.i18n.t(key, options && options.interpolate).toString(); return this.i18n.t(key, options && options.interpolate).toString();
} }

File diff suppressed because it is too large Load diff

View file

@ -170,7 +170,6 @@ import {
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { import {
loadLanguage,
addNodeTranslation, addNodeTranslation,
addHeaders, addHeaders,
} from '@/plugins/i18n'; } from '@/plugins/i18n';
@ -232,9 +231,6 @@ export default mixins(
deep: true, deep: true,
}, },
async defaultLocale (newLocale, oldLocale) {
loadLanguage(newLocale);
},
}, },
async beforeRouteLeave(to, from, next) { async beforeRouteLeave(to, from, next) {
const result = this.$store.getters.getStateIsDirty; const result = this.$store.getters.getStateIsDirty;
@ -271,7 +267,7 @@ export default mixins(
defaultLocale (): string { defaultLocale (): string {
return this.$store.getters.defaultLocale; return this.$store.getters.defaultLocale;
}, },
englishLocale(): boolean { isEnglishLocale(): boolean {
return this.defaultLocale === 'en'; return this.defaultLocale === 'en';
}, },
...mapGetters(['nativelyNumberSuffixedDefaults']), ...mapGetters(['nativelyNumberSuffixedDefaults']),
@ -379,7 +375,7 @@ export default mixins(
type?: string, type?: string,
}) { }) {
const allNodeNamesOnCanvas = this.$store.getters.allNodes.map((n: INodeUi) => n.name); const allNodeNamesOnCanvas = this.$store.getters.allNodes.map((n: INodeUi) => n.name);
originalName = this.englishLocale ? originalName : this.translateName(type, originalName); originalName = this.isEnglishLocale ? originalName : this.translateName(type, originalName);
if ( if (
!allNodeNamesOnCanvas.includes(originalName) && !allNodeNamesOnCanvas.includes(originalName) &&
@ -389,7 +385,7 @@ export default mixins(
} }
let natives: string[] = this.nativelyNumberSuffixedDefaults; let natives: string[] = this.nativelyNumberSuffixedDefaults;
natives = this.englishLocale ? natives : natives.map(name => { natives = this.isEnglishLocale ? natives : natives.map(name => {
const type = name.toLowerCase().replace('_', ''); const type = name.toLowerCase().replace('_', '');
return this.translateName(type, name); return this.translateName(type, name);
}); });
@ -1261,15 +1257,10 @@ export default mixins(
const maxNodes = nodeTypeData.maxNodes; const maxNodes = nodeTypeData.maxNodes;
this.$showMessage({ this.$showMessage({
title: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.title'), title: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.title'),
message: this.$locale.baseText( message: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.message',
maxNodes === 1
? 'nodeView.showMessage.showMaxNodeTypeError.message.singular'
: 'nodeView.showMessage.showMaxNodeTypeError.message.plural',
{ {
interpolate: { adjustToNumber: maxNodes,
maxNodes: maxNodes!.toString(), interpolate: { nodeTypeDataDisplayName: nodeTypeData.displayName },
nodeTypeDataDisplayName: nodeTypeData.displayName,
},
}, },
), ),
type: 'error', type: 'error',
@ -2757,15 +2748,6 @@ export default mixins(
try { try {
await Promise.all(loadPromises); await Promise.all(loadPromises);
if (this.defaultLocale !== 'en') {
try {
const headers = await this.restApi().getNodeTranslationHeaders();
addHeaders(headers, this.defaultLocale);
} catch (_) {
// no headers available
}
}
} catch (error) { } catch (error) {
this.$showError( this.$showError(
error, error,