mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Kibana Globalization - Phase 2 (#8766)
* 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
This commit is contained in:
parent
d22861f3ec
commit
3aa5938daa
16 changed files with 380 additions and 85 deletions
|
@ -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 (`<kibana_root>/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 `<p></p>` tag for translation purposes. Also note the prefix `KIBANA-`
|
||||
matching the plugin being translated.
|
||||
|
||||
[[before]]
|
||||
Before
|
||||
++++++
|
||||
|
||||
`_create.html`
|
||||
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
<h1>Configure an index pattern</h1>
|
||||
In order to use Kibana you must configure at least one index pattern…
|
||||
|
||||
<kbn-info info="This field will be used to filter events with the global time filter"></kbn-info>
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
|
||||
[[after]]
|
||||
After
|
||||
+++++
|
||||
|
||||
`_create.html`
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
<h1 translate="KIBANA-CONFIGURE_INDEX_PATTERN"</h1>
|
||||
<p translate="KIBANA-MUST_CONFIGURE_INDEX_PATTERN"</p>
|
||||
|
||||
<kbn-info info="{{ 'KIBANA-FIELD_FILTER_EVENTS_GLOBAL_TIME' | translate }}"></kbn-info>
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
* 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.
|
|
@ -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",
|
||||
|
@ -216,6 +217,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": "6.0.0",
|
||||
"grunt-cli": "0.1.13",
|
||||
|
|
|
@ -2,20 +2,16 @@
|
|||
<kbn-management-indices>
|
||||
<div ng-controller="managementIndicesCreate" class="kbn-management-indices-create">
|
||||
<div class="page-header">
|
||||
<h1>Configure an index pattern</h1>
|
||||
In order to use Kibana you must configure at least one index pattern. Index patterns are
|
||||
used to identify the Elasticsearch index to run search and analytics against. They are also
|
||||
used to configure fields.
|
||||
<h1 translate="KIBANA-CONFIGURE_INDEX_PATTERN"></h1>
|
||||
<p translate="KIBANA-MUST_CONFIGURE_INDEX_PATTERN"></p>
|
||||
</div>
|
||||
<div>
|
||||
<form name="form" role="form" class="well" ng-submit="createIndexPattern()">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Index name or pattern
|
||||
</label>
|
||||
<p class="help-block" ng-if="!index.nameIsPattern">Patterns allow you to define dynamic index names using * as a wildcard. Example: logstash-*</p>
|
||||
<p class="help-block" ng-if="index.isTimeBased && index.nameIsPattern">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. —
|
||||
<small><a target="_blank" href="http://momentjs.com/docs/#/displaying/format/">Date Format Documentation</a></small></p>
|
||||
<label translate="KIBANA-INDEX_NAME_OR_PATTERN"></label>
|
||||
<p class="help-block" ng-if="!index.nameIsPattern" translate="KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS"></p>
|
||||
<p class="help-block" ng-if="index.isTimeBased && index.nameIsPattern"><span translate="KIBANA-STATIC_TEXT_IN_DYNAMIC_INDEX_PATTERNS"></span> —
|
||||
<small><a target="_blank" href="http://momentjs.com/docs/#/displaying/format/" translate="KIBANA-DATE_FORMAT_DOCS"></a></small></p>
|
||||
<input
|
||||
ng-model="index.name"
|
||||
ng-attr-placeholder="{{index.defaultName}}"
|
||||
|
@ -27,9 +23,10 @@
|
|||
type="text"
|
||||
class="form-control">
|
||||
<small ng-show="index.nameInterval.name == 'weeks'">
|
||||
<strong>Note: </strong>
|
||||
I noticed you're using weekly indices. Kibana requires ISO weeks be used in your index creation.
|
||||
See <a href="https://en.wikipedia.org/wiki/ISO_week_date" target="_blank" title="Wikipedia: ISO Week Date">Wikipedia: ISO Week Date</a>
|
||||
<strong translate="KIBANA-NOTE_COLON"></strong>
|
||||
<span translate="KIBANA-WEEKLY_ISO_NOTICE"></span>
|
||||
<span translate="KIBANA-SEE"></span>
|
||||
<a href="https://en.wikipedia.org/wiki/ISO_week_date" target="_blank" title="Wikipedia: ISO Week Date" translate="KIBANA-WIKI_ISO_WEEK_DATE"></a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
@ -38,18 +35,18 @@
|
|||
<input
|
||||
ng-model="index.isTimeBased"
|
||||
type="checkbox">
|
||||
Index contains time-based events
|
||||
<span translate="KIBANA-CONTAINS_TIME_BASED_EVENTS"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="index.isTimeBased">
|
||||
<label>
|
||||
Time-field name
|
||||
<span translate="KIBANA-TIME_FIELD_NAME"></span>
|
||||
|
||||
<kbn-info info="This field will be used to filter events with the global time filter"></kbn-info>
|
||||
<kbn-info info="{{ 'KIBANA-FIELD_FILTER_EVENTS_GLOBAL_TIME' | translate }}"></kbn-info>
|
||||
|
||||
<small>
|
||||
<a ng-click="refreshFieldList();"> refresh fields</a>
|
||||
<a ng-click="refreshFieldList();" translate="KIBANA-REFRESH_FIELDS"></a>
|
||||
</small>
|
||||
</label>
|
||||
<select
|
||||
|
@ -65,58 +62,50 @@
|
|||
<div class="form-group" ng-if="canExpandIndices()">
|
||||
<label>
|
||||
<input ng-model="index.notExpandable" type="checkbox">
|
||||
Do not expand index pattern when searching <small>(Not recommended)</small>
|
||||
<span translate="KIBANA-NOT_EXPAND_INDEX_PATTERN"></span>
|
||||
<small translate="KIBANA-NOT_RECOMMENDED"></small>
|
||||
</label>
|
||||
|
||||
<div ng-if="index.notExpandable" class="alert alert-info">
|
||||
This index pattern will be queried directly rather than being
|
||||
expanded into more performant searches against individual indices.
|
||||
<span translate="KIBANA-NOT_EXPANDABLE"></span>
|
||||
|
||||
Elasticsearch will receive a query against <em>{{index.name}}</em>
|
||||
and will have to search through all matching indices regardless
|
||||
of whether they have data that matches the current time range.
|
||||
<span translate="KIBANA-ES_QUERY"></span>
|
||||
<em translate="KIBANA-INDEX_NAME_VAR" translate-values="{ indexName: '{{index.name}}' }"></em>
|
||||
<span translate="KIBANA-SEARCH_ALL_DATA"></span>
|
||||
</div>
|
||||
|
||||
<p class="help-block">
|
||||
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.
|
||||
</p>
|
||||
<p class="help-block" translate="KIBANA-WILDCARD_DEFAULT_EXPANDED_TO_CURRENT_TIME_RANGE"></p>
|
||||
|
||||
<p class="help-block">
|
||||
Searching against the index pattern <em>logstash-*</em> will
|
||||
actually query elasticsearch for the specific matching indices
|
||||
(e.g. <em>logstash-2015.12.21</em>) that fall within the current
|
||||
time range.
|
||||
<span translate="KIBANA-SEARCH_AGAINST_INDEX_PATTERN"></span>
|
||||
<em translate="KIBANA-LOGSTASH_WILDCARD"></em>
|
||||
<span translate="KIBANA-ACTUALLY_QUERY"></span>
|
||||
<em translate="KIBANA-EXAMPLE_TIME_RANGE"></em>
|
||||
<span translate="KIBANA-FALL_WITHIN_CURRENT_TIME_RANGE"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group time-and-pattern">
|
||||
<label ng-if="index.isTimeBased">
|
||||
<input ng-model="index.nameIsPattern" type="checkbox">
|
||||
Use event times to create index names <small>[DEPRECATED]</small>
|
||||
<span translate="KIBANA-INDEX_NAME_CREATED_BY_EVENT_TIMES"></span>
|
||||
<small translate="KIBANA-DEPRECATED"></small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="index.isTimeBased && index.nameIsPattern">
|
||||
<div class="alert alert-warning">
|
||||
<h4>Time-interval based index patterns are deprecated!</h4>
|
||||
<p>
|
||||
We <strong>strongly recommend</strong> using wildcard pattern names instead of
|
||||
time-interval based index patterns.
|
||||
</p>
|
||||
<h4 translate="KIBANA-ALERT_INDEX_PATTERN_DEPRECATED"></h4>
|
||||
<p>
|
||||
Kibana is now smart enough to automatically determine which
|
||||
indices to search against within the current time range for
|
||||
wildcard index patterns. This means that wildcard
|
||||
index patterns now get the same performance optimizations when
|
||||
searching within a time range as time-interval patterns.
|
||||
<span translate="KIBANA-WE"></span>
|
||||
<strong translate="KIBANA-STRONGLY_RECOMMEND"></strong>
|
||||
<span translate="KIBANA-WILD_CARD_PATTERN"></span>
|
||||
</p>
|
||||
</div>
|
||||
<label>
|
||||
Index pattern interval
|
||||
<kbn-info info="The interval at which index names rotate."></kbn-info>
|
||||
<span translate="KIBANA-INDEX_PATTERN_INTERVAL"></span>
|
||||
<kbn-info info="{{ 'KIBANA-INTERVAL_INDEX_NAMES_ROTATE' | translate }}"></kbn-info>
|
||||
</label>
|
||||
<select
|
||||
required
|
||||
|
@ -132,17 +121,17 @@
|
|||
</div>
|
||||
|
||||
<div class="alert alert-info" ng-if="index.samples">
|
||||
Attempted to match the following indices and aliases:
|
||||
<span translate="KIBANA-SAMPLE_ALERT"></span>
|
||||
<ul>
|
||||
<li ng-repeat="sample in index.samples">{{sample}}</li>
|
||||
</ul>
|
||||
<button type="button" ng-click="moreSamples(true)" class="btn btn-default">
|
||||
Expand Search
|
||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-{{index.existing.class}}" ng-if="index.existing">
|
||||
Pattern matches {{index.existing.matchPercent}} of existing indices and aliases
|
||||
<span translate="KIBANA-EXISTING_MATCH_PERCENT" translate-values="{ indexExistingMatchPercent: '{{index.existing.matchPercent}}' }"></span>
|
||||
<ul>
|
||||
<li ng-repeat="match in index.existing.matches | orderBy:'toString()'| limitTo: index.sampleCount">{{match}}</li>
|
||||
</ul>
|
||||
|
@ -151,12 +140,12 @@
|
|||
ng-click="moreSamples()"
|
||||
type="button"
|
||||
class="btn btn-default">
|
||||
Expand Search
|
||||
<span translate="KIBANA-EXPAND_SEARCH"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" ng-if="index.existing.failures.length">
|
||||
Indices and aliases that were found, but did not match the pattern:
|
||||
<span translate="KIBANA-NON_MATCHING_INDICES_AND_ALIASES"></span>
|
||||
<ul>
|
||||
<li ng-repeat="match in index.existing.failures | limitTo: index.sampleCount">{{match}}</li>
|
||||
</ul>
|
||||
|
@ -164,7 +153,7 @@
|
|||
ng-if="index.sampleCount < index.existing.matches.length"
|
||||
ng-click="moreSamples()"
|
||||
class="alert-link">
|
||||
more
|
||||
<span translate="KIBANA-MORE"></span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -175,8 +164,9 @@
|
|||
ng-class="index.fetchFieldsError ? 'btn-default' : 'btn-success'"
|
||||
type="submit"
|
||||
class="btn">
|
||||
<span ng-hide="form.name.$error.indexNameInput">{{index.fetchFieldsError || "Create" }}</span>
|
||||
<span ng-show="form.name.$error.indexNameInput">Invalid index name pattern.</span>
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="index.fetchFieldsError">{{index.fetchFieldsError}}</span>
|
||||
<span ng-hide="form.name.$error.indexNameInput" ng-if="!index.fetchFieldsError" translate="KIBANA-CREATE"></span>
|
||||
<span ng-show="form.name.$error.indexNameInput" translate="KIBANA-INVALID_INDEX_PATTERN"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
@ -29,7 +29,7 @@ uiModules.get('apps/management')
|
|||
sampleCount: 5,
|
||||
nameIntervalOptions: intervals,
|
||||
|
||||
fetchFieldsError: 'Loading'
|
||||
fetchFieldsError: $translate.instant('KIBANA-LOADING')
|
||||
};
|
||||
|
||||
index.nameInterval = _.find(index.nameIntervalOptions, { name: 'daily' });
|
||||
|
@ -88,7 +88,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);
|
||||
});
|
||||
|
@ -192,12 +192,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;
|
||||
}
|
||||
|
@ -214,12 +214,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;
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,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 [];
|
||||
}
|
||||
|
||||
|
@ -287,7 +287,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) {
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
ng-if="editingId"
|
||||
href="#/management/kibana/index"
|
||||
class="btn btn-primary btn-xs"
|
||||
aria-label="Add New">
|
||||
<span class="sr-only">Add New</span>
|
||||
<i aria-hidden="true" class="fa fa-plus"></i> Add New
|
||||
aria-label="{{ 'KIBANA-ADD_NEW' | translate }}">
|
||||
<span class="sr-only" translate="KIBANA-ADD_NEW"></span>
|
||||
<i aria-hidden="true" class="fa fa-plus"></i>
|
||||
<span class="sr-only" translate="KIBANA-ADD_NEW"></span>
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
|
@ -17,7 +18,8 @@
|
|||
ng-if="!defaultIndex"
|
||||
class="sidebar-item">
|
||||
<div class="sidebar-item-title full-title">
|
||||
<span class="label label-warning">Warning</span> No default index pattern. You must select or create one to continue.
|
||||
<span class="label label-warning" translate="KIBANA-WARNING"></span>
|
||||
<p translate="KIBANA-NO_DEFAULT_INDEX_PATTERN"></p>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
|
|
|
@ -1,4 +1,56 @@
|
|||
{
|
||||
"UI-WELCOME_MESSAGE": "Loading Kibana",
|
||||
"UI-WELCOME_ERROR": "Kibana did not load properly. Check the server output for more information."
|
||||
"UI-WELCOME_ERROR": "Kibana did not load properly. Check the server output for more information.",
|
||||
"KIBANA-CONFIGURE_INDEX_PATTERN": "Configure an index pattern",
|
||||
"KIBANA-MUST_CONFIGURE_INDEX_PATTERN": "In order to use Kibana you must configure at least one index pattern. Index patterns are used to identify the Elasticsearch index to run search and analytics against. They are also used to configure fields.",
|
||||
"KIBANA-CONTAINS_TIME_BASED_EVENTS": "Index contains time-based events",
|
||||
"KIBANA-INDEX_NAME_CREATED_BY_EVENT_TIMES": "Use event times to create index names ",
|
||||
"KIBANA-DEPRECATED": "[DEPRECATED]",
|
||||
"KIBANA-ALERT_INDEX_PATTERN_DEPRECATED": "Time-interval based index patterns are deprecated!",
|
||||
"KIBANA-WE": " We",
|
||||
"KIBANA-STRONGLY_RECOMMEND": " strongly recommend",
|
||||
"KIBANA-WILD_CARD_PATTERN": " using wildcard pattern names instead of time-interval based index patterns.",
|
||||
"KIBANA-RECOMMEND_WILD_CARD_PATTERN_DETAILS": "Kibana is now smart enough to automatically determine which indices to search against within the current time range for wildcard index patterns. This means that wildcard index patterns now get the same performance optimizations when searching within a time range as time-interval patterns.",
|
||||
"KIBANA-INDEX_PATTERN_INTERVAL": "Index pattern interval",
|
||||
"KIBANA-INDEX_NAME_OR_PATTERN": "Index name or pattern",
|
||||
"KIBANA-WILDCARD_DYNAMIC_INDEX_PATTERNS": "Patterns allow you to define dynamic index names using * as a wildcard. Example: logstash-*",
|
||||
"KIBANA-STATIC_TEXT_IN_DYNAMIC_INDEX_PATTERNS": "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.",
|
||||
"KIBANA-NOTE_COLON": "Note:",
|
||||
"KIBANA-WEEKLY_ISO_NOTICE": "I noticed you are using weekly indices. Kibana requires ISO weeks be used in your index creation.",
|
||||
"KIBANA-NOT_EXPAND_INDEX_PATTERN": "Do not expand index pattern when searching ",
|
||||
"KIBANA-NOT_RECOMMENDED": "(Not recommended)",
|
||||
"KIBANA-NOT_EXPANDABLE": "This index pattern will be queried directly rather than being expanded into more performant searches against individual indices.",
|
||||
"KIBANA-ES_QUERY": "Elasticsearch will receive a query against ",
|
||||
"KIBANA-INDEX_NAME_VAR": "{{indexName}} ",
|
||||
"KIBANA-SEARCH_ALL_DATA": "and will have to search through all matching indices regardless of whether they have data that matches the current time range.",
|
||||
"KIBANA-WILDCARD_DEFAULT_EXPANDED_TO_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.",
|
||||
"KIBANA-SEARCH_AGAINST_INDEX_PATTERN": "Searching against the index pattern ",
|
||||
"KIBANA-LOGSTASH_WILDCARD": "logstash-*",
|
||||
"KIBANA-ACTUALLY_QUERY": " will actually query elasticsearch for the specific matching indices (e.g. ",
|
||||
"KIBANA-EXAMPLE_TIME_RANGE": "logstash-2015.12.21",
|
||||
"KIBANA-FALL_WITHIN_CURRENT_TIME_RANGE": ") that fall within the current time range.",
|
||||
"KIBANA-SAMPLE_ALERT": "Attempted to match the following indices and aliases:",
|
||||
"KIBANA-EXPAND_SEARCH": "Expand Search",
|
||||
"KIBANA-EXISTING_MATCH_PERCENT": "Pattern matches {{indexExistingMatchPercent}} of existing indices and aliases",
|
||||
"KIBANA-NON_MATCHING_INDICES_AND_ALIASES": "Indices and aliases that were found, but did not match the pattern:",
|
||||
"KIBANA-MORE": "more",
|
||||
"KIBANA-TIME_FIELD_NAME": "Time-field name",
|
||||
"KIBANA-REFRESH_FIELDS": "refresh fields",
|
||||
"KIBANA-INVALID_INDEX_PATTERN": "Invalid index name pattern.",
|
||||
"KIBANA-DATE_FORMAT_DOCS": "Date Format Documentation",
|
||||
"KIBANA-WIKI_ISO_WEEK_DATE": "Wikipedia: ISO Week Date",
|
||||
"KIBANA-NO_INDICES_MATCHING_PATTERN": "Could not locate any indices matching that pattern. Please add the index to Elasticsearch",
|
||||
"KIBANA-PATTERN_DOES_NOT_MATCH_EXIST_INDICES": "Pattern does not match any existing indices",
|
||||
"KIBANA-INVALID_NON_UNIQUE_INDEX_NAME_CREATED": "Invalid pattern, interval does not create unique index names",
|
||||
"KIBANA-FIELD_FILTER_EVENTS_GLOBAL_TIME" : "This field will be used to filter events with the global time filter",
|
||||
"KIBANA-INTERVAL_INDEX_NAMES_ROTATE" : "The interval at which index names rotate.",
|
||||
"KIBANA-WARNING" : "Warning",
|
||||
"KIBANA-NO_DEFAULT_INDEX_PATTERN" : "No default index pattern. You must select or create one to continue.",
|
||||
"KIBANA-LOADING": "Loading",
|
||||
"KIBANA-SET_INDEX_NAME_FIRST": "Set an index name first",
|
||||
"KIBANA-INTERVAL_INDICES_POPULATED" : "Select the interval at which your indices are populated.",
|
||||
"KIBANA-INDICES_MATCH_PATTERN" : "Unable to fetch mapping. Do you have indices matching the pattern?",
|
||||
"KIBANA-ADD_NEW" : "Add New",
|
||||
"KIBANA-SEE" : "See",
|
||||
"KIBANA-CREATE" : "Create"
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ export default async (kbnServer, server, config) => {
|
|||
|
||||
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)
|
||||
|
|
15
src/ui/public/chrome/api/translations.js
Normal file
15
src/ui/public/chrome/api/translations.js
Normal file
|
@ -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 || [];
|
||||
};
|
||||
};
|
|
@ -18,6 +18,7 @@ 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 = {};
|
||||
|
@ -43,6 +44,7 @@ angularApi(chrome, internals);
|
|||
controlsApi(chrome, internals);
|
||||
templateApi(chrome, internals);
|
||||
themeApi(chrome, internals);
|
||||
translationsApi(chrome, internals);
|
||||
|
||||
chrome.bootstrap = function () {
|
||||
chrome.setupAngular();
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
* ],
|
||||
* },
|
||||
|
|
|
@ -6,9 +6,14 @@ import KbnServer from '../../src/server/kbn_server';
|
|||
import * as i18nVerify from '../utils/i18n_verify_keys';
|
||||
|
||||
export default function (grunt) {
|
||||
grunt.registerTask('_build:verifyTranslations', function () {
|
||||
|
||||
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 @@ export default 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,32 @@ export default 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);
|
||||
|
|
9
tasks/config/i18nextract.js
Normal file
9
tasks/config/i18nextract.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default function (grunt) {
|
||||
return {
|
||||
default_options: {
|
||||
src: ['src/**/*.js', 'src/**/*.html'],
|
||||
lang: ['en'],
|
||||
dest: 'build/i18n_extract'
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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<String>} filesPatterns - List of file patterns to be checkd for translation keys
|
||||
* @param {Array<String>} 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,7 +44,7 @@ function getFilesToVerify(verifyFilesPatterns) {
|
|||
|
||||
return Promise.map(verifyFilesPatterns, (verifyFilesPattern) => {
|
||||
const baseSearchDir = path.dirname(verifyFilesPattern);
|
||||
const pattern = path.basename(verifyFilesPattern);
|
||||
const pattern = path.join('**', path.basename(verifyFilesPattern));
|
||||
return globProm(pattern, { cwd: baseSearchDir, matchBase: true })
|
||||
.then(function (files) {
|
||||
for (const file of files) {
|
||||
|
@ -57,9 +57,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) => {
|
||||
|
|
3
webpackShims/angular.js
vendored
3
webpackShims/angular.js
vendored
|
@ -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']);
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue