2021-12-07 05:04:36 -08:00
import _Vue from "vue" ;
2021-12-07 08:46:17 -08:00
import axios from 'axios' ;
2021-12-07 05:04:36 -08:00
import VueI18n from 'vue-i18n' ;
2021-12-07 06:58:26 -08:00
import { Store } from "vuex" ;
2021-12-07 08:46:17 -08:00
import Vue from 'vue' ;
import { INodeTranslationHeaders , IRootState } from '@/Interface' ;
2022-01-07 13:02:21 -08:00
import {
deriveMiddleKey ,
isNestedInCollectionLike ,
normalize ,
insertOptionsAndValues ,
} from "./utils" ;
2021-12-07 08:46:17 -08:00
const englishBaseText = require ( './locales/en' ) ;
Vue . use ( VueI18n ) ;
2021-12-07 05:04:36 -08:00
2021-12-07 06:58:26 -08:00
export function I18nPlugin ( vue : typeof _Vue , store : Store < IRootState > ) : void {
2021-12-07 05:04:36 -08:00
const i18n = new I18nClass ( store ) ;
2021-12-15 04:16:53 -08:00
Object . defineProperty ( vue , '$locale' , {
2021-12-07 08:28:10 -08:00
get ( ) { return i18n ; } ,
} ) ;
2021-12-15 04:16:53 -08:00
Object . defineProperty ( vue . prototype , '$locale' , {
2021-12-07 08:28:10 -08:00
get ( ) { return i18n ; } ,
} ) ;
2021-12-07 05:04:36 -08:00
}
export class I18nClass {
2021-12-07 06:58:26 -08:00
$store : Store < IRootState > ;
2021-12-07 05:04:36 -08:00
2021-12-07 06:58:26 -08:00
constructor ( store : Store < IRootState > ) {
2021-12-07 05:04:36 -08:00
this . $store = store ;
}
private get i18n ( ) : VueI18n {
2021-12-07 08:46:17 -08:00
return i18nInstance ;
2021-12-07 05:04:36 -08:00
}
2021-12-07 06:58:26 -08:00
// ----------------------------------
2021-12-07 08:46:17 -08:00
// helper methods
2021-12-07 06:58:26 -08:00
// ----------------------------------
exists ( key : string ) {
return this . i18n . te ( key ) ;
}
shortNodeType ( longNodeType : string ) {
2021-12-07 05:04:36 -08:00
return longNodeType . replace ( 'n8n-nodes-base.' , '' ) ;
}
2021-12-07 08:46:17 -08:00
// ----------------------------------
// render methods
// ----------------------------------
2021-12-07 05:04:36 -08:00
/ * *
2022-01-07 13:02:21 -08:00
* Render a string of base text , i . e . a string with a fixed path to the localized value . 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.
2021-12-07 05:04:36 -08:00
* /
baseText (
2022-01-07 13:02:21 -08:00
key : string ,
options ? : { interpolate : { [ key : string ] : string } } ,
2021-12-07 05:04:36 -08:00
) : string {
return this . i18n . t ( key , options && options . interpolate ) . toString ( ) ;
}
2021-12-07 08:46:17 -08:00
/ * *
2022-01-07 13:02:21 -08:00
* Render a string of dynamic text , i . e . a string with a constructed path to the localized value .
2021-12-07 08:46:17 -08:00
* /
private dynamicRender (
{ key , fallback } : { key : string ; fallback : string ; } ,
) {
return this . i18n . te ( key ) ? this . i18n . t ( key ) . toString ( ) : fallback ;
}
2021-12-07 05:04:36 -08:00
/ * *
2022-01-07 13:02:21 -08:00
* Render a string of header text ( a node ' s name and description ) ,
* used variously in the nodes panel , under the node icon , etc .
2021-12-07 05:04:36 -08:00
* /
headerText ( arg : { key : string ; fallback : string ; } ) {
2021-12-07 06:58:26 -08:00
return this . dynamicRender ( arg ) ;
2021-12-07 05:04:36 -08:00
}
2022-01-07 13:02:21 -08:00
/ * *
* Namespace for methods to render text in the credentials details modal .
* /
2021-12-07 05:04:36 -08:00
credText ( ) {
2022-01-07 13:02:21 -08:00
const credentialType = this . $store . getters . activeCredentialType ;
const credentialPrefix = ` n8n-nodes-base.credentials. ${ credentialType } ` ;
2021-12-07 05:04:36 -08:00
const context = this ;
return {
/ * *
2022-01-07 13:02:21 -08:00
* Display name for a top - level param .
2021-12-07 05:04:36 -08:00
* /
2022-01-07 13:02:21 -08:00
inputLabelDisplayName (
2021-12-07 05:04:36 -08:00
{ name : parameterName , displayName } : { name : string ; displayName : string ; } ,
) {
if ( [ 'clientId' , 'clientSecret' ] . includes ( parameterName ) ) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` reusableDynamicText.oauth2. ${ parameterName } ` ,
2021-12-07 05:04:36 -08:00
fallback : displayName ,
} ) ;
}
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2021-12-07 05:04:36 -08:00
key : ` ${ credentialPrefix } . ${ parameterName } .displayName ` ,
fallback : displayName ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Description ( tooltip text ) for an input label param .
2021-12-07 05:04:36 -08:00
* /
2022-01-07 13:02:21 -08:00
inputLabelDescription (
2021-12-07 05:04:36 -08:00
{ name : parameterName , description } : { name : string ; description : string ; } ,
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2021-12-07 05:04:36 -08:00
key : ` ${ credentialPrefix } . ${ parameterName } .description ` ,
fallback : description ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Display name for an option inside an ` options ` or ` multiOptions ` param .
2021-12-07 05:04:36 -08:00
* /
optionsOptionDisplayName (
{ name : parameterName } : { name : string ; } ,
{ value : optionName , name : displayName } : { value : string ; name : string ; } ,
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2021-12-07 05:04:36 -08:00
key : ` ${ credentialPrefix } . ${ parameterName } .options. ${ optionName } .displayName ` ,
fallback : displayName ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Description for an option inside an ` options ` or ` multiOptions ` param .
2021-12-07 05:04:36 -08:00
* /
optionsOptionDescription (
{ name : parameterName } : { name : string ; } ,
{ value : optionName , description } : { value : string ; description : string ; } ,
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2021-12-07 05:04:36 -08:00
key : ` ${ credentialPrefix } . ${ parameterName } .options. ${ optionName } .description ` ,
fallback : description ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Placeholder for a ` string ` or ` collection ` or ` fixedCollection ` param .
2021-12-07 05:04:36 -08:00
* - For a ` string ` parameter , the placeholder is unselectable greyed - out sample text .
* - For a ` collection ` or ` fixedCollection ` parameter , the placeholder is the button text .
* /
placeholder (
{ name : parameterName , displayName } : { name : string ; displayName : string ; } ,
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2021-12-07 05:04:36 -08:00
key : ` ${ credentialPrefix } . ${ parameterName } .placeholder ` ,
fallback : displayName ,
} ) ;
} ,
} ;
}
2022-01-07 13:02:21 -08:00
/ * *
* Namespace for methods to render text in the node details view ,
* except for ` eventTriggerDescription ` .
* /
2021-12-07 05:04:36 -08:00
nodeText ( ) {
2022-01-07 13:02:21 -08:00
const activeNode = this . $store . getters . activeNode ;
const nodeType = activeNode ? this . shortNodeType ( activeNode . type ) : '' ; // unused in eventTriggerDescription
const initialKey = ` n8n-nodes-base.nodes. ${ nodeType } .nodeView ` ;
2021-12-07 05:04:36 -08:00
const context = this ;
return {
/ * *
2022-01-07 13:02:21 -08:00
* Display name for an input label , whether top - level or nested .
2021-12-07 05:04:36 -08:00
* /
2022-01-07 13:02:21 -08:00
inputLabelDisplayName (
parameter : { name : string ; displayName : string ; type : string } ,
path : string ,
2021-12-07 05:04:36 -08:00
) {
2022-01-07 13:02:21 -08:00
const middleKey = deriveMiddleKey ( path , parameter ) ;
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ middleKey } .displayName ` ,
fallback : parameter.displayName ,
2021-12-07 05:04:36 -08:00
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Description ( tooltip text ) for an input label , whether top - level or nested .
2021-12-07 05:04:36 -08:00
* /
2022-01-07 13:02:21 -08:00
inputLabelDescription (
parameter : { name : string ; description : string ; type : string } ,
path : string ,
2021-12-07 05:04:36 -08:00
) {
2022-01-07 13:02:21 -08:00
const middleKey = deriveMiddleKey ( path , parameter ) ;
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ middleKey } .description ` ,
fallback : parameter.description ,
2021-12-07 05:04:36 -08:00
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Placeholder for an input label or ` collection ` or ` fixedCollection ` param ,
* whether top - level or nested .
* - For an input label , the placeholder is unselectable greyed - out sample text .
* - For a ` collection ` or ` fixedCollection ` , the placeholder is the button text .
2021-12-07 05:04:36 -08:00
* /
2022-01-07 13:02:21 -08:00
placeholder (
parameter : { name : string ; placeholder : string ; type : string } ,
path : string ,
2021-12-07 05:04:36 -08:00
) {
2022-01-07 13:02:21 -08:00
let middleKey = parameter . name ;
if ( isNestedInCollectionLike ( path ) ) {
const pathSegments = normalize ( path ) . split ( '.' ) ;
middleKey = insertOptionsAndValues ( pathSegments ) . join ( '.' ) ;
}
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ middleKey } .placeholder ` ,
fallback : parameter.placeholder ,
2021-12-07 05:04:36 -08:00
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Display name for an option inside an ` options ` or ` multiOptions ` param ,
* whether top - level or nested .
2021-12-07 05:04:36 -08:00
* /
optionsOptionDisplayName (
2022-01-07 13:02:21 -08:00
parameter : { name : string ; } ,
2021-12-07 05:04:36 -08:00
{ value : optionName , name : displayName } : { value : string ; name : string ; } ,
2022-01-07 13:02:21 -08:00
path : string ,
2021-12-07 05:04:36 -08:00
) {
2022-01-07 13:02:21 -08:00
let middleKey = parameter . name ;
if ( isNestedInCollectionLike ( path ) ) {
const pathSegments = normalize ( path ) . split ( '.' ) ;
middleKey = insertOptionsAndValues ( pathSegments ) . join ( '.' ) ;
}
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ middleKey } .options. ${ optionName } .displayName ` ,
2021-12-07 05:04:36 -08:00
fallback : displayName ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Description for an option inside an ` options ` or ` multiOptions ` param ,
* whether top - level or nested .
2021-12-07 05:04:36 -08:00
* /
optionsOptionDescription (
2022-01-07 13:02:21 -08:00
parameter : { name : string ; } ,
2021-12-07 05:04:36 -08:00
{ value : optionName , description } : { value : string ; description : string ; } ,
2022-01-07 13:02:21 -08:00
path : string ,
2021-12-07 05:04:36 -08:00
) {
2022-01-07 13:02:21 -08:00
let middleKey = parameter . name ;
if ( isNestedInCollectionLike ( path ) ) {
const pathSegments = normalize ( path ) . split ( '.' ) ;
middleKey = insertOptionsAndValues ( pathSegments ) . join ( '.' ) ;
}
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ middleKey } .options. ${ optionName } .description ` ,
2021-12-07 05:04:36 -08:00
fallback : description ,
} ) ;
} ,
/ * *
2022-01-07 13:02:21 -08:00
* Display name for an option in the dropdown menu of a ` collection ` or
* fixedCollection ` param. No nesting support since ` collection ` cannot
* be nested in a ` collection ` or in a ` fixedCollection ` .
* /
collectionOptionDisplayName (
parameter : { name : string ; } ,
{ name : optionName , displayName } : { name : string ; displayName : string ; } ,
path : string ,
) {
let middleKey = parameter . name ;
if ( isNestedInCollectionLike ( path ) ) {
const pathSegments = normalize ( path ) . split ( '.' ) ;
middleKey = insertOptionsAndValues ( pathSegments ) . join ( '.' ) ;
}
return context . dynamicRender ( {
key : ` ${ initialKey } . ${ middleKey } .options. ${ optionName } .displayName ` ,
fallback : displayName ,
} ) ;
} ,
/ * *
* Text for a button to add another option inside a ` collection ` or
* ` fixedCollection ` param having ` multipleValues: true ` .
2021-12-07 05:04:36 -08:00
* /
multipleValueButtonText (
{ name : parameterName , typeOptions : { multipleValueButtonText } } :
{ name : string ; typeOptions : { multipleValueButtonText : string ; } } ,
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` ${ initialKey } . ${ parameterName } .multipleValueButtonText ` ,
2021-12-07 05:04:36 -08:00
fallback : multipleValueButtonText ,
} ) ;
} ,
2022-01-07 13:02:21 -08:00
eventTriggerDescription (
nodeType : string ,
eventTriggerDescription : string ,
2021-12-07 05:04:36 -08:00
) {
2021-12-07 06:58:26 -08:00
return context . dynamicRender ( {
2022-01-07 13:02:21 -08:00
key : ` n8n-nodes-base.nodes. ${ nodeType } .nodeView.eventTriggerDescription ` ,
fallback : eventTriggerDescription ,
2021-12-07 05:04:36 -08:00
} ) ;
} ,
} ;
}
}
2021-12-07 08:46:17 -08:00
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 ) ;
}
2022-01-07 13:02:21 -08:00
/ * *
* Add a node translation to the i18n instance ' s ` messages ` object .
* /
2021-12-07 08:46:17 -08:00
export function addNodeTranslation (
2022-01-07 13:02:21 -08:00
nodeTranslation : { [ nodeType : string ] : object } ,
language : string ,
) {
const oldNodesBase = i18nInstance . messages [ language ] [ 'n8n-nodes-base' ] || { } ;
const updatedNodes = {
// @ts-ignore
. . . oldNodesBase . nodes ,
. . . nodeTranslation ,
} ;
const newNodesBase = {
'n8n-nodes-base' : Object . assign (
oldNodesBase ,
{ nodes : updatedNodes } ,
) ,
} ;
i18nInstance . setLocaleMessage (
language ,
Object . assign ( i18nInstance . messages [ language ] , newNodesBase ) ,
) ;
}
/ * *
* Add a credential translation to the i18n instance ' s ` messages ` object .
* /
export function addCredentialTranslation (
nodeCredentialTranslation : { [ credentialType : string ] : object } ,
2021-12-07 08:46:17 -08:00
language : string ,
) {
2022-01-07 13:02:21 -08:00
const oldNodesBase = i18nInstance . messages [ language ] [ 'n8n-nodes-base' ] || { } ;
const updatedCredentials = {
// @ts-ignore
. . . oldNodesBase . credentials ,
. . . nodeCredentialTranslation ,
} ;
2021-12-07 08:46:17 -08:00
const newNodesBase = {
'n8n-nodes-base' : Object . assign (
2022-01-07 13:02:21 -08:00
oldNodesBase ,
{ credentials : updatedCredentials } ,
2021-12-07 08:46:17 -08:00
) ,
} ;
i18nInstance . setLocaleMessage (
language ,
Object . assign ( i18nInstance . messages [ language ] , newNodesBase ) ,
) ;
}
2022-01-07 13:02:21 -08:00
/ * *
* Add a node 's header strings to the i18n instance' s ` messages ` object .
* /
2021-12-07 08:46:17 -08:00
export function addHeaders (
headers : INodeTranslationHeaders ,
language : string ,
) {
i18nInstance . setLocaleMessage (
language ,
Object . assign ( i18nInstance . messages [ language ] , { headers } ) ,
) ;
2022-01-07 13:02:21 -08:00
}