From 0b39fbb2fe2b675d7deddef456985c1c42febcd4 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 21 Nov 2018 17:26:41 +0300 Subject: [PATCH 01/13] Register translations before plugins init --- src/cli/serve/serve.js | 15 ++++++ src/core/server/plugins/plugins_config.ts | 8 ++++ src/plugin_discovery/find_plugin_specs.js | 58 +++++++++++++++++++++-- src/plugin_discovery/plugin_pack/index.js | 1 + src/server/config/schema.js | 6 ++- 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 8de60c209dc7c..19f432c02d72e 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -132,6 +132,21 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { : [], ))); + set('plugins.translations.scanDirs', _.compact([].concat( + get('plugins.scanDirs'), + get('plugins.translations.scanDirs'), + opts.translationsPluginDir, + XPACK_INSTALLED && (!XPACK_OPTIONAL || !opts.oss) + ? [resolve(XPACK_INSTALLED_DIR, 'plugins')] + : [], + ))); + + set('plugins.translations.paths', _.compact([].concat( + get('plugins.translations.paths'), + opts.translationsPluginPath, + [resolve(__dirname, '..', '..', 'ui')], + ))); + merge(extraCliOptions); merge(readKeystore(get('path.data'))); diff --git a/src/core/server/plugins/plugins_config.ts b/src/core/server/plugins/plugins_config.ts index ad86581653310..b527a443dba98 100644 --- a/src/core/server/plugins/plugins_config.ts +++ b/src/core/server/plugins/plugins_config.ts @@ -27,6 +27,14 @@ const pluginsSchema = schema.object({ paths: schema.arrayOf(schema.string(), { defaultValue: [], }), + translations: schema.object({ + scanDirs: schema.arrayOf(schema.string(), { + defaultValue: [], + }), + paths: schema.arrayOf(schema.string(), { + defaultValue: [], + }), + }), }); type PluginsConfigType = TypeOf; diff --git a/src/plugin_discovery/find_plugin_specs.js b/src/plugin_discovery/find_plugin_specs.js index 29434357558b1..15d58d75121de 100644 --- a/src/plugin_discovery/find_plugin_specs.js +++ b/src/plugin_discovery/find_plugin_specs.js @@ -18,8 +18,22 @@ */ import * as Rx from 'rxjs'; -import { distinct, toArray, mergeMap, share, shareReplay, filter, last, map, tap } from 'rxjs/operators'; -import { realpathSync } from 'fs'; +import { + distinct, + toArray, + mergeMap, + share, + shareReplay, + filter, + last, + map, + tap, + combineLatest, +} from 'rxjs/operators'; +import { realpathSync, existsSync, readdirSync } from 'fs'; +import { resolve } from 'path'; +import { i18nLoader } from '@kbn/i18n'; + import { transformDeprecations, Config } from '../server/config'; @@ -32,6 +46,7 @@ import { createPack$, createPackageJsonAtPath$, createPackageJsonsInDirectory$, + createChildDirectory$, } from './plugin_pack'; import { @@ -115,7 +130,43 @@ export function findPluginSpecs(settings, configToMutate) { ...config.get('plugins.scanDirs').map(createPackageJsonsInDirectory$) )), distinct(getDistinctKeyForFindResult), - share() + ); + + const translationPath$ = config$.pipe( + mergeMap(config => + Rx.zip( + ...config.get('plugins.translations.paths').map(path => Rx.of([path])), + ...config.get('plugins.translations.scanDirs').map( + path => createChildDirectory$(path).pipe( + toArray() + ) + ) + ) + ), + map(pathArrays => [].concat(...pathArrays)), + map(paths => { + const localeFiles = []; + + for (const pluginPath of paths) { + const translationsDir = resolve(pluginPath, 'translations'); + + if (!existsSync(translationsDir)) { + continue; + } + + const translations = (readdirSync(translationsDir) || []) + .filter(filePath => filePath.endsWith('.json')) + .map(filePath => resolve(translationsDir, filePath)); + + localeFiles.push(...translations); + } + + return localeFiles; + }), + tap(i18nLoader.registerTranslationFiles), + tap(() => { + console.log(i18nLoader.getRegisteredLocales()); + }) ); const pack$ = createPack$(packageJson$).pipe( @@ -125,6 +176,7 @@ export function findPluginSpecs(settings, configToMutate) { const extendConfig$ = config$.pipe( mergeMap(config => ( pack$.pipe( + combineLatest(translationPath$, pack => pack), // get the specs for each found plugin pack mergeMap(({ pack }) => ( pack ? pack.getPluginSpecs() : [] diff --git a/src/plugin_discovery/plugin_pack/index.js b/src/plugin_discovery/plugin_pack/index.js index 69e55baee660b..9e89c0dd1711b 100644 --- a/src/plugin_discovery/plugin_pack/index.js +++ b/src/plugin_discovery/plugin_pack/index.js @@ -21,3 +21,4 @@ export { createPack$ } from './create_pack'; export { createPackageJsonAtPath$ } from './package_json_at_path'; export { createPackageJsonsInDirectory$ } from './package_jsons_in_directory'; export { PluginPack } from './plugin_pack'; +export { createChildDirectory$ } from './lib'; diff --git a/src/server/config/schema.js b/src/server/config/schema.js index ae14c03e8cfcd..ba6360ed7395c 100644 --- a/src/server/config/schema.js +++ b/src/server/config/schema.js @@ -196,7 +196,11 @@ export default () => Joi.object({ plugins: Joi.object({ paths: Joi.array().items(Joi.string()).default([]), scanDirs: Joi.array().items(Joi.string()).default([]), - initialize: Joi.boolean().default(true) + initialize: Joi.boolean().default(true), + translations: Joi.object({ + scanDirs: Joi.array().items(Joi.string()).default([]), + paths: Joi.array().items(Joi.string()).default([]) + }).default() }).default(), path: Joi.object({ From f026bcc426071fa156f1dabbc3c093c46447d354 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Fri, 23 Nov 2018 11:50:45 +0300 Subject: [PATCH 02/13] Fix i18n engine initialization --- src/core_plugins/kibana/index.js | 2 - src/core_plugins/timelion/index.js | 1 - src/plugin_discovery/find_plugin_specs.js | 47 +++----------------- src/server/i18n/index.js | 37 --------------- src/server/kbn_server.js | 2 - src/server/plugins/scan_mixin.js | 46 ++++++++++++++++++- src/ui/ui_exports/ui_export_types/index.js | 4 -- src/ui/ui_exports/ui_export_types/ui_i18n.js | 24 ---------- 8 files changed, 50 insertions(+), 113 deletions(-) delete mode 100644 src/server/i18n/index.js delete mode 100644 src/ui/ui_exports/ui_export_types/ui_i18n.js diff --git a/src/core_plugins/kibana/index.js b/src/core_plugins/kibana/index.js index c7a44ac2b04bd..d8f35fd56e2e1 100644 --- a/src/core_plugins/kibana/index.js +++ b/src/core_plugins/kibana/index.js @@ -134,8 +134,6 @@ export default function (kibana) { }; }, - translations: [], - mappings, uiSettingDefaults: getUiSettingDefaults(), }, diff --git a/src/core_plugins/timelion/index.js b/src/core_plugins/timelion/index.js index 3a1284eaacf90..5387d38789897 100644 --- a/src/core_plugins/timelion/index.js +++ b/src/core_plugins/timelion/index.js @@ -104,7 +104,6 @@ export default function (kibana) { category: ['timelion'], } }, - translations: [], }, init: require('./init.js'), }); diff --git a/src/plugin_discovery/find_plugin_specs.js b/src/plugin_discovery/find_plugin_specs.js index 15d58d75121de..3eb3c69061fc0 100644 --- a/src/plugin_discovery/find_plugin_specs.js +++ b/src/plugin_discovery/find_plugin_specs.js @@ -18,22 +18,8 @@ */ import * as Rx from 'rxjs'; -import { - distinct, - toArray, - mergeMap, - share, - shareReplay, - filter, - last, - map, - tap, - combineLatest, -} from 'rxjs/operators'; -import { realpathSync, existsSync, readdirSync } from 'fs'; -import { resolve } from 'path'; -import { i18nLoader } from '@kbn/i18n'; - +import { distinct, toArray, mergeMap, share, shareReplay, filter, last, map, tap } from 'rxjs/operators'; +import { realpathSync } from 'fs'; import { transformDeprecations, Config } from '../server/config'; @@ -130,6 +116,7 @@ export function findPluginSpecs(settings, configToMutate) { ...config.get('plugins.scanDirs').map(createPackageJsonsInDirectory$) )), distinct(getDistinctKeyForFindResult), + share() ); const translationPath$ = config$.pipe( @@ -143,30 +130,7 @@ export function findPluginSpecs(settings, configToMutate) { ) ) ), - map(pathArrays => [].concat(...pathArrays)), - map(paths => { - const localeFiles = []; - - for (const pluginPath of paths) { - const translationsDir = resolve(pluginPath, 'translations'); - - if (!existsSync(translationsDir)) { - continue; - } - - const translations = (readdirSync(translationsDir) || []) - .filter(filePath => filePath.endsWith('.json')) - .map(filePath => resolve(translationsDir, filePath)); - - localeFiles.push(...translations); - } - - return localeFiles; - }), - tap(i18nLoader.registerTranslationFiles), - tap(() => { - console.log(i18nLoader.getRegisteredLocales()); - }) + map(pathArrays => [].concat(...pathArrays)) ); const pack$ = createPack$(packageJson$).pipe( @@ -176,7 +140,6 @@ export function findPluginSpecs(settings, configToMutate) { const extendConfig$ = config$.pipe( mergeMap(config => ( pack$.pipe( - combineLatest(translationPath$, pack => pack), // get the specs for each found plugin pack mergeMap(({ pack }) => ( pack ? pack.getPluginSpecs() : [] @@ -300,6 +263,8 @@ export function findPluginSpecs(settings, configToMutate) { invalidVersionSpec$: extendConfig$.pipe( mergeMap(result => result.invalidVersionSpecs) ), + + translationPath$, }; } diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js deleted file mode 100644 index 6334f50bfaa4c..0000000000000 --- a/src/server/i18n/index.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n, i18nLoader } from '@kbn/i18n'; - -export async function i18nMixin(kbnServer, server, config) { - const { translationPaths = [] } = kbnServer.uiExports; - const locale = config.get('i18n.locale'); - - i18nLoader.registerTranslationFiles(translationPaths); - - const pureTranslations = await i18nLoader.getTranslationsByLocale(locale); - const translations = Object.freeze({ - locale, - ...pureTranslations, - }); - - i18n.init(translations); - - server.decorate('server', 'getUiTranslations', () => translations); -} diff --git a/src/server/kbn_server.js b/src/server/kbn_server.js index 977f3abf586c8..ae20cc4919446 100644 --- a/src/server/kbn_server.js +++ b/src/server/kbn_server.js @@ -41,7 +41,6 @@ import { urlShorteningMixin } from './url_shortening'; import { serverExtensionsMixin } from './server_extensions'; import { uiMixin } from '../ui'; import { sassMixin } from './sass'; -import { i18nMixin } from './i18n'; const rootDir = fromRoot('.'); @@ -83,7 +82,6 @@ export default class KbnServer { // setup this.uiExports and this.uiBundles uiMixin, - i18nMixin, indexPatternsMixin, // setup saved object routes diff --git a/src/server/plugins/scan_mixin.js b/src/server/plugins/scan_mixin.js index 8a25ad231593a..4b20032caea4d 100644 --- a/src/server/plugins/scan_mixin.js +++ b/src/server/plugins/scan_mixin.js @@ -18,11 +18,19 @@ */ import * as Rx from 'rxjs'; -import { map, distinct, toArray, tap } from 'rxjs/operators'; -import { findPluginSpecs } from '../../plugin_discovery'; +import { map, distinct, toArray, tap, mergeMap } from 'rxjs/operators'; + +import { resolve } from 'path'; +import { readdir, existsSync } from 'fs'; +import { promisify } from 'util'; +import { i18nLoader, i18n } from '@kbn/i18n'; +import { findPluginSpecs } from '../../plugin_discovery'; import { Plugin } from './lib'; + +const readdirAsync = promisify(readdir); + export async function scanMixin(kbnServer, server, config) { const { pack$, @@ -33,6 +41,7 @@ export async function scanMixin(kbnServer, server, config) { invalidVersionSpec$, spec$, disabledSpec$, + translationPath$, } = findPluginSpecs(kbnServer.settings, config); const logging$ = Rx.merge( @@ -105,6 +114,39 @@ export async function scanMixin(kbnServer, server, config) { }) ); + await translationPath$.pipe( + mergeMap(async (paths) => { + const translationPaths = []; + + for (const pluginPath of paths) { + const translationsDir = resolve(pluginPath, 'translations'); + + if (!existsSync(translationsDir)) { + continue; + } + + const translations = (await readdirAsync(translationsDir) || []) + .filter(filePath => filePath.endsWith('.json')) + .map(filePath => resolve(translationsDir, filePath)); + + translationPaths.push(...translations); + } + + i18nLoader.registerTranslationFiles(translationPaths); + + const locale = config.get('i18n.locale'); + const pureTranslations = await i18nLoader.getTranslationsByLocale(locale); + + const translations = Object.freeze({ + locale, + ...pureTranslations, + }); + + i18n.init(translations); + server.decorate('server', 'getUiTranslations', () => translations); + }) + ).toPromise(); + // await completion of enabledSpecs$, disabledSpecs$, and logging$ await Rx.merge(logging$, enabledSpecs$, disabledSpecs$).toPromise(); diff --git a/src/ui/ui_exports/ui_export_types/index.js b/src/ui/ui_exports/ui_export_types/index.js index fa7847fccbf2a..c12c445540efd 100644 --- a/src/ui/ui_exports/ui_export_types/index.js +++ b/src/ui/ui_exports/ui_export_types/index.js @@ -61,10 +61,6 @@ export { shareContextMenuExtensions, } from './ui_app_extensions'; -export { - translations, -} from './ui_i18n'; - export { link, links, diff --git a/src/ui/ui_exports/ui_export_types/ui_i18n.js b/src/ui/ui_exports/ui_export_types/ui_i18n.js deleted file mode 100644 index 01c85b6883025..0000000000000 --- a/src/ui/ui_exports/ui_export_types/ui_i18n.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { flatConcatAtType } from './reduce'; -import { wrap, alias } from './modify_reduce'; - -// paths to translation files -export const translations = wrap(alias('translationPaths'), flatConcatAtType); From 075a05eed0bf05a4270e7e4df364d760052fab0e Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Mon, 26 Nov 2018 11:40:58 +0300 Subject: [PATCH 03/13] Fix translationPath$ RxJS pipeline --- src/plugin_discovery/find_plugin_specs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin_discovery/find_plugin_specs.js b/src/plugin_discovery/find_plugin_specs.js index 3eb3c69061fc0..bc474b586d7dd 100644 --- a/src/plugin_discovery/find_plugin_specs.js +++ b/src/plugin_discovery/find_plugin_specs.js @@ -122,7 +122,7 @@ export function findPluginSpecs(settings, configToMutate) { const translationPath$ = config$.pipe( mergeMap(config => Rx.zip( - ...config.get('plugins.translations.paths').map(path => Rx.of([path])), + Rx.of(config.get('plugins.translations.paths')), ...config.get('plugins.translations.scanDirs').map( path => createChildDirectory$(path).pipe( toArray() From e95133c3931041032bb2330bc66fc5d983ce1417 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 28 Nov 2018 15:43:08 +0300 Subject: [PATCH 04/13] Move translations registration to mixin --- src/cli/serve/serve.js | 15 ++----- src/core/server/plugins/plugins_config.ts | 8 ---- src/plugin_discovery/find_plugin_specs.js | 17 -------- src/plugin_discovery/plugin_pack/index.js | 1 - src/server/config/schema.js | 7 +--- src/server/i18n/index.js | 49 +++++++++++++++++++++++ src/server/kbn_server.js | 4 ++ src/server/plugins/scan_mixin.js | 46 +-------------------- 8 files changed, 60 insertions(+), 87 deletions(-) create mode 100644 src/server/i18n/index.js diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 19f432c02d72e..de1386fb61ed8 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -132,19 +132,10 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { : [], ))); - set('plugins.translations.scanDirs', _.compact([].concat( + set('i18n.translationsScanDirs', _.compact([].concat( get('plugins.scanDirs'), - get('plugins.translations.scanDirs'), - opts.translationsPluginDir, - XPACK_INSTALLED && (!XPACK_OPTIONAL || !opts.oss) - ? [resolve(XPACK_INSTALLED_DIR, 'plugins')] - : [], - ))); - - set('plugins.translations.paths', _.compact([].concat( - get('plugins.translations.paths'), - opts.translationsPluginPath, - [resolve(__dirname, '..', '..', 'ui')], + get('plugins.paths'), + resolve(__dirname, '..', '..', 'ui'), ))); merge(extraCliOptions); diff --git a/src/core/server/plugins/plugins_config.ts b/src/core/server/plugins/plugins_config.ts index b527a443dba98..ad86581653310 100644 --- a/src/core/server/plugins/plugins_config.ts +++ b/src/core/server/plugins/plugins_config.ts @@ -27,14 +27,6 @@ const pluginsSchema = schema.object({ paths: schema.arrayOf(schema.string(), { defaultValue: [], }), - translations: schema.object({ - scanDirs: schema.arrayOf(schema.string(), { - defaultValue: [], - }), - paths: schema.arrayOf(schema.string(), { - defaultValue: [], - }), - }), }); type PluginsConfigType = TypeOf; diff --git a/src/plugin_discovery/find_plugin_specs.js b/src/plugin_discovery/find_plugin_specs.js index bc474b586d7dd..29434357558b1 100644 --- a/src/plugin_discovery/find_plugin_specs.js +++ b/src/plugin_discovery/find_plugin_specs.js @@ -32,7 +32,6 @@ import { createPack$, createPackageJsonAtPath$, createPackageJsonsInDirectory$, - createChildDirectory$, } from './plugin_pack'; import { @@ -119,20 +118,6 @@ export function findPluginSpecs(settings, configToMutate) { share() ); - const translationPath$ = config$.pipe( - mergeMap(config => - Rx.zip( - Rx.of(config.get('plugins.translations.paths')), - ...config.get('plugins.translations.scanDirs').map( - path => createChildDirectory$(path).pipe( - toArray() - ) - ) - ) - ), - map(pathArrays => [].concat(...pathArrays)) - ); - const pack$ = createPack$(packageJson$).pipe( share() ); @@ -263,8 +248,6 @@ export function findPluginSpecs(settings, configToMutate) { invalidVersionSpec$: extendConfig$.pipe( mergeMap(result => result.invalidVersionSpecs) ), - - translationPath$, }; } diff --git a/src/plugin_discovery/plugin_pack/index.js b/src/plugin_discovery/plugin_pack/index.js index 9e89c0dd1711b..69e55baee660b 100644 --- a/src/plugin_discovery/plugin_pack/index.js +++ b/src/plugin_discovery/plugin_pack/index.js @@ -21,4 +21,3 @@ export { createPack$ } from './create_pack'; export { createPackageJsonAtPath$ } from './package_json_at_path'; export { createPackageJsonsInDirectory$ } from './package_jsons_in_directory'; export { PluginPack } from './plugin_pack'; -export { createChildDirectory$ } from './lib'; diff --git a/src/server/config/schema.js b/src/server/config/schema.js index ba6360ed7395c..0dc6e17084a1d 100644 --- a/src/server/config/schema.js +++ b/src/server/config/schema.js @@ -196,11 +196,7 @@ export default () => Joi.object({ plugins: Joi.object({ paths: Joi.array().items(Joi.string()).default([]), scanDirs: Joi.array().items(Joi.string()).default([]), - initialize: Joi.boolean().default(true), - translations: Joi.object({ - scanDirs: Joi.array().items(Joi.string()).default([]), - paths: Joi.array().items(Joi.string()).default([]) - }).default() + initialize: Joi.boolean().default(true) }).default(), path: Joi.object({ @@ -263,6 +259,7 @@ export default () => Joi.object({ i18n: Joi.object({ locale: Joi.string().default('en'), + translationsScanDirs: Joi.array().items(Joi.string()).default([]), }).default(), }).default(); diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js new file mode 100644 index 0000000000000..0c941d7707705 --- /dev/null +++ b/src/server/i18n/index.js @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import glob from 'glob'; +import { promisify } from 'util'; +import { i18n, i18nLoader } from '@kbn/i18n'; + +const globAsync = promisify(glob); + +export async function i18nMixin(kbnServer, server, config) { + const locale = config.get('i18n.locale'); + + const groupedEntries = await Promise.all(config.get('i18n.translationsScanDirs').map(path => + globAsync('**/translations/*.json', { + cwd: path, + ignore: ['**/node_modules/**', '**/__tests__/**'], + }) + )); + + const translationPaths = [].concat(groupedEntries); + + i18nLoader.registerTranslationFiles(translationPaths); + + const pureTranslations = await i18nLoader.getTranslationsByLocale(locale); + const translations = Object.freeze({ + locale, + ...pureTranslations, + }); + + i18n.init(translations); + + server.decorate('server', 'getUiTranslations', () => translations); +} diff --git a/src/server/kbn_server.js b/src/server/kbn_server.js index ae20cc4919446..da63771112bd7 100644 --- a/src/server/kbn_server.js +++ b/src/server/kbn_server.js @@ -41,6 +41,7 @@ import { urlShorteningMixin } from './url_shortening'; import { serverExtensionsMixin } from './server_extensions'; import { uiMixin } from '../ui'; import { sassMixin } from './sass'; +import { i18nMixin } from './i18n'; const rootDir = fromRoot('.'); @@ -74,6 +75,9 @@ export default class KbnServer { // writes pid file pidMixin, + // scan translations dirs, register locale files, initialize i18n engine and define `server.getUiTranslations` + i18nMixin, + // find plugins and set this.plugins and this.pluginSpecs Plugins.scanMixin, diff --git a/src/server/plugins/scan_mixin.js b/src/server/plugins/scan_mixin.js index 4b20032caea4d..8a25ad231593a 100644 --- a/src/server/plugins/scan_mixin.js +++ b/src/server/plugins/scan_mixin.js @@ -18,18 +18,10 @@ */ import * as Rx from 'rxjs'; -import { map, distinct, toArray, tap, mergeMap } from 'rxjs/operators'; - -import { resolve } from 'path'; -import { readdir, existsSync } from 'fs'; -import { promisify } from 'util'; -import { i18nLoader, i18n } from '@kbn/i18n'; - +import { map, distinct, toArray, tap } from 'rxjs/operators'; import { findPluginSpecs } from '../../plugin_discovery'; -import { Plugin } from './lib'; - -const readdirAsync = promisify(readdir); +import { Plugin } from './lib'; export async function scanMixin(kbnServer, server, config) { const { @@ -41,7 +33,6 @@ export async function scanMixin(kbnServer, server, config) { invalidVersionSpec$, spec$, disabledSpec$, - translationPath$, } = findPluginSpecs(kbnServer.settings, config); const logging$ = Rx.merge( @@ -114,39 +105,6 @@ export async function scanMixin(kbnServer, server, config) { }) ); - await translationPath$.pipe( - mergeMap(async (paths) => { - const translationPaths = []; - - for (const pluginPath of paths) { - const translationsDir = resolve(pluginPath, 'translations'); - - if (!existsSync(translationsDir)) { - continue; - } - - const translations = (await readdirAsync(translationsDir) || []) - .filter(filePath => filePath.endsWith('.json')) - .map(filePath => resolve(translationsDir, filePath)); - - translationPaths.push(...translations); - } - - i18nLoader.registerTranslationFiles(translationPaths); - - const locale = config.get('i18n.locale'); - const pureTranslations = await i18nLoader.getTranslationsByLocale(locale); - - const translations = Object.freeze({ - locale, - ...pureTranslations, - }); - - i18n.init(translations); - server.decorate('server', 'getUiTranslations', () => translations); - }) - ).toPromise(); - // await completion of enabledSpecs$, disabledSpecs$, and logging$ await Rx.merge(logging$, enabledSpecs$, disabledSpecs$).toPromise(); From c467eb22df641bf3fe40d9b57782fda09220de16 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 28 Nov 2018 16:00:05 +0300 Subject: [PATCH 05/13] Fix arrays concatenation --- src/server/i18n/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index 0c941d7707705..ee42fa83fcabf 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -33,7 +33,7 @@ export async function i18nMixin(kbnServer, server, config) { }) )); - const translationPaths = [].concat(groupedEntries); + const translationPaths = [].concat(...groupedEntries); i18nLoader.registerTranslationFiles(translationPaths); From dadeceee572265ecebeb8305122be5dbe1ba36c9 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 28 Nov 2018 16:01:55 +0300 Subject: [PATCH 06/13] Use prettier --- src/server/i18n/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index ee42fa83fcabf..90778a2f04c25 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -26,12 +26,14 @@ const globAsync = promisify(glob); export async function i18nMixin(kbnServer, server, config) { const locale = config.get('i18n.locale'); - const groupedEntries = await Promise.all(config.get('i18n.translationsScanDirs').map(path => - globAsync('**/translations/*.json', { - cwd: path, - ignore: ['**/node_modules/**', '**/__tests__/**'], - }) - )); + const groupedEntries = await Promise.all( + config.get('i18n.translationsScanDirs').map(path => + globAsync('**/translations/*.json', { + cwd: path, + ignore: ['**/node_modules/**', '**/__tests__/**'], + }) + ) + ); const translationPaths = [].concat(...groupedEntries); From 2fbe32e1cf24341b63c7323503a6fec8e6dc6300 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 28 Nov 2018 16:48:36 +0300 Subject: [PATCH 07/13] Fix translations relative paths --- src/server/i18n/index.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index 90778a2f04c25..631a5cae02874 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -17,6 +17,7 @@ * under the License. */ +import { resolve } from 'path'; import glob from 'glob'; import { promisify } from 'util'; import { i18n, i18nLoader } from '@kbn/i18n'; @@ -27,12 +28,14 @@ export async function i18nMixin(kbnServer, server, config) { const locale = config.get('i18n.locale'); const groupedEntries = await Promise.all( - config.get('i18n.translationsScanDirs').map(path => - globAsync('**/translations/*.json', { + config.get('i18n.translationsScanDirs').map(async path => { + const entries = await globAsync('**/translations/*.json', { cwd: path, ignore: ['**/node_modules/**', '**/__tests__/**'], - }) - ) + }); + + return entries.map(entry => resolve(path, entry)); + }) ); const translationPaths = [].concat(...groupedEntries); From 8afe95b1dd2c0c13d8c76afdc1dfd6e6e19ccf99 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Thu, 29 Nov 2018 13:17:52 +0300 Subject: [PATCH 08/13] Use globby instead of glob --- src/server/i18n/index.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index 631a5cae02874..3ba97c336b183 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -18,21 +18,20 @@ */ import { resolve } from 'path'; -import glob from 'glob'; -import { promisify } from 'util'; +import globby from 'globby'; import { i18n, i18nLoader } from '@kbn/i18n'; -const globAsync = promisify(glob); - export async function i18nMixin(kbnServer, server, config) { const locale = config.get('i18n.locale'); const groupedEntries = await Promise.all( config.get('i18n.translationsScanDirs').map(async path => { - const entries = await globAsync('**/translations/*.json', { - cwd: path, - ignore: ['**/node_modules/**', '**/__tests__/**'], - }); + const entries = await globby( + ['translations/*.json', '*/translations/*.json', '*/plugins/*/translations/*.json'], + { + cwd: path, + } + ); return entries.map(entry => resolve(path, entry)); }) From 11f8ca2dbf4917bc2ad7b82254057850b301747b Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Thu, 29 Nov 2018 15:26:04 +0300 Subject: [PATCH 09/13] Update docs --- packages/kbn-i18n/README.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/kbn-i18n/README.md b/packages/kbn-i18n/README.md index cff7fbe98ad44..d4ac457cba5b4 100644 --- a/packages/kbn-i18n/README.md +++ b/packages/kbn-i18n/README.md @@ -56,21 +56,7 @@ For example: src/core_plugins/kibana/translations/fr.json ``` -When a new translation file is added, you have to register this file into -`uiExports.translations` array of plugin constructor parameters. For example: -```js -export default function (kibana) { - return new kibana.Plugin({ - uiExports: { - translations: [ - resolve(__dirname, './translations/fr.json'), - ], - ... - }, - ... - }); -} -``` +The engine scans `x-pack/plugins/*/translations`, `src/core_plugins/*/translations`, `plugins/*/translations` and `src/ui/translations` folders on initialization, so there is no need to register translation files. The engine uses a `config/kibana.yml` file for locale resolution process. If locale is defined via `i18n.locale` option in `config/kibana.yml` then it will be used as a base From 4c7e4586270ca86aba743c361b8c7ac8ed6de370 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Mon, 3 Dec 2018 10:48:23 +0300 Subject: [PATCH 10/13] Move globby to dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a87bf60eb8e8..d3f6f3487607d 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "getos": "^3.1.0", "glob": "^7.1.2", "glob-all": "^3.1.0", + "globby": "^8.0.1", "good-squeeze": "2.1.0", "h2o2": "^8.1.2", "handlebars": "4.0.5", @@ -335,7 +336,6 @@ "fetch-mock": "^5.13.1", "geckodriver": "1.12.2", "getopts": "2.0.0", - "globby": "^8.0.1", "grunt": "1.0.1", "grunt-cli": "^1.2.0", "grunt-contrib-watch": "^1.1.0", From 760703f80d6f24e104bfeb822121d7571016b062 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 5 Dec 2018 13:04:48 +0300 Subject: [PATCH 11/13] Get rid of translation directories config --- src/cli/serve/serve.js | 6 ------ src/server/config/schema.js | 1 - src/server/i18n/index.js | 34 ++++++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 63aa7167781ae..085e9d0e4882b 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -132,12 +132,6 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { : [], ))); - set('i18n.translationsScanDirs', _.compact([].concat( - get('plugins.scanDirs'), - get('plugins.paths'), - resolve(__dirname, '..', '..', 'ui'), - ))); - merge(extraCliOptions); merge(readKeystore(get('path.data'))); diff --git a/src/server/config/schema.js b/src/server/config/schema.js index 0dc6e17084a1d..ae14c03e8cfcd 100644 --- a/src/server/config/schema.js +++ b/src/server/config/schema.js @@ -259,7 +259,6 @@ export default () => Joi.object({ i18n: Joi.object({ locale: Joi.string().default('en'), - translationsScanDirs: Joi.array().items(Joi.string()).default([]), }).default(), }).default(); diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index 3ba97c336b183..f97fd181ad120 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -21,21 +21,35 @@ import { resolve } from 'path'; import globby from 'globby'; import { i18n, i18nLoader } from '@kbn/i18n'; +import { fromRoot } from '../../utils'; + export async function i18nMixin(kbnServer, server, config) { const locale = config.get('i18n.locale'); - const groupedEntries = await Promise.all( - config.get('i18n.translationsScanDirs').map(async path => { - const entries = await globby( - ['translations/*.json', '*/translations/*.json', '*/plugins/*/translations/*.json'], - { - cwd: path, - } - ); + const translationsDirs = [fromRoot('src/ui/translations')]; + + const groupedEntries = await Promise.all([ + ...config.get('plugins.scanDirs').map(async path => { + const entries = await globby('*/translations/*.json', { + cwd: path, + }); + return entries.map(entry => resolve(path, entry)); + }), + + ...config.get('plugins.paths').map(async path => { + const entries = await globby('plugins/*/translations/*.json', { + cwd: path, + }); + return entries.map(entry => resolve(path, entry)); + }), + ...translationsDirs.map(async path => { + const entries = await globby('*.json', { + cwd: path, + }); return entries.map(entry => resolve(path, entry)); - }) - ); + }), + ]); const translationPaths = [].concat(...groupedEntries); From cd9a5a1a900b1f9460905fa5ff8ff1de821aa19e Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Tue, 11 Dec 2018 12:00:58 +0300 Subject: [PATCH 12/13] Update globby patterns --- src/server/i18n/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index f97fd181ad120..460d2ff3767a0 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -37,7 +37,7 @@ export async function i18nMixin(kbnServer, server, config) { }), ...config.get('plugins.paths').map(async path => { - const entries = await globby('plugins/*/translations/*.json', { + const entries = await globby(['translations/*.json', 'plugins/*/translations/*.json'], { cwd: path, }); return entries.map(entry => resolve(path, entry)); From df16137e7f08fba7e60a55197e53ec806ea8fa69 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Tue, 11 Dec 2018 15:09:40 +0300 Subject: [PATCH 13/13] Search only for current locale translation files --- src/server/i18n/index.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/server/i18n/index.js b/src/server/i18n/index.js index 460d2ff3767a0..d010c5a8b5678 100644 --- a/src/server/i18n/index.js +++ b/src/server/i18n/index.js @@ -30,21 +30,24 @@ export async function i18nMixin(kbnServer, server, config) { const groupedEntries = await Promise.all([ ...config.get('plugins.scanDirs').map(async path => { - const entries = await globby('*/translations/*.json', { + const entries = await globby(`*/translations/${locale}.json`, { cwd: path, }); return entries.map(entry => resolve(path, entry)); }), ...config.get('plugins.paths').map(async path => { - const entries = await globby(['translations/*.json', 'plugins/*/translations/*.json'], { - cwd: path, - }); + const entries = await globby( + [`translations/${locale}.json`, `plugins/*/translations/${locale}.json`], + { + cwd: path, + } + ); return entries.map(entry => resolve(path, entry)); }), ...translationsDirs.map(async path => { - const entries = await globby('*.json', { + const entries = await globby(`${locale}.json`, { cwd: path, }); return entries.map(entry => resolve(path, entry));