From 8a5d867cf531ee89c6d0b1f2698c7ec22c63a113 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Mon, 20 Mar 2017 18:09:06 +0000 Subject: [PATCH] backport from Kibana Globalization - Phase 2 (#8766) SHA#3aa5938daaefc30e21bdd18d1ce2263923cfe12a * Integrate angular-translate globalization framework with i18n engine * Provide template for enabling translations in an AngularJS view by translating a view * Verification tool for translation keys used in angular-translate * Documentation # Conflicts: # src/core_plugins/kibana/public/management/sections/indices/_create.html # src/ui/public/chrome/chrome.js # tasks/build/verify_translations.js # tasks/utils/i18n_verify_keys.js --- .../development-internationalization.asciidoc | 187 ++++++++++++++++++ package.json | 2 + .../management/sections/indices/_create.html | 127 ++++++++---- .../management/sections/indices/_create.js | 18 +- .../management/sections/indices/index.html | 10 +- src/core_plugins/kibana/translations/en.json | 54 ++++- src/ui/index.js | 2 + src/ui/public/chrome/api/translations.js | 15 ++ src/ui/public/chrome/chrome.js | 25 ++- src/ui/public/debounce/__tests__/debounce.js | 3 + src/ui/ui_i18n.js | 4 +- tasks/build/verify_translations.js | 44 ++++- tasks/config/i18nextract.js | 9 + tasks/utils/i18n_verify_keys.js | 15 +- webpackShims/angular.js | 3 +- webpackShims/ui-bootstrap.js | 10 +- 16 files changed, 449 insertions(+), 79 deletions(-) create mode 100644 docs/development/plugin/development-internationalization.asciidoc create mode 100644 src/ui/public/chrome/api/translations.js create mode 100644 tasks/config/i18nextract.js diff --git a/docs/development/plugin/development-internationalization.asciidoc b/docs/development/plugin/development-internationalization.asciidoc new file mode 100644 index 00000000000000..25501878288386 --- /dev/null +++ b/docs/development/plugin/development-internationalization.asciidoc @@ -0,0 +1,187 @@ +[[translating-kibana]] +Translating Kibana +------------------ + +[[background]] +Background +~~~~~~~~~ + +Please see https://github.com/elastic/kibana/issues/6515[kibana#6515] +for background discussion on the Kibana translation work. + +[[prerequisites]] +Prerequisites +~~~~~~~~~~~~ + +Kibana must be installed and operational, see README.md + +[[audience]] +Audience +~~~~~~~ + +There are three audiences for this document: + +* those that want to contribute language packs to Kibana +* those that want to enable translations in existing Kibana plugins +* those that want to create a new Kibana Plugin + +[[contributing-language-packs]] +Contributing Language Packs +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this example, we will demonstrate translation into Maltese (Language +code `mt`). Add-on functionality for Kibana is implemented with plug-in modules. +Refer to +https://www.elastic.co/guide/en/kibana/current/kibana-plugins.html[Kibana +Plugins] for more details. + +* Fork the `kibana` source, and ensure you have an up to date copy of +the source. +* Ensure you have signed the agreement as in CONTRIBUTING.md +* Choose the right link:[bcp47] language code for your work. In this +example, we will use the `kibana` plugin for translating and `mt` for +Maltese. Other examples might be `zh-Hans` for Chinese using Simplified +characters, or `az-Cyrl` for Azerbaijani using Cyrillic characters. The +following links can help: +* https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes[List of ISO +639-1 codes] +* +http://cldr.unicode.org/index/cldr-spec/picking-the-right-language-code[“Picking +the right language code”] +* Create a new branch for your work: ++ +git checkout -b translate-mt +* For each translation scope (see link:#Enabling%20Translations%20on%20Existing%20Plugins[Enabling Translations on Existing Plugins], below), generate a Kibana plugin with name _plugin_-_languagecode_ using the https://github.com/elastic/generator-kibana-plugin[Kibana Plugin Yeoman Generator]: ++ +* Replace the the `es.json` translation file with _languagecode_`.json`: +`mv src/plugins/kibana-mt/translations/es.json src/plugins/kibana-mt/translations/mt.json` +* Translate the `mt.json` file in a JSON editor +* Edit index file (`kibana-mt/index.js`), updating the +'translations' item in 'uiExports' as per your plugin translation file(s) +* Copy translations plugin (`kibana-mt/`) to the Kibana plugins (`/plugins`) directory +* Start up Kibana and verify the translation works as expected +* Ensure Kibana tests pass +* Commit the `kibana-mt` directory and files, and push them to your own +fork of `kibana` +* Open a pull request titled `Translation: Maltese (mt)` + +[[enabling-ranslations-on-existing-plugins]] +Enabling Translations on Existing Plugins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Kibana translates according to plugin scope, so there is a `.json` file +in `translations` subdirectory for each plugin. + +[[enabling-translation-of-an-angular-view]] +Enabling Translation of an Angular view +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Determine which views share a plugin scope. In this example, `create` +and `edit` will share scope. +* If it does not already exist, Create the appropriate `translation` +directory and the new translation file `en.json` inside it. In the above +example, it would be: `src/core_plugins/kibana/translations/en.json` +* If it does not exist add 'translations' item to the 'uiExports' in the plugin creation (`src/core_plugins/kibana/translations/en.json`) as follows: +------------------------------------------------------------------------- +uiExports { + translations: [ + resolve(__dirname, './translations/en.json') + ], …. +} +------------------------------------------------------------------------- + +* In the view (HTML) file, such as +`src/core_plugins/kibana/public/management/sections/indices/_create.html` +Replace English text with translation keys, and copy the English text +into the `en.json` file appropriately. Note that loose text was put into +a `

` tag for translation purposes. Also note the prefix `KIBANA-` +matching the plugin being translated. + +[[before]] +Before +++++++ + +`_create.html` + +----------------------------------------------------------------------------------------------------- +

Configure an index pattern

+ In order to use Kibana you must configure at least one index pattern… + + +----------------------------------------------------------------------------------------------------- + +[[after]] +After ++++++ + +`_create.html` + +------------------------------------------------------------------------------------------- +

+

+ + +------------------------------------------------------------------------------------------- + +* In the view (JS) file, such as +`src/core_plugins/kibana/public/management/sections/indices/_create.js` +As above, replace English text with translation keys, and copy the English text +into the `en.json` file appropriately. Note that some strings may not be user facing +and do not need to be replaced then. Also note the prefix `KIBANA-` matching the plugin +being translated. + +[[before]] +Before +++++++ + +`_create.js` + +-------------------------------------------------------------------------------------------------------------- + notify.error('Could not locate any indices matching that pattern. Please add the index to Elasticsearch'); +-------------------------------------------------------------------------------------------------------------- + +[[after]] +After ++++++ + +`_create.js` + +------------------------------------------------------------------------------------------- + notify.error($translate.instant('KIBANA-NO_INDICES_MATCHING_PATTERN')); +------------------------------------------------------------------------------------------- + +`en.json` + +----------------------------------------------------------------------------------------------------------------------------------------- + { + "KIBANA-CONFIGURE_INDEX_PATTERN": "Configure an index pattern", + "KIBANA-MUST_CONFIGURE_INDEX_PATTERN": "In order to use Kibana you must…", + "KIBANA-FIELD_FILTER_EVENTS_GLOBAL_TIME" : "This field will be used to filter events with the global time filter", + "KIBANA-NO_INDICES_MATCHING_PATTERN": "Could not locate any indices matching that pattern. Please add the index to Elasticsearch", + } +----------------------------------------------------------------------------------------------------------------------------------------- + +* Refresh the Kibana page and verify the UI looks the same. +* Refer to Kibana core plugin (`src/core_plugins/kibana/`) for examples. + +[[new-plugin-authors]] +New Plugin Authors +~~~~~~~~~~~~~~~~~ + +Add-on functionality for Kibana is implemented with plug-in modules. +Refer to +https://www.elastic.co/guide/en/kibana/current/kibana-plugins.html[Kibana +Plugins] for more details. It is recommended that when creating a plugin +you enable translations (see link:#Enabling%20Translations%20on%20Existing%20Plugins[Enabling Translations on Existing Plugins], above). + +[[enabling-translation-in-a-plugin]] +Enabling Translation in a New Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Generate a Kibana plugin using the https://github.com/elastic/generator-kibana-plugin[Kibana Plugin Yeoman Generator]. In this +example, `plugin1` +* Add the translation IDs to the views +* Add the corresponding translation IDs and text to the default translation file (`translations/en.json`) +* Refer to link:#Enabling%20Translations%20on%20Existing%20Plugins[Enabling Translations on Existing Plugins] for more +details on enabling translation in your plugin views and refer to Kibana +core plugin (`src/core_plugins/kibana/`) for an example. diff --git a/package.json b/package.json index 2ce1433fa27222..aac74d640c2fa4 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "angular-route": "1.4.7", "angular-sanitize": "1.5.7", "angular-sortable-view": "0.0.15", + "angular-translate": "2.13.1", "ansicolors": "0.3.2", "autoprefixer": "6.5.4", "autoprefixer-loader": "2.0.0", @@ -185,6 +186,7 @@ "expect.js": "0.3.1", "faker": "1.1.0", "grunt": "1.0.1", + "grunt-angular-translate": "0.3.0", "grunt-aws-s3": "0.14.5", "grunt-babel": "5.0.1", "grunt-cli": "0.1.13", diff --git a/src/core_plugins/kibana/public/management/sections/indices/_create.html b/src/core_plugins/kibana/public/management/sections/indices/_create.html index 53e6e5f7c5a393..e0585283cc168d 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/_create.html +++ b/src/core_plugins/kibana/public/management/sections/indices/_create.html @@ -2,10 +2,8 @@

@@ -51,12 +49,10 @@

Time-interval based index patterns are deprecated!

- -

Patterns allow you to define dynamic index names using * as a wildcard. Example: logstash-*

-

Patterns allow you to define dynamic index names. Static text in an index name is denoted using brackets. Example: [logstash-]YYYY.MM.DD. Please note that weeks are setup to use ISO weeks which start on Monday. — - Date Format Documentation

+ +

+

— +

Time-interval based index patterns are deprecated!

type="text" class="form-control"> - Note: - I noticed you're using weekly indices. Kibana requires ISO weeks be used in your index creation. - See Wikipedia: ISO Week Date +   + + + +
+ +
+ +
+ + +
+
- This index pattern will be queried directly rather than being - expanded into more performant searches against individual indices. + - Elasticsearch will receive a query against {{index.name}} - and will have to search through all matching indices regardless - of whether they have data that matches the current time range. + + +
-

- By default, searches against any time-based index pattern that - contains a wildcard will automatically be expanded to query only - the indices that contain data within the currently selected time - range. -

+

- Searching against the index pattern logstash-* will - actually query elasticsearch for the specific matching indices - (e.g. logstash-2015.12.21) that fall within the current - time range. + + + + +

+
+ +
+ +
+
+

+

+

+ + + +

+
+ + +
+
{{err}}
- Attempted to match the following indices and aliases: +
  • {{sample}}
- Pattern matches {{index.existing.matchPercent}} of existing indices and aliases +
  • {{match}}
@@ -129,12 +181,12 @@

Time-interval based index patterns are deprecated!

ng-click="moreSamples()" type="button" class="btn btn-default"> - Expand Search +
- Indices and aliases that were found, but did not match the pattern: +
  • {{match}}
@@ -142,7 +194,7 @@

Time-interval based index patterns are deprecated!

ng-if="index.sampleCount < index.existing.matches.length" ng-click="moreSamples()" class="alert-link"> - more +
@@ -173,8 +225,9 @@

Time-interval based index patterns are deprecated!

ng-class="index.fetchFieldsError ? 'btn-default' : 'btn-success'" type="submit" class="btn"> - {{index.fetchFieldsError || "Create" }} - Invalid index name pattern. + {{index.fetchFieldsError}} + + diff --git a/src/core_plugins/kibana/public/management/sections/indices/_create.js b/src/core_plugins/kibana/public/management/sections/indices/_create.js index 050187efdeb2a6..1f5fd2bf62a519 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/_create.js +++ b/src/core_plugins/kibana/public/management/sections/indices/_create.js @@ -14,7 +14,7 @@ uiRoutes }); uiModules.get('apps/management') -.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise) { +.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise, $translate) { const notify = new Notifier(); const refreshKibanaIndex = Private(RefreshKibanaIndex); const intervals = indexPatterns.intervals; @@ -30,7 +30,7 @@ uiModules.get('apps/management') sampleCount: 5, nameIntervalOptions: intervals, - fetchFieldsError: 'Loading' + fetchFieldsError: $translate.instant('KIBANA-LOADING') }; index.nameInterval = _.find(index.nameIntervalOptions, { name: 'daily' }); @@ -89,7 +89,7 @@ uiModules.get('apps/management') }) .catch(function (err) { if (err instanceof IndexPatternMissingIndices) { - notify.error('Could not locate any indices matching that pattern. Please add the index to Elasticsearch'); + notify.error($translate.instant('KIBANA-NO_INDICES_MATCHING_PATTERN')); } else notify.fatal(err); }); @@ -193,12 +193,12 @@ uiModules.get('apps/management') return; } - patternErrors.push('Pattern does not match any existing indices'); + patternErrors.push($translate.instant('KIBANA-PATTERN_DOES_NOT_MATCH_EXIST_INDICES')); const radius = Math.round(index.sampleCount / 2); const samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius); if (_.uniq(samples).length !== samples.length) { - patternErrors.push('Invalid pattern, interval does not create unique index names'); + patternErrors.push($translate.instant('KIBANA-INVALID_NON_UNIQUE_INDEX_NAME_CREATED')); } else { index.samples = samples; } @@ -215,12 +215,12 @@ uiModules.get('apps/management') // we don't have enough info to continue if (!index.name) { - fetchFieldsError = 'Set an index name first'; + fetchFieldsError = $translate.instant('KIBANA-SET_INDEX_NAME_FIRST'); return; } if (useIndexList && !index.nameInterval) { - fetchFieldsError = 'Select the interval at which your indices are populated.'; + fetchFieldsError = $translate.instant('KIBANA-INTERVAL_INDICES_POPULATED'); return; } @@ -232,7 +232,7 @@ uiModules.get('apps/management') .catch(function (err) { // TODO: we should probably display a message of some kind if (err instanceof IndexPatternMissingIndices) { - fetchFieldsError = 'Unable to fetch mapping. Do you have indices matching the pattern?'; + fetchFieldsError = $translate.instant('KIBANA-INDICES_MATCH_PATTERN'); return []; } @@ -288,7 +288,7 @@ uiModules.get('apps/management') index.patternErrors = []; index.samples = null; index.existing = null; - index.fetchFieldsError = 'Loading'; + index.fetchFieldsError = $translate.instant('KIBANA-LOADING'); } function getPatternDefault(interval) { diff --git a/src/core_plugins/kibana/public/management/sections/indices/index.html b/src/core_plugins/kibana/public/management/sections/indices/index.html index 56c30b16e5d0d0..7dbde5849a3460 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/index.html +++ b/src/core_plugins/kibana/public/management/sections/indices/index.html @@ -6,9 +6,10 @@
ng-if="editingId" href="#/management/kibana/index" class="btn btn-primary btn-xs" - aria-label="Add New"> - Add New - Add New + aria-label="{{ 'KIBANA-ADD_NEW' | translate }}"> + + +
@@ -17,7 +18,8 @@
ng-if="!defaultIndex" class="sidebar-item">
  • { async function getKibanaPayload({ app, request, includeUserProvidedConfig }) { const uiSettings = server.uiSettings(); + const translations = await uiI18n.getTranslationsForRequest(request); return { app: app, @@ -76,6 +77,7 @@ export default async (kbnServer, server, config) => { basePath: config.get('server.basePath'), serverName: config.get('server.name'), devMode: config.get('env.dev'), + translations: translations, uiSettings: await props({ defaults: uiSettings.getDefaults(), user: includeUserProvidedConfig && uiSettings.getUserProvided(request) diff --git a/src/ui/public/chrome/api/translations.js b/src/ui/public/chrome/api/translations.js new file mode 100644 index 00000000000000..6d8455fa84c32d --- /dev/null +++ b/src/ui/public/chrome/api/translations.js @@ -0,0 +1,15 @@ +module.exports = function (chrome, internals) { + /** + * ui/chrome Translations API + * + * Translations + * Returns the translations which have been loaded by the Kibana server instance + */ + + /** + * @return {Object} - Translations + */ + chrome.getTranslations = function () { + return internals.translations || []; + }; +}; diff --git a/src/ui/public/chrome/chrome.js b/src/ui/public/chrome/chrome.js index 81aef82f654f13..26d8e9dc0c8815 100644 --- a/src/ui/public/chrome/chrome.js +++ b/src/ui/public/chrome/chrome.js @@ -11,6 +11,14 @@ import 'ui/storage'; import 'ui/directives/kbn_src'; import 'ui/watch_multi'; import './services'; +import angularApi from './api/angular'; +import appsApi from './api/apps'; +import controlsApi from './api/controls'; +import navApi from './api/nav'; +import templateApi from './api/template'; +import themeApi from './api/theme'; +import translationsApi from './api/translations'; +import xsrfApi from './api/xsrf'; const chrome = {}; const internals = _.defaults( @@ -28,13 +36,16 @@ const internals = _.defaults( } ); -require('./api/apps')(chrome, internals); -require('./api/xsrf')(chrome, internals); -require('./api/nav')(chrome, internals); -require('./api/angular')(chrome, internals); -require('./api/controls')(chrome, internals); -require('./api/template')(chrome, internals); -require('./api/theme')(chrome, internals); + +appsApi(chrome, internals); +xsrfApi(chrome, internals); +navApi(chrome, internals); +angularApi(chrome, internals); +controlsApi(chrome, internals); +templateApi(chrome, internals); +themeApi(chrome, internals); +translationsApi(chrome, internals); + chrome.bootstrap = function () { chrome.setupAngular(); diff --git a/src/ui/public/debounce/__tests__/debounce.js b/src/ui/public/debounce/__tests__/debounce.js index 5208365c38a8ff..ef097827e7699e 100644 --- a/src/ui/public/debounce/__tests__/debounce.js +++ b/src/ui/public/debounce/__tests__/debounce.js @@ -15,6 +15,9 @@ function init() { $timeoutSpy = sinon.spy($timeout); debounce = $injector.get('debounce'); + + // ensure there is a clean slate before testing deferred tasks + $timeout.flush(); }); } diff --git a/src/ui/ui_i18n.js b/src/ui/ui_i18n.js index ebb062bd25bcba..0895ec1add423c 100644 --- a/src/ui/ui_i18n.js +++ b/src/ui/ui_i18n.js @@ -38,11 +38,11 @@ export class UiI18n { * do with the uiExports defined by each plugin. * * This consumer will allow plugins to define export with the - * "language" type like so: + * "translations" type like so: * * new kibana.Plugin({ * uiExports: { - * languages: [ + * translations: [ * resolve(__dirname, './translations/es.json'), * ], * }, diff --git a/tasks/build/verify_translations.js b/tasks/build/verify_translations.js index 356118fe9133cd..645011589971a1 100644 --- a/tasks/build/verify_translations.js +++ b/tasks/build/verify_translations.js @@ -5,10 +5,15 @@ import fromRoot from '../../src/utils/from_root'; import KbnServer from '../../src/server/kbn_server'; import * as i18nVerify from '../utils/i18n_verify_keys'; -module.exports = function (grunt) { - grunt.registerTask('_build:verifyTranslations', function () { +export default function (grunt) { + + grunt.registerTask('_build:verifyTranslations', [ + 'i18nextract', + '_build:check' + ]); + + grunt.registerTask('_build:check', function () { const done = this.async(); - const parsePaths = [fromRoot('/src/ui/views/*.jade')]; const serverConfig = { env: 'production', @@ -35,7 +40,7 @@ module.exports = function (grunt) { const kbnServer = new KbnServer(serverConfig); kbnServer.ready() - .then(() => verifyTranslations(kbnServer.uiI18n, parsePaths)) + .then(() => verifyTranslations(kbnServer.uiI18n)) .then(() => kbnServer.close()) .then(done) .catch((err) => { @@ -43,14 +48,33 @@ module.exports = function (grunt) { .then(() => done(err)); }); }); -}; -function verifyTranslations(uiI18nObj, parsePaths) +} + + +function verifyTranslations(uiI18nObj) { - return uiI18nObj.getAllTranslations() - .then(function (translations) { - return i18nVerify.getTranslationKeys(parsePaths) - .then(function (translationKeys) { + const angularTranslations = require(fromRoot('build/i18n_extract/en.json')); + const translationKeys = Object.keys(angularTranslations); + const translationPatterns = [ + { regEx: 'i18n\\(\'(.*)\'\\)', + parsePaths: [fromRoot('/src/ui/views/*.jade')] } + ]; + + const keyPromises = _.map(translationPatterns, (pattern) => { + return i18nVerify.getTranslationKeys(pattern.regEx, pattern.parsePaths) + .then(function (keys) { + const arrayLength = keys.length; + for (let i = 0; i < arrayLength; i++) { + translationKeys.push(keys[i]); + } + }); + }); + + return Promise.all(keyPromises) + .then(function () { + return uiI18nObj.getAllTranslations() + .then(function (translations) { const keysNotTranslatedPerLocale = i18nVerify.getNonTranslatedKeys(translationKeys, translations); if (!_.isEmpty(keysNotTranslatedPerLocale)) { const str = JSON.stringify(keysNotTranslatedPerLocale); diff --git a/tasks/config/i18nextract.js b/tasks/config/i18nextract.js new file mode 100644 index 00000000000000..d7b5f689381b41 --- /dev/null +++ b/tasks/config/i18nextract.js @@ -0,0 +1,9 @@ +export default function (grunt) { + return { + default_options: { + src: ['src/**/*.js', 'src/**/*.html'], + lang: ['en'], + dest: 'build/i18n_extract' + } + }; +} diff --git a/tasks/utils/i18n_verify_keys.js b/tasks/utils/i18n_verify_keys.js index 82f0ac43767b2a..f1d4e70a903a76 100644 --- a/tasks/utils/i18n_verify_keys.js +++ b/tasks/utils/i18n_verify_keys.js @@ -9,15 +9,15 @@ const globProm = Promise.promisify(glob); /** * Return all the translation keys found for the file pattern + * @param {String} translationPattern - regEx pattern for translations * @param {Array} filesPatterns - List of file patterns to be checkd for translation keys - * @param {Array} translations - List of translations keys * @return {Promise} - A Promise object which will return a String Array of the translation keys * not translated then the Object will contain all non translated translation keys with value of file the key is from */ -export function getTranslationKeys(filesPatterns) { +export function getTranslationKeys(translationPattern, filesPatterns) { return getFilesToVerify(filesPatterns) .then(function (filesToVerify) { - return getKeys(filesToVerify); + return getKeys(translationPattern, filesToVerify); }); }; @@ -44,8 +44,10 @@ function getFilesToVerify(verifyFilesPatterns) { return Promise.map(verifyFilesPatterns, (verifyFilesPattern) => { const baseSearchDir = path.dirname(verifyFilesPattern); - const pattern = path.basename(verifyFilesPattern); - return globProm(pattern, {cwd: baseSearchDir, matchBase: true}) + + const pattern = path.join('**', path.basename(verifyFilesPattern)); + return globProm(pattern, { cwd: baseSearchDir, matchBase: true }) + .then(function (files) { for (const file of files) { filesToVerify.push(path.join(baseSearchDir, file)); @@ -57,9 +59,8 @@ function getFilesToVerify(verifyFilesPatterns) { }); } -function getKeys(filesToVerify) { +function getKeys(translationPattern, filesToVerify) { const translationKeys = []; - const translationPattern = 'i18n\\(\'(.*)\'\\)'; const translationRegEx = new RegExp(translationPattern, 'g'); const filePromises = _.map(filesToVerify, (file) => { diff --git a/webpackShims/angular.js b/webpackShims/angular.js index df9833dbddd6cb..9cc9932944d49f 100644 --- a/webpackShims/angular.js +++ b/webpackShims/angular.js @@ -1,7 +1,8 @@ require('jquery'); require('node_modules/angular/angular'); +require('node_modules/angular-translate'); module.exports = window.angular; require('node_modules/angular-elastic/elastic'); -require('ui/modules').get('kibana', ['monospaced.elastic']); +require('ui/modules').get('kibana', ['monospaced.elastic', 'pascalprecht.translate']); diff --git a/webpackShims/ui-bootstrap.js b/webpackShims/ui-bootstrap.js index 92ddecab13d05e..9dc879b6d85065 100644 --- a/webpackShims/ui-bootstrap.js +++ b/webpackShims/ui-bootstrap.js @@ -1,11 +1,19 @@ define(function (require) { require('angular'); require('ui/angular-bootstrap/index'); + const chrome = require('../src/ui/public/chrome/chrome'); return require('ui/modules') - .get('kibana', ['ui.bootstrap']) + .get('kibana', ['ui.bootstrap', 'pascalprecht.translate']) .config(function ($tooltipProvider) { $tooltipProvider.setTriggers({ 'mouseenter': 'mouseleave click' }); + }) + .config(function ($translateProvider) { + $translateProvider.translations('default', chrome.getTranslations()); + $translateProvider.preferredLanguage('default'); + // Enable escaping of HTML + // issue in https://angular-translate.github.io/docs/#/guide/19_security + $translateProvider.useSanitizeValueStrategy('escape'); }); });