⚡ Bring setup logic to plugin
|
@ -81,7 +81,7 @@ import CopyInput from '../CopyInput.vue';
|
||||||
import CredentialInputs from './CredentialInputs.vue';
|
import CredentialInputs from './CredentialInputs.vue';
|
||||||
import OauthButton from './OauthButton.vue';
|
import OauthButton from './OauthButton.vue';
|
||||||
import { restApi } from '@/components/mixins/restApi';
|
import { restApi } from '@/components/mixins/restApi';
|
||||||
import { addNodeTranslation } from '@/i18n';
|
import { addNodeTranslation } from '@/plugins/i18n';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default mixins(restApi).extend({
|
export default mixins(restApi).extend({
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
import Vue from 'vue';
|
|
||||||
import VueI18n from 'vue-i18n';
|
|
||||||
const englishBaseText = require('./locales/en');
|
|
||||||
import axios from 'axios';
|
|
||||||
import { INodeTranslationHeaders } from '@/Interface';
|
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
|
||||||
|
|
||||||
export const i18n = new VueI18n({
|
|
||||||
locale: 'en',
|
|
||||||
fallbackLocale: 'en',
|
|
||||||
messages: { en: englishBaseText },
|
|
||||||
silentTranslationWarn: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadedLanguages = ['en'];
|
|
||||||
|
|
||||||
function setLanguage(language: string) {
|
|
||||||
i18n.locale = language;
|
|
||||||
axios.defaults.headers.common['Accept-Language'] = language;
|
|
||||||
document!.querySelector('html')!.setAttribute('lang', language);
|
|
||||||
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadLanguage(language?: string) {
|
|
||||||
if (!language) return Promise.resolve();
|
|
||||||
|
|
||||||
if (i18n.locale === language) {
|
|
||||||
return Promise.resolve(setLanguage(language));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadedLanguages.includes(language)) {
|
|
||||||
return Promise.resolve(setLanguage(language));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { numberFormats, ...rest } = require(`./locales/${language}.json`);
|
|
||||||
|
|
||||||
i18n.setLocaleMessage(language, rest);
|
|
||||||
|
|
||||||
if (numberFormats) {
|
|
||||||
i18n.setNumberFormat(language, numberFormats);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedLanguages.push(language);
|
|
||||||
|
|
||||||
setLanguage(language);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addNodeTranslation(
|
|
||||||
nodeTranslation: { [key: string]: object },
|
|
||||||
language: string,
|
|
||||||
) {
|
|
||||||
const newNodesBase = {
|
|
||||||
'n8n-nodes-base': Object.assign(
|
|
||||||
i18n.messages[language]['n8n-nodes-base'] || {},
|
|
||||||
nodeTranslation,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
i18n.setLocaleMessage(
|
|
||||||
language,
|
|
||||||
Object.assign(i18n.messages[language], newNodesBase),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addHeaders(
|
|
||||||
headers: INodeTranslationHeaders,
|
|
||||||
language: string,
|
|
||||||
) {
|
|
||||||
i18n.setLocaleMessage(
|
|
||||||
language,
|
|
||||||
Object.assign(i18n.messages[language], { headers }),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -31,7 +31,7 @@ Base text is directly rendered with no dependencies. Base text is supplied by th
|
||||||
|
|
||||||
### Locating base text
|
### Locating base text
|
||||||
|
|
||||||
Each base text file is located at `/packages/editor-ui/src/i18n/locales/{localeIdentifier}.json` and exports an object where keys are Vue component names (and their containing dirs if any) and references to parts of those Vue components.
|
Each base text file is located at `/packages/editor-ui/src/plugins/i18n/locales/{localeIdentifier}.json` and exports an object where keys are Vue component names (and their containing dirs if any) and references to parts of those Vue components.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"nodeCreator": {
|
"nodeCreator": {
|
||||||
|
@ -46,7 +46,7 @@ Each base text file is located at `/packages/editor-ui/src/i18n/locales/{localeI
|
||||||
1. For the new locale identifier, e.g. `de`, copy the `en` base text and rename it:
|
1. For the new locale identifier, e.g. `de`, copy the `en` base text and rename it:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp ./packages/editor-ui/src/i18n/locales/en.json ./packages/editor-ui/src/i18n/locales/de.json
|
cp ./packages/editor-ui/src/plugins/i18n/locales/en.json ./packages/editor-ui/src/plugins/i18n/locales/de.json
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check in the UI for a base text string to translate, and find it in the newly created base text file.
|
2. Check in the UI for a base text string to translate, and find it in the newly created base text file.
|
||||||
|
@ -213,14 +213,14 @@ export class Github implements INodeType {
|
||||||
Header text is used wherever the node's display name and description are needed:
|
Header text is used wherever the node's display name and description are needed:
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/header1.png" width="400">
|
<img src="header1.png" width="400">
|
||||||
<img src="img/header2.png" width="200">
|
<img src="header2.png" width="200">
|
||||||
<img src="img/header3.png" width="400">
|
<img src="header3.png" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/header4.png" width="400">
|
<img src="header4.png" width="400">
|
||||||
<img src="img/header5.png" width="500">
|
<img src="header5.png" width="500">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
#### `credentialsModal` section
|
#### `credentialsModal` section
|
||||||
|
@ -322,7 +322,7 @@ The object for each node credential parameter allows for the keys `displayName`,
|
||||||
```
|
```
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/cred.png">
|
<img src="cred.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
#### `nodeView` section
|
#### `nodeView` section
|
||||||
|
@ -393,7 +393,7 @@ Allowed keys: `displayName`, `description`, and `placeholder`.
|
||||||
```
|
```
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/node1.png" width="400">
|
<img src="node1.png" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
#### `options` parameter
|
#### `options` parameter
|
||||||
|
@ -446,7 +446,7 @@ Allowed subkeys: `options.{optionName}.displayName` and `options.{optionName}.de
|
||||||
```
|
```
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/node2.png" width="400">
|
<img src="node2.png" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
#### `collection` and `fixedCollection` parameters
|
#### `collection` and `fixedCollection` parameters
|
||||||
|
@ -523,7 +523,7 @@ To reduce nesting and to share translations, a parameter inside a collection's o
|
||||||
```
|
```
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="img/node4.png" width="400">
|
<img src="node4.png" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> **Note**: In case of deep nesting, i.e. a child of a child of a `collection` and `fixedCollection` parameter, the deeply nested child in principle should be translatable at the same level of nesting as the `collection` and `fixedCollection` parameter, but this has not been fully tested for this first release.
|
> **Note**: In case of deep nesting, i.e. a child of a child of a `collection` and `fixedCollection` parameter, the deeply nested child in principle should be translatable at the same level of nesting as the `collection` and `fixedCollection` parameter, but this has not been fully tested for this first release.
|
||||||
|
@ -546,7 +546,7 @@ Currently only the keys `oauth.clientId` and `oauth.clientSecret` are supported
|
||||||
|
|
||||||
## Base text
|
## Base text
|
||||||
|
|
||||||
When translating a base text file at `/packages/editor-ui/src/i18n/locales/{localeIdentifier}.json`:
|
When translating a base text file at `/packages/editor-ui/src/plugins/i18n/locales/{localeIdentifier}.json`:
|
||||||
|
|
||||||
1. Open a terminal:
|
1. Open a terminal:
|
||||||
|
|
Before Width: | Height: | Size: 857 KiB After Width: | Height: | Size: 857 KiB |
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 253 KiB |
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 239 KiB |
Before Width: | Height: | Size: 344 KiB After Width: | Height: | Size: 344 KiB |
Before Width: | Height: | Size: 296 KiB After Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
@ -1,8 +1,12 @@
|
||||||
import _Vue from "vue";
|
import _Vue from "vue";
|
||||||
import { IRootState } from '@/Interface';
|
import axios from 'axios';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import { i18n as i18nLib } from '../../i18n';
|
|
||||||
import { Store } from "vuex";
|
import { Store } from "vuex";
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { INodeTranslationHeaders, IRootState } from '@/Interface';
|
||||||
|
const englishBaseText = require('./locales/en');
|
||||||
|
|
||||||
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
const REUSABLE_DYNAMIC_TEXT_KEY = 'reusableDynamicText';
|
const REUSABLE_DYNAMIC_TEXT_KEY = 'reusableDynamicText';
|
||||||
const CREDENTIALS_MODAL_KEY = 'credentialsModal';
|
const CREDENTIALS_MODAL_KEY = 'credentialsModal';
|
||||||
|
@ -28,20 +32,11 @@ export class I18nClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get i18n(): VueI18n {
|
private get i18n(): VueI18n {
|
||||||
return i18nLib;
|
return i18nInstance;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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, in the node view, or in the headers. Unlike in `baseText`, the fallback has to be set manually for dynamic text.
|
|
||||||
*/
|
|
||||||
private dynamicRender(
|
|
||||||
{ key, fallback }: { key: string; fallback: string; },
|
|
||||||
) {
|
|
||||||
return this.i18n.te(key) ? this.i18n.t(key).toString() : fallback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// template helpers
|
// helper methods
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
||||||
exists(key: string) {
|
exists(key: string) {
|
||||||
|
@ -56,6 +51,10 @@ export class I18nClass {
|
||||||
return longNodeType.replace('n8n-nodes-base.', '');
|
return longNodeType.replace('n8n-nodes-base.', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// render methods
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a string of base text, i.e. a string with a fixed path to the localized value in the base text object. Optionally allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
* Render a string of base text, i.e. a string with a fixed path to the localized value in the base text object. Optionally allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
||||||
*/
|
*/
|
||||||
|
@ -65,6 +64,15 @@ export class I18nClass {
|
||||||
return this.i18n.t(key, options && options.interpolate).toString();
|
return this.i18n.t(key, options && options.interpolate).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, in the node view, or in the headers. Unlike in `baseText`, the fallback has to be set manually for dynamic text.
|
||||||
|
*/
|
||||||
|
private dynamicRender(
|
||||||
|
{ key, fallback }: { key: string; fallback: string; },
|
||||||
|
) {
|
||||||
|
return this.i18n.te(key) ? this.i18n.t(key).toString() : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a string of dynamic header text, used in the nodes panel and in the node view.
|
* Render a string of dynamic header text, used in the nodes panel and in the node view.
|
||||||
*/
|
*/
|
||||||
|
@ -252,3 +260,71 @@ export class I18nClass {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const i18nInstance = new VueI18n({
|
||||||
|
locale: 'en',
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages: { en: englishBaseText },
|
||||||
|
silentTranslationWarn: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadedLanguages = ['en'];
|
||||||
|
|
||||||
|
function setLanguage(language: string) {
|
||||||
|
i18nInstance.locale = language;
|
||||||
|
axios.defaults.headers.common['Accept-Language'] = language;
|
||||||
|
document!.querySelector('html')!.setAttribute('lang', language);
|
||||||
|
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadLanguage(language?: string) {
|
||||||
|
if (!language) return Promise.resolve();
|
||||||
|
|
||||||
|
if (i18nInstance.locale === language) {
|
||||||
|
return Promise.resolve(setLanguage(language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedLanguages.includes(language)) {
|
||||||
|
return Promise.resolve(setLanguage(language));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { numberFormats, ...rest } = require(`./locales/${language}.json`);
|
||||||
|
|
||||||
|
i18nInstance.setLocaleMessage(language, rest);
|
||||||
|
|
||||||
|
if (numberFormats) {
|
||||||
|
i18nInstance.setNumberFormat(language, numberFormats);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedLanguages.push(language);
|
||||||
|
|
||||||
|
setLanguage(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addNodeTranslation(
|
||||||
|
nodeTranslation: { [key: string]: object },
|
||||||
|
language: string,
|
||||||
|
) {
|
||||||
|
const newNodesBase = {
|
||||||
|
'n8n-nodes-base': Object.assign(
|
||||||
|
i18nInstance.messages[language]['n8n-nodes-base'] || {},
|
||||||
|
nodeTranslation,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
i18nInstance.setLocaleMessage(
|
||||||
|
language,
|
||||||
|
Object.assign(i18nInstance.messages[language], newNodesBase),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addHeaders(
|
||||||
|
headers: INodeTranslationHeaders,
|
||||||
|
language: string,
|
||||||
|
) {
|
||||||
|
i18nInstance.setLocaleMessage(
|
||||||
|
language,
|
||||||
|
Object.assign(i18nInstance.messages[language], { headers }),
|
||||||
|
);
|
||||||
|
}
|
|
@ -175,7 +175,7 @@ import {
|
||||||
loadLanguage,
|
loadLanguage,
|
||||||
addNodeTranslation,
|
addNodeTranslation,
|
||||||
addHeaders,
|
addHeaders,
|
||||||
} from '@/i18n';
|
} from '@/plugins/i18n';
|
||||||
|
|
||||||
import '../plugins/N8nCustomConnectorType';
|
import '../plugins/N8nCustomConnectorType';
|
||||||
import '../plugins/PlusEndpointType';
|
import '../plugins/PlusEndpointType';
|
||||||
|
|