⚡ Bring setup logic to plugin
|
@ -81,7 +81,7 @@ import CopyInput from '../CopyInput.vue';
|
|||
import CredentialInputs from './CredentialInputs.vue';
|
||||
import OauthButton from './OauthButton.vue';
|
||||
import { restApi } from '@/components/mixins/restApi';
|
||||
import { addNodeTranslation } from '@/i18n';
|
||||
import { addNodeTranslation } from '@/plugins/i18n';
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
"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:
|
||||
|
||||
```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.
|
||||
|
@ -213,14 +213,14 @@ export class Github implements INodeType {
|
|||
Header text is used wherever the node's display name and description are needed:
|
||||
|
||||
<p align="center">
|
||||
<img src="img/header1.png" width="400">
|
||||
<img src="img/header2.png" width="200">
|
||||
<img src="img/header3.png" width="400">
|
||||
<img src="header1.png" width="400">
|
||||
<img src="header2.png" width="200">
|
||||
<img src="header3.png" width="400">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="img/header4.png" width="400">
|
||||
<img src="img/header5.png" width="500">
|
||||
<img src="header4.png" width="400">
|
||||
<img src="header5.png" width="500">
|
||||
</p>
|
||||
|
||||
#### `credentialsModal` section
|
||||
|
@ -322,7 +322,7 @@ The object for each node credential parameter allows for the keys `displayName`,
|
|||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="img/cred.png">
|
||||
<img src="cred.png">
|
||||
</p>
|
||||
|
||||
#### `nodeView` section
|
||||
|
@ -393,7 +393,7 @@ Allowed keys: `displayName`, `description`, and `placeholder`.
|
|||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="img/node1.png" width="400">
|
||||
<img src="node1.png" width="400">
|
||||
</p>
|
||||
|
||||
#### `options` parameter
|
||||
|
@ -446,7 +446,7 @@ Allowed subkeys: `options.{optionName}.displayName` and `options.{optionName}.de
|
|||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="img/node2.png" width="400">
|
||||
<img src="node2.png" width="400">
|
||||
</p>
|
||||
|
||||
#### `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">
|
||||
<img src="img/node4.png" width="400">
|
||||
<img src="node4.png" width="400">
|
||||
</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.
|
||||
|
@ -546,7 +546,7 @@ Currently only the keys `oauth.clientId` and `oauth.clientSecret` are supported
|
|||
|
||||
## 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:
|
||||
|
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 { IRootState } from '@/Interface';
|
||||
import axios from 'axios';
|
||||
import VueI18n from 'vue-i18n';
|
||||
import { i18n as i18nLib } from '../../i18n';
|
||||
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 CREDENTIALS_MODAL_KEY = 'credentialsModal';
|
||||
|
@ -28,20 +32,11 @@ export class I18nClass {
|
|||
}
|
||||
|
||||
private get i18n(): VueI18n {
|
||||
return i18nLib;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return i18nInstance;
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// template helpers
|
||||
// helper methods
|
||||
// ----------------------------------
|
||||
|
||||
exists(key: string) {
|
||||
|
@ -56,6 +51,10 @@ export class I18nClass {
|
|||
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.
|
||||
*/
|
||||
|
@ -65,6 +64,15 @@ export class I18nClass {
|
|||
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.
|
||||
*/
|
||||
|
@ -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,
|
||||
addNodeTranslation,
|
||||
addHeaders,
|
||||
} from '@/i18n';
|
||||
} from '@/plugins/i18n';
|
||||
|
||||
import '../plugins/N8nCustomConnectorType';
|
||||
import '../plugins/PlusEndpointType';
|
||||
|
|