Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore i18n post-UM #3072

Merged
merged 15 commits into from
Apr 11, 2022
7 changes: 5 additions & 2 deletions packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
/* eslint-disable no-await-in-loop */

import * as express from 'express';
import { readFileSync } from 'fs';
import { existsSync, readFileSync } from 'fs';
import { readFile } from 'fs/promises';
import { cloneDeep } from 'lodash';
import { dirname as pathDirname, join as pathJoin, resolve as pathResolve } from 'path';
Expand Down Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ivov Why are we using a sync method here? Could we not simply use an async one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, switched to promisified access, still working:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great thanks for the fast fix!


try {
return require(headersPath);
} catch (error) {
res.status(500).send('Failed to find headers file');
res.status(500).send('Failed to load headers file');
}
},
),
Expand Down
18 changes: 16 additions & 2 deletions packages/editor-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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 {
Expand Down Expand Up @@ -79,7 +85,7 @@ export default mixins(
}
},
logHiringBanner() {
if (!this.isHiringBannerEnabled && this.$route.name !== VIEWS.DEMO) {
if (this.isHiringBannerEnabled && this.$route.name !== VIEWS.DEMO) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you change logic here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the original implementation, export N8N_HIRING_BANNER_ENABLED=false does not disable the banner.

console.log(HIRING_BANNER); // eslint-disable-line no-console
}
},
Expand Down Expand Up @@ -143,15 +149,20 @@ export default mixins(
},
},
async mounted() {
this.logHiringBanner();
await this.initialize();
this.logHiringBanner();
this.authenticate();
this.redirectIfNecessary();

this.loading = false;

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) {
Expand All @@ -160,6 +171,9 @@ export default mixins(

this.trackPage();
},
defaultLocale(newLocale) {
loadLanguage(newLocale);
},
},
});
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
66 changes: 35 additions & 31 deletions packages/editor-ui/src/plugins/i18n/docs/ADDENDUM.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@

## Base text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for updating the guide. can you also update section that says we should use first words of a message? I think I saw this in a previous PR. Instead names should be more descriptive.

Copy link
Contributor Author

@ivov ivov Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That section was in the other PR that was not merged. I intentionally left that part out.

Edit: To expand, the current keys still reference file structure. Not sure if we should add any recommendations that are not being followed yet.


### Pluralization

Certain base text strings accept [singular and plural versions](https://kazupon.github.io/vue-i18n/guide/pluralization.html) separated by a `|` character:

```json
{
"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 to be passed in, signalled by curly braces:
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!",
"title": "Execution stopped"
}
"stopExecution.message": "The execution with the ID {activeExecutionId} got stopped!",
"stopExecution.title": "Execution stopped"
}
```

When translating a string containing an interpolated variable, leave the variable untranslated:

```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"
}
```

Expand All @@ -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",
}
```

Expand Down Expand Up @@ -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",
}
```

Cron node:

```json
{
"nodeView": {
"eventTriggerDescription": "🇩🇪 Waiting for you to call the Test URL"
}
"nodeView.activationMessage": "🇩🇪 'Your cron trigger will now trigger executions on the schedule you have defined."
}
```
Loading