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> => {
const packagesPath = pathJoin(__dirname, '..', '..', '..');
const headersPath = pathJoin(packagesPath, 'nodes-base', 'dist', 'nodes', 'headers');
if (!existsSync(`${headersPath}.js`)) return;
try {
return require(headersPath);
} 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 { mapGetters } from 'vuex';
import { userHelpers } from './components/mixins/userHelpers';
import { addHeaders, loadLanguage } from './plugins/i18n';
import { restApi } from '@/components/mixins/restApi';
export default mixins(
showMessage,
userHelpers,
restApi,
).extend({
name: 'App',
components: {
@ -42,6 +45,9 @@ export default mixins(
computed: {
...mapGetters('settings', ['isHiringBannerEnabled', 'isTemplatesEnabled', 'isTemplatesEndpointReachable', 'isUserManagementEnabled', 'showSetupPage']),
...mapGetters('users', ['currentUser']),
defaultLocale (): string {
return this.$store.getters.defaultLocale;
},
},
data() {
return {
@ -79,7 +85,7 @@ export default mixins(
}
},
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
}
},
@ -143,8 +149,8 @@ export default mixins(
},
},
async mounted() {
this.logHiringBanner();
await this.initialize();
this.logHiringBanner();
this.authenticate();
this.redirectIfNecessary();
@ -152,6 +158,11 @@ export default mixins(
this.trackPage();
this.$externalHooks().run('app.mount');
if (this.defaultLocale !== 'en') {
const headers = await this.restApi().getNodeTranslationHeaders();
if (headers) addHeaders(headers, this.defaultLocale);
}
},
watch: {
$route(route) {
@ -160,6 +171,9 @@ export default mixins(
this.trackPage();
},
defaultLocale(newLocale) {
loadLanguage(newLocale);
},
},
});
</script>

View file

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

View file

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

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

View file

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

File diff suppressed because it is too large Load diff

View file

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