Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kibana Globalization #6515

Closed
srl295 opened this issue Mar 11, 2016 · 104 comments
Closed

Kibana Globalization #6515

srl295 opened this issue Mar 11, 2016 · 104 comments

Comments

@srl295
Copy link
Contributor

srl295 commented Mar 11, 2016

Kibana Globalization

Note: These requirements were originally written up over a year ago. Take any specific implementation details with a grain of salt. Phases that were already implemented should be reevaluated to make sure they still make sense, especially in light of the new kibana platform work.

Purpose

  • Provide translation readiness in Kibana for view text (user data is a separate issue)
  • A core translation layer that is framework agnostic and consumable in all parts of Kibana
  • Provide plug-and-play capability (i.e. code free) for localization engineers when providing translations of different languages
  • Delivered in a phased approach

Architecture Diagram

This was our original plan, but it might need to change dramatically for the new platform

Design

Phase 1

Note this phase was completed, but it's possible the implementation might need to change for the new platform. Most of the code lives here

Purpose

  • Implement the internationalization engine (I18n class) which provides a means to supply translations in a standard format that it not dependent on a localization framework
  • i18n engine manages all locale translations; registering all translations and loading the correct locale when required
  • Translate the Kibana welcome message which proves that the I18n class provides translations as registered for Kibana

Decide Language for Translation Algorithm

  • The algorithm which decides the language for loading translations is as follows:
    1. First, do a direct comparison with the highest priority locale in the HTTP header “accept-language” against the registered translation languages. If comparison found then this is the language returned.
    2. Next, do a partial comparison of this locale whereby you try and get a language code comparison against the registered translation languages. For example, "fr" or "fr-FR" is used for all regions of French if comparison applies. If comparison found then this is the language returned.
    3. Next, repeat step 2 and if need be step 3 for the next highest priority locale in the “accept-language” list.
    4. Continue, until match found in step 2/step 3 or end of the “accept-language” list. If no match found then return empty language.

I18n Class

  • Manages the language translations for Kibana

  • Responsible for loading translated content per language

  • The translations file are JSON files with a flat keyspace, where the keys are unique. This uniqueness between translation plugins could be achieved by prefixing the keys with the plugin name. The key signifies the translation ID which would be referenced in translatable files (like JS, HTML etc.).

  • The key value is the translation string

  • Example translation JSON file

en.json

    {
        "UI-WELCOME_MESSAGE": "Loading Kibana",
        "UI-WELCOME_ERROR": "Kibana did not load properly. Check the server output for more information."
    }
  • Core Kibana plugins like ‘kibana’ and ‘status_page’ could come with their own English translations bundled in
  • API:
    • Return all translations for registered locales
      • getAllTranslations()
      • A Promise object where keys are the locale and values are Objects of translation keys and translations
    • Return all translations registered for the default locale:
      • getTranslationsForDefaultLocale()
      • A Promise for an object where keys are translation keys and values are translations
    • Return translations for a suitable locale from a user side locale list of BCP 47 language tags:
      • getTranslations(...languageTags)
      • A Promise for an object where keys are translation keys and values are translations
      • This object will contain all registered translations for the highest priority locale which is registered with the i18n module
      • This object can be empty if no locale in the language tags can be matched against the registered locales
    • Register translations:
      • registerTranslations(<absolute_path_to_translation_file>)
      • The path to the translation file is registered with i18n class

UiI18n Class

  • Handles the interaction between the UI/server and localization (I18n class)
  • Maps the accept-language header to BCP 47 tags
  • Fetches the language as requested where the locale is decided by the translation algorithm
  • It also substitutes any missing translations with the default locale translation

Tool for Verifying All Translations have Translatable Strings

  • Grunt run task tool that tests all translatable strings have a translation (i.e. all translation IDs have a corresponding translation string)
  • This could be run by CI to verify globalization end-to-end capability
  • A possible solution:
    • For non-angular constructs: Enforce a pattern to be used. For example a i18n(<key>) function in the Jade template. A tool can then be used to find such pattern and extract the keys to file
    • The keys in the key file(s) would then be checked against the language translation files registered

Deliverable

  • Translate the start-up message (“Kibana is loading ...”) and error message in the Jade template
    (Kibana Jade template)
  • Translation message will be loaded using the language as per the deciding algorithm. For phase 1 the default 'en' langauge will be used.
  • I18n unit tests
  • Tool can be run by CI to verify translation IDs have a corresponding translation string
  • Kibana core plugin registers its translation file during the initialization phase. The translation file contains strings for the welcome message and the start-up error message.

Phase 2

Purpose

  • Integrate a framework like i18next or formatJS with the i18n engine, or build our own. i18n framework needs to be independent from any UI framework (angular, react, etc).
  • Provide example for enabling translations in an AngularJS view and a React view by creating a POC for each
  • Implement a tool to generate the translation plugin
  • Documentation

Translations Available Client Side

  • Make the translations available on the client side by:
    • Embedding the translations in the initial HTML payload:
      • Reduces complexity and removes a round trip
      • Evaluation required to test if it performs well and does not create a large payload
    • If the evaluation of HTML payload is non performant, then the following approaches should be investigated in this order:
      1. Use current bundle mechanism:
        • (Kibana Jade template) to call API and generate the bundle which will be loaded during start-up
        • The JavaScript bundle produced will be of the following format: i18n_<language>.bundle.js
        • Kibana loads all resource bundles on the client side after starting the single-page application
      2. Client side directly calls REST API and loads the JSON payload:
        • GET /i18n/translations ==> returns the English (or German or whatever) translations negotiated with the browser HTTP header “accept-language” priority list compared against the languages supported
        • GET /i18n/translationsForDefaultLocale ==> returns the default locale translations
    • All approaches will decide the language to be served up by using the language deciding algorithm (from Phase 1)

Translation Identifiers Added to Kibana UI

  • Ids are added to the relevant view with Angular and HTML content
  • English (en) translation file is generated for the Ids defined

BEFORE (HTML)

    <div class="sidebar-list"><div class="sidebar-list-header">
        <h5>Selected Fields</h5>
    </div>

AFTER (HTML)

    <div class="sidebar-list"><div class="sidebar-list-header">
        <h5 translate="FIELDS_SELECTEDFIELDS"></h5>
    </div>

Tool for Verifying All Translations have Translatable Strings (Update)

  • Verify translation IDs in Angular templates
  • The keys in the key file(s) would then be checked against the language translation files registered

Translation Plugin Generator

  • Tool which generates a translation plugin
  • Localization engineer should only need to add translation file(s) within the plugin directory, refernce the file(s) in the 'uiExports' plugin extension, and add plugin to Kibana
  • All integration with Kibana works OOTB

Deliverable

  • Angular and React views where globalization is enabled showing the pattern to use
  • Verification tool updated to check translation keys in UI
  • A generator that localization engineers can use to produce language translations and integrate them in Kibana in an easy manner
  • Documentation relevant for the following stakeholders:
    • those that want to enable translation in Kibana
    • those that want to contribute translations to Kibana
    • those that want to create a Kibana Plugin

Phase 3

Purpose

  • Add the translation identifiers and English translation strings for the existing Kibana views

Translation Identifiers Added to Kibana UI

  • Ids are added to all the relevant UI content (HTML, JS etc.)
  • English (en) translation file(s) are generated for the Ids defined

Deliverables

  • English translation bundles are available so that localization engineers can start generating translation plugins for different languages

Phase 4

Purpose

  • Contributing language packs

Handling Language Packs

  • Provide a pseudo language which will test the potential real estate of views for all language
  • Define core supported languages
  • Process for language pack builders:
    • Creating packs
    • Merging packs

Deliverables

  • Process for contributing language packs
  • Core language packs as supported by Kibana

Blockers

Open Questions/Issues

  • Translation of user data. TODO: requires a separate issue.
  • Server side set locale (ignore the client's requested locale)
  • Provide mechanism in UI for user to manually switch their language from list of supported languages
  • What about bi-directional language support? This affects language, charts and UI content support.
  • UI views should be able to handle different languages (This means when switching from one language to another (for example, English to German) that the look and feel is maintained) :
    • Different languages can have variable content lengths. This means having adequate spacing to handle the strings in each language.
    • Need to consider different fonts. Some web fonts won't support all possible languages.

History

This section has links to prior versions of this issue text.

Authors

@rashidkpc
Copy link
Contributor

    <div class="sidebar-list-header">
        <h5>{{ FIELDS_SELECTEDFIELDS | translate }}</h5>
    </div>

Is this expecting FIELDS_SELECTEDFIELDS to be attached to $scope of the controller? It would make more sense to implement this as an attribute directive similar to ng-bind

eg:

     <div class="sidebar-list-header">
         <h5 kbn-bind="FIELDS_SELECTEDFIELDS"></h5>
     </div>

Then kbn-bind would know how to handle FIELDS_SELECTEDFIELDS as a string, retrieve the appropriate replacement text, and fill it. In addition it would allow us to use one time binding and not suffer the performance consequences of two way binding with a filter.

@rashidkpc
Copy link
Contributor

    {
        "FIELDS_SELECTEDFIELDS": "Selected Fields",
        "METRIC_COUNT": "Count"
    }

My initial thought is that a massive master list doesn't seem very elegant. It seems like this sort of thing would be better implemented the way we do .less files, scoped to the current app?

@rashidkpc
Copy link
Contributor

There would also need to be a test framework around this to ensure that all of the constants that must be defined, are. Potential translation volunteers need a simple way of discovering what needs translation and a simple way of confirming that they've defined everything required

@rashidkpc
Copy link
Contributor

    $translate('METRIC_COUNT').then( function(count) {
        return new MetricAggType({
    title: count
    }}

This seems to imply that any functions that currently contain strings would need to be updated to return a promise. I don't see the need for the async behavior here.

@rashidkpc
Copy link
Contributor

Given the need to translate both angular and non-angular components, why was a framework specific translation implementation chosen? And why this one specifically?

@srl295
Copy link
Contributor Author

srl295 commented Mar 11, 2016

@rashidkpc thanks. Comments on a couple of items:

  • translation scoping - yes, the translations could definitely be scoped to smaller units. I'll take a look at how the .less files are scoped.
  • async behavior / angular-translate - this was one option. There may be a simpler option. The non-angular components might be able to use a different mechanism for loading content. We picked angular-translate to try and use something pre existing for the angular side.

@DTownSMR
Copy link

Rashid, maybe I should take these out of order.

  • Why a framework & angular-translate? - The angularjs side of things looked to us like a
    good place to start a first iteration of efforts of adding g11n to Kibana. As Steven
    mentioned we picked angular-translate since we didn't want to reinvent the wheel and it
    is feature rich, good community support and seems to be gaining momentum within the
    angularjs community. We're not married to that solution of if there is a different
    preference within the Kibana community we can certainly look at that.
  • angular-translate supports a "translate" directive in addition to it's filter mechanism.
    We were just showing an example of what translation can look like.
  • angular-translate does not require a single resource bundle. It supports a number of
    options. Their partialLoader that will handle breaking up the resources at a component
    level.
  • The idea of a test framework is interesting, especially for on-going efforts.

@shikhasriva
Copy link

Rashid, regarding test framework, we suggest having that as a separate issue. And definitely agree with the need and usage of it... what do you think ?

@shikhasriva
Copy link

Rashid and all, DO you all agree with the phased approach we suggested ...thanks

@w33ble
Copy link
Contributor

w33ble commented Mar 18, 2016

Given the need to translate both angular and non-angular components, why was a framework specific translation implementation chosen?

While I agree that angular-translate looks pleasant to use, and seems to be well maintained, using something that's explicitly bound to angular for something as far-reaching as i18n concerns me.

As @rashidkpc points out, we have non-angular components in our code, and that's likely to become increasingly common in the future. I'm also concerned about server-side use as we push things into the node server.

Is there something more generic that fits the bill, and also has an existing angular wrapper?

@rashidkpc
Copy link
Contributor

I haven't looked deeply into i18n, but I'm hesitant to tie to angular-translate for the full implementation. It could be used in places, but there's plenty of places it can't be used. Like joe said, I think it would be good to investigate something lower level that could be wrapped up for the angular bits.

On the topic of the phases. I don't see any problem with those stages, but i think more discussion is needed on the approach before beginning.

I would like discussion of the testing framework to happen here. We can always split it off if needed, but it has direct influence on the approach taken.

@srl295
Copy link
Contributor Author

srl295 commented Mar 19, 2016

The translated files are just JSON and could certainly be consumed by the server side or the widgets, etc without use of the angular-specific framework. So this approach doesn't preclude using angular-translate where it makes sense, and something else (or no framework) elsewhere.

Re testing - yes, we can definitely discuss it here. We were wanting to split the test itself into a separate phase as above. (I added 1AA) One issue would be that I would recommend having a missing translation be more of a warning than a failure from a CI perspective, otherwise a feature couldn't be added in the source language (English) without all translations being done, nor could a translation be partial.

@w33ble
Copy link
Contributor

w33ble commented Mar 22, 2016

The translated files are just JSON and could certainly be consumed by the server side or the widgets, etc without use of the angular-specific framework. So this approach doesn't preclude using angular-translate where it makes sense, and something else (or no framework) elsewhere.

That's a fair point. It does seem like the format is pretty common, and the more or less the same regardless is which library/framework you use. Getting hung up on angular-translate specifically is probably a waste of time.

Given that, I think it's worth adding something in one of the earlier phases about ensuring that whatever translation format we go with works both with and without Angular somehow, most likely by specifically targeting a part of the application that isn't in Angular. Server-side code is probably an easy candidate.

@srl295
Copy link
Contributor Author

srl295 commented Mar 22, 2016

@w33ble OK. Perhaps something like - "a simple JSON format will be specified in the documentation, and the proof of concept will use angular-translate. Non-angular portions (such as the server) would not use angular-translate, and the specific plugin technology used for translating on the client side is subject to discussion"

@w33ble
Copy link
Contributor

w33ble commented Mar 22, 2016

I think Rashid and I just want to make sure the plan specifically takes into account using the i18n definitions outside of Angular. Adding the above sounds good to me.

@srl295
Copy link
Contributor Author

srl295 commented Mar 23, 2016

@w33ble — thanks, that makes sense.
Edit I have updated the text above to address the feedback from yourself and @rashidkpc .

@srl295
Copy link
Contributor Author

srl295 commented Mar 30, 2016

@rashidkpc @w33ble — any comments on the updates?

@rashidkpc
Copy link
Contributor

I'm happier with the new proposition.

    var uiStrings = ; // loading TBD
    return new MetricAggType({
      title: uiStrings.METRIC_COUNT,

Either webpack/import or though a Private module, which would make it easy to inject into other parts of the application.

@srl295
Copy link
Contributor Author

srl295 commented Mar 30, 2016

great. We will investigate using webpack.

It seems like the proof of concept should be able to proceed for phase 1A if there are no issues on that part.

@Bargs
Copy link
Contributor

Bargs commented May 20, 2016

Can we create a new ticket specifically for Phase 1A and turn this ticket into more of a meta ticket? There's a lot of good discussion and context here so I wouldn't want it to get closed when #7247 gets merged.

@srl295 srl295 changed the title Kibana Globalization - Phase 1A Kibana Globalization May 20, 2016
@rashidkpc rashidkpc assigned Bargs and unassigned rashidkpc May 24, 2016
@hickeyma
Copy link
Contributor

Great stuff @Bargs and @spalger. Lets try and sync-up on IRC and work out how to progress for phase 2 and 3.

@hickeyma
Copy link
Contributor

hickeyma commented Dec 20, 2016

@Bargs, @epixa, @shikhasriva, @spalger, @posijon Thank you all for attending the sync-up yesterday and for your feedback.

Here are notes from the meeting (let me know if I have misrepresented anything):

  • It is good that we landed Phase 1. We want to now drive on with Phase 2 and 3.
  • Phase 2 is to get the angular template right for localization engineers to use.
  • Phase 3 is to add translation bundles for various languages.
  • Updates to Phase 2:
    • In "Deliverable" section, "making the translations to client side" needs to be updated. The proposal is to now implement it by embedding the translations in the initial HTML payload. If discovered that this implementation is non performant then we will consider other approaches like: using current bundle mechanism or then using REST APIs to return the translations.
    • Add to "Deliverable" section, Elastic needs to handle how to translate and verify xplugins with React
  • "Tool for verification of translations" needs to be moved from Phase 3 to Phase 2.
  • "Implement a tool to generate the translation plugin" is to be moved from Phase 3 to Phase 4.

@hickeyma
Copy link
Contributor

@Bargs, @spalger Updated the design doc after yesterday's meeting as kibana-proposal-update. Could you update the design doc when you get a chance pls.?

@Bargs
Copy link
Contributor

Bargs commented Dec 20, 2016

Updated

@yuwtennis
Copy link

+1

@hickeyma
Copy link
Contributor

@spalger Updated the design doc kibana-proposal-update. Could you update the design doc when you get a chance pls.?

@spalger
Copy link
Contributor

spalger commented Feb 21, 2017

Could you update the design doc when you get a chance pls.?

Sorry, which design doc are you referring to?

@srl295
Copy link
Contributor Author

srl295 commented Feb 21, 2017

@spalger he's asking for this issue ^^ ’s text to be updated from the above gist. As issue creator I was able to do it - so Done

@hickeyma
Copy link
Contributor

Thanks @srl295

@hickeyma
Copy link
Contributor

@spalger unfortunately no rights on the doc to update!

elastic-jasper added a commit that referenced this issue Mar 2, 2017
Backports PR #7545

**Commit 1:**
Add low level i18n plugin

Manages languages that are available and is responsible for loading translated
content at the granularity of a plugin.

To be done:
 - APIs for store and retrieval

* Original sha: aca671f
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-20T17:12:25Z

**Commit 2:**
Use Kibana install as root for the translation store directory

Setting the path for storing the bundled language translation files to
<KIBANA_INSTALL>/data/store_translations/<PLUGIN_NAME>

* Original sha: c3ba578
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-21T13:36:59Z

**Commit 3:**
Updated i18n core plugin APIs to be asynchronous

To be done:
 - Better error handling in APIs
 - Fix threading issue with storePluginLanguageTranslations API

* Original sha: 80d8a2c
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-22T17:25:30Z

**Commit 4:**
Fix thread synchroization issue in storePluginLanguageTranslations

* Original sha: 856fb02
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-24T10:43:06Z

**Commit 5:**
Update error handling in i18n core plugin

* Original sha: c5d22be
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-24T14:30:27Z

**Commit 6:**
Change to use NodeJS mkdirp function for creating directories recursively

Updates with review comments from @srl295. Changed export syntax to show the
exported functions at end of file.

* Original sha: b37e4f8
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-27T11:04:01Z

**Commit 7:**
Add REST API for getting translations of a language for a plugin

To be done:
 - Add algorithm to decide on the language for a plugin by comparing the accept languages
from the REST call and the plugin supported languages
 - Add REST API tests

* Original sha: ec6d2b1
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-27T22:42:15Z

**Commit 8:**
Add algorithm for determining plugin language when retrieving translations

Client would pass languages used in the 'accept-language' header. These
languages would then be compared against the plugin supported languages
and best compared language would be selected.

To be done:
 - Add REST API tests

* Original sha: a75faae
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-28T21:10:39Z

**Commit 9:**
Add API to return all registered plugin language translations

* Original sha: 66d051b
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-29T17:02:34Z

**Commit 10:**
Add HAPI API to get all plugins translation files

* Original sha: c3403e7
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-30T16:08:28Z

**Commit 11:**
Update register translations API to be independent of plugin name and language

The register API is updated to be independent of plugin name and language. The API will now
traverse the path given and create language bundles as per language files it traverses.
The translations files structure has also been simplified to be just key/value objects.

To be done:
 - Add hapi API to get translations
 - Extend the API tests to test responses

* Original sha: 0f4902d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-01T16:09:33Z

**Commit 12:**
Update API test

* Original sha: 9785df2
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-05T17:52:25Z

**Commit 13:**
Add eslint fix for API test

* Original sha: d41fcec
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-06T10:00:22Z

**Commit 14:**
Update with review comments

From review #7545 (comment)
following comments updated:
- README, .gitignore, and .eslintrc are not needed in a core plugin
- package.json only needs name and version
- unit tests need to go in a tests directory otherwise they won't get picked up
by the grunt tasks. Also our convention is to name the test file with the same
name as the module it's testing (so i18n_tests.js should just be i18n.js)
- For consistency with the rest of the code base, rename the data directory to fixtures.
- Prefer const (or let if necessary). Don't use var.
- Use ES6 imports/exports rather than commonjs style
- Only export the i18n module's public API. For instance, I don't think getPluginTranslationDetails is used outside of the i18n module, so it shouldn't be exposed publicly. If you want to expose it for testing purposes, I would recommend creating an i18n directory with an index.js file that exports the module's public API, and a separate i18n.js file with the "private" API. index.js will be for public use, i18n.js will be for private internal use.

* Original sha: c8b2197
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-11T11:23:37Z

**Commit 15:**
Update after review comments

From review (#7545 (comment)):
- i18n module API should return promises for async operations instead of using
callbacks
- All filesystem access should be async
- Unit tests need to be updated based on new proposed plugin structure
(single language file, not split by view)

From design (#6515 (comment)):
- Removed API as will consider in later phase

TODO:
- Make write function async

* Original sha: e17653d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-15T18:24:21Z

**Commit 16:**
Update after review comments

Updated write function to be asynchronous

* Original sha: eaf35ab
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-19T11:03:45Z

**Commit 17:**
Update registerTranslations API to take absolute translation file as argument

The API originally took the directory as the argument but following reviews it
was decided to change to absolute file because it will be less brittle
since it is more explicit.

* Original sha: f1974ca
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-21T17:43:27Z

**Commit 18:**
Translate the Kibana welcome message

Translates the start-up message (“Kibana is loading ...”)in the Jade template.

To be done:
 - Means to register the core plugin translations. They are currently added
in the fixtures directory as static files. Need to be generated on the fly.

* Original sha: 10db458
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-25T13:58:54Z

**Commit 19:**
Add build task to generate core plugin translations

Task which calls registerTranslations API and then a task which copies the
regsitered translations to <kibana_root>/build/kibana

* Original sha: 4957173
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-27T10:00:35Z

**Commit 20:**
Add hook to optimize module to add registration during dev startup

Registration of the core plugin translations during development start of
Kibana server. The translations include the welcome message and server error
startup message.

* Original sha: 5f61475
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-28T14:18:19Z

**Commit 21:**
Handle scenario when the user preferred language header is not passed

The UI when loading asks i18n plugin which language translation to use
depending on the user preferred language header 'accept-language'.
This commit is to handle scenario where header is not passed. The algorithm
then chooses the default language.

* Original sha: 9fbe6d5
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-29T10:51:33Z

**Commit 22:**
Replace registering of translations at plugin install time to the plugin init phase

This change follows review comments in:
#6515 (comment)

* Original sha: 32d5034
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-08T14:39:34Z

**Commit 23:**
Update after review comments

Comments:
- #7545 (diff)
- #7545 (diff)

* Original sha: 8c7f51c
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-09T09:36:04Z

**Commit 24:**
Update after plugin folder layout changes in Kibana

This require to use <kibana_root>/data for registered translations
and i18n plugin moved to core_plugins from plugins.

Refer to PR for more details:
#7562

* Original sha: b84f40a
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-09T11:09:18Z

**Commit 25:**
Update translation registration to file path rather than bundling

After review discussions it was agreed to just register the absolute paths
to translation files rather than bundling each file into one central file
at registration.

* Original sha: 2dd3065
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-16T09:28:38Z

**Commit 26:**
Update review comments

This commit contains the following review comments:
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)

* Original sha: db11a2d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-18T14:09:22Z

**Commit 27:**
Update review comments

The following review comments are included in the commit:
- #7545 (comment)
- #7545 (comment)
- #7545 (comment)

* Original sha: 8c39a8d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-19T12:44:39Z

**Commit 28:**
Expose the i18n APIs in the server object for plugin access

Plugins should call the i18n plugin APIs through the server object
and not directly from the module.

This closes he following comments:
- #7545 (comment)
- #7545 (comment)
- #7545 (comment)

* Original sha: a9e30d3
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-19T17:31:10Z

**Commit 29:**
Update accept-language-parser module to 1.2.0

Module version 1.2.0 fixes issue:
opentable/accept-language-parser#8

This commit updates review comments:
#7545 (diff)
#7545 (comment)

* Original sha: da669da
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-30T17:49:23Z

**Commit 30:**
Add i18n default locale as a configurable item

Adds 'defaultLocale' configurable item to the i18n plugin configuration.
The default locale is used for translations if the locale specified by user
is not supported.

This commit satisfies the review comment:
- #7545 (diff)

* Original sha: 9064e8a
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-31T10:42:52Z
spalger pushed a commit that referenced this issue Mar 2, 2017
* Kibana Globalization - Phase 1

Backports PR #7545

**Commit 1:**
Add low level i18n plugin

Manages languages that are available and is responsible for loading translated
content at the granularity of a plugin.

To be done:
 - APIs for store and retrieval

* Original sha: aca671f
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-20T17:12:25Z

**Commit 2:**
Use Kibana install as root for the translation store directory

Setting the path for storing the bundled language translation files to
<KIBANA_INSTALL>/data/store_translations/<PLUGIN_NAME>

* Original sha: c3ba578
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-21T13:36:59Z

**Commit 3:**
Updated i18n core plugin APIs to be asynchronous

To be done:
 - Better error handling in APIs
 - Fix threading issue with storePluginLanguageTranslations API

* Original sha: 80d8a2c
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-22T17:25:30Z

**Commit 4:**
Fix thread synchroization issue in storePluginLanguageTranslations

* Original sha: 856fb02
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-24T10:43:06Z

**Commit 5:**
Update error handling in i18n core plugin

* Original sha: c5d22be
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-24T14:30:27Z

**Commit 6:**
Change to use NodeJS mkdirp function for creating directories recursively

Updates with review comments from @srl295. Changed export syntax to show the
exported functions at end of file.

* Original sha: b37e4f8
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-27T11:04:01Z

**Commit 7:**
Add REST API for getting translations of a language for a plugin

To be done:
 - Add algorithm to decide on the language for a plugin by comparing the accept languages
from the REST call and the plugin supported languages
 - Add REST API tests

* Original sha: ec6d2b1
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-27T22:42:15Z

**Commit 8:**
Add algorithm for determining plugin language when retrieving translations

Client would pass languages used in the 'accept-language' header. These
languages would then be compared against the plugin supported languages
and best compared language would be selected.

To be done:
 - Add REST API tests

* Original sha: a75faae
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-28T21:10:39Z

**Commit 9:**
Add API to return all registered plugin language translations

* Original sha: 66d051b
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-29T17:02:34Z

**Commit 10:**
Add HAPI API to get all plugins translation files

* Original sha: c3403e7
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-06-30T16:08:28Z

**Commit 11:**
Update register translations API to be independent of plugin name and language

The register API is updated to be independent of plugin name and language. The API will now
traverse the path given and create language bundles as per language files it traverses.
The translations files structure has also been simplified to be just key/value objects.

To be done:
 - Add hapi API to get translations
 - Extend the API tests to test responses

* Original sha: 0f4902d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-01T16:09:33Z

**Commit 12:**
Update API test

* Original sha: 9785df2
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-05T17:52:25Z

**Commit 13:**
Add eslint fix for API test

* Original sha: d41fcec
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-06T10:00:22Z

**Commit 14:**
Update with review comments

From review #7545 (comment)
following comments updated:
- README, .gitignore, and .eslintrc are not needed in a core plugin
- package.json only needs name and version
- unit tests need to go in a tests directory otherwise they won't get picked up
by the grunt tasks. Also our convention is to name the test file with the same
name as the module it's testing (so i18n_tests.js should just be i18n.js)
- For consistency with the rest of the code base, rename the data directory to fixtures.
- Prefer const (or let if necessary). Don't use var.
- Use ES6 imports/exports rather than commonjs style
- Only export the i18n module's public API. For instance, I don't think getPluginTranslationDetails is used outside of the i18n module, so it shouldn't be exposed publicly. If you want to expose it for testing purposes, I would recommend creating an i18n directory with an index.js file that exports the module's public API, and a separate i18n.js file with the "private" API. index.js will be for public use, i18n.js will be for private internal use.

* Original sha: c8b2197
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-11T11:23:37Z

**Commit 15:**
Update after review comments

From review (#7545 (comment)):
- i18n module API should return promises for async operations instead of using
callbacks
- All filesystem access should be async
- Unit tests need to be updated based on new proposed plugin structure
(single language file, not split by view)

From design (#6515 (comment)):
- Removed API as will consider in later phase

TODO:
- Make write function async

* Original sha: e17653d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-15T18:24:21Z

**Commit 16:**
Update after review comments

Updated write function to be asynchronous

* Original sha: eaf35ab
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-19T11:03:45Z

**Commit 17:**
Update registerTranslations API to take absolute translation file as argument

The API originally took the directory as the argument but following reviews it
was decided to change to absolute file because it will be less brittle
since it is more explicit.

* Original sha: f1974ca
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-21T17:43:27Z

**Commit 18:**
Translate the Kibana welcome message

Translates the start-up message (“Kibana is loading ...”)in the Jade template.

To be done:
 - Means to register the core plugin translations. They are currently added
in the fixtures directory as static files. Need to be generated on the fly.

* Original sha: 10db458
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-25T13:58:54Z

**Commit 19:**
Add build task to generate core plugin translations

Task which calls registerTranslations API and then a task which copies the
regsitered translations to <kibana_root>/build/kibana

* Original sha: 4957173
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-27T10:00:35Z

**Commit 20:**
Add hook to optimize module to add registration during dev startup

Registration of the core plugin translations during development start of
Kibana server. The translations include the welcome message and server error
startup message.

* Original sha: 5f61475
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-28T14:18:19Z

**Commit 21:**
Handle scenario when the user preferred language header is not passed

The UI when loading asks i18n plugin which language translation to use
depending on the user preferred language header 'accept-language'.
This commit is to handle scenario where header is not passed. The algorithm
then chooses the default language.

* Original sha: 9fbe6d5
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-07-29T10:51:33Z

**Commit 22:**
Replace registering of translations at plugin install time to the plugin init phase

This change follows review comments in:
#6515 (comment)

* Original sha: 32d5034
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-08T14:39:34Z

**Commit 23:**
Update after review comments

Comments:
- #7545 (diff)
- #7545 (diff)

* Original sha: 8c7f51c
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-09T09:36:04Z

**Commit 24:**
Update after plugin folder layout changes in Kibana

This require to use <kibana_root>/data for registered translations
and i18n plugin moved to core_plugins from plugins.

Refer to PR for more details:
#7562

* Original sha: b84f40a
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-09T11:09:18Z

**Commit 25:**
Update translation registration to file path rather than bundling

After review discussions it was agreed to just register the absolute paths
to translation files rather than bundling each file into one central file
at registration.

* Original sha: 2dd3065
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-16T09:28:38Z

**Commit 26:**
Update review comments

This commit contains the following review comments:
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)
- #7545 (diff)

* Original sha: db11a2d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-18T14:09:22Z

**Commit 27:**
Update review comments

The following review comments are included in the commit:
- #7545 (comment)
- #7545 (comment)
- #7545 (comment)

* Original sha: 8c39a8d
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-19T12:44:39Z

**Commit 28:**
Expose the i18n APIs in the server object for plugin access

Plugins should call the i18n plugin APIs through the server object
and not directly from the module.

This closes he following comments:
- #7545 (comment)
- #7545 (comment)
- #7545 (comment)

* Original sha: a9e30d3
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-19T17:31:10Z

**Commit 29:**
Update accept-language-parser module to 1.2.0

Module version 1.2.0 fixes issue:
opentable/accept-language-parser#8

This commit updates review comments:
#7545 (diff)
#7545 (comment)

* Original sha: da669da
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-30T17:49:23Z

**Commit 30:**
Add i18n default locale as a configurable item

Adds 'defaultLocale' configurable item to the i18n plugin configuration.
The default locale is used for translations if the locale specified by user
is not supported.

This commit satisfies the review comment:
- #7545 (diff)

* Original sha: 9064e8a
* Authored by Martin Hickey <martin.hickey@ie.ibm.com> on 2016-08-31T10:42:52Z

* [eslint] autofix lint errors
@srl295
Copy link
Contributor Author

srl295 commented Mar 17, 2017

FYI: 🎦 posted blog+movie showing off g11n progress so far https://srl295.github.io/2017/03/17/translating-kibana/

weishionshi pushed a commit to weishionshi/kibana that referenced this issue May 15, 2017
* Add low level i18n plugin

Manages languages that are available and is responsible for loading translated
content at the granularity of a plugin.

To be done:
 - APIs for store and retrieval

* Use Kibana install as root for the translation store directory

Setting the path for storing the bundled language translation files to
<KIBANA_INSTALL>/data/store_translations/<PLUGIN_NAME>

* Updated i18n core plugin APIs to be asynchronous

To be done:
 - Better error handling in APIs
 - Fix threading issue with storePluginLanguageTranslations API

* Fix thread synchroization issue in storePluginLanguageTranslations

* Update error handling in i18n core plugin

* Change to use NodeJS mkdirp function for creating directories recursively

Updates with review comments from @srl295. Changed export syntax to show the
exported functions at end of file.

* Add REST API for getting translations of a language for a plugin

To be done:
 - Add algorithm to decide on the language for a plugin by comparing the accept languages
from the REST call and the plugin supported languages
 - Add REST API tests

* Add algorithm for determining plugin language when retrieving translations

Client would pass languages used in the 'accept-language' header. These
languages would then be compared against the plugin supported languages
and best compared language would be selected.

To be done:
 - Add REST API tests

* Add API to return all registered plugin language translations

* Add HAPI API to get all plugins translation files

* Update register translations API to be independent of plugin name and language

The register API is updated to be independent of plugin name and language. The API will now
traverse the path given and create language bundles as per language files it traverses.
The translations files structure has also been simplified to be just key/value objects.

To be done:
 - Add hapi API to get translations
 - Extend the API tests to test responses

* Update API test

* Add eslint fix for API test

* Update with review comments

From review elastic#7545 (comment)
following comments updated:
- README, .gitignore, and .eslintrc are not needed in a core plugin
- package.json only needs name and version
- unit tests need to go in a tests directory otherwise they won't get picked up
by the grunt tasks. Also our convention is to name the test file with the same
name as the module it's testing (so i18n_tests.js should just be i18n.js)
- For consistency with the rest of the code base, rename the data directory to fixtures.
- Prefer const (or let if necessary). Don't use var.
- Use ES6 imports/exports rather than commonjs style
- Only export the i18n module's public API. For instance, I don't think getPluginTranslationDetails is used outside of the i18n module, so it shouldn't be exposed publicly. If you want to expose it for testing purposes, I would recommend creating an i18n directory with an index.js file that exports the module's public API, and a separate i18n.js file with the "private" API. index.js will be for public use, i18n.js will be for private internal use.

* Update after review comments

From review (elastic#7545 (comment)):
- i18n module API should return promises for async operations instead of using
callbacks
- All filesystem access should be async
- Unit tests need to be updated based on new proposed plugin structure
(single language file, not split by view)

From design (elastic#6515 (comment)):
- Removed API as will consider in later phase

TODO:
- Make write function async

* Update after review comments

Updated write function to be asynchronous

* Update registerTranslations API to take absolute translation file as argument

The API originally took the directory as the argument but following reviews it
was decided to change to absolute file because it will be less brittle
since it is more explicit.

* Translate the Kibana welcome message

Translates the start-up message (“Kibana is loading ...”)in the Jade template.

To be done:
 - Means to register the core plugin translations. They are currently added
in the fixtures directory as static files. Need to be generated on the fly.

* Add build task to generate core plugin translations

Task which calls registerTranslations API and then a task which copies the
regsitered translations to <kibana_root>/build/kibana

* Add hook to optimize module to add registration during dev startup

Registration of the core plugin translations during development start of
Kibana server. The translations include the welcome message and server error
startup message.

* Handle scenario when the user preferred language header is not passed

The UI when loading asks i18n plugin which language translation to use
depending on the user preferred language header 'accept-language'.
This commit is to handle scenario where header is not passed. The algorithm
then chooses the default language.

* Replace registering of translations at plugin install time to the plugin init phase

This change follows review comments in:
elastic#6515 (comment)

* Update after review comments

Comments:
- elastic#7545 (diff)
- elastic#7545 (diff)

* Update after plugin folder layout changes in Kibana

This require to use <kibana_root>/data for registered translations
and i18n plugin moved to core_plugins from plugins.

Refer to PR for more details:
elastic#7562

* Update translation registration to file path rather than bundling

After review discussions it was agreed to just register the absolute paths
to translation files rather than bundling each file into one central file
at registration.

* Update review comments

This commit contains the following review comments:
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)

* Update review comments

The following review comments are included in the commit:
- elastic#7545 (comment)
- elastic#7545 (comment)
- elastic#7545 (comment)

* Expose the i18n APIs in the server object for plugin access

Plugins should call the i18n plugin APIs through the server object
and not directly from the module.

This closes he following comments:
- elastic#7545 (comment)
- elastic#7545 (comment)
- elastic#7545 (comment)

* Update accept-language-parser module to 1.2.0

Module version 1.2.0 fixes issue:
opentable/accept-language-parser#8

This commit updates review comments:
elastic#7545 (diff)
elastic#7545 (comment)

* Add i18n default locale as a configurable item

Adds 'defaultLocale' configurable item to the i18n plugin configuration.
The default locale is used for translations if the locale specified by user
is not supported.

This commit satisfies the review comment:
- elastic#7545 (diff)

* Move UI i18n wrapper functionality into a module

This commit better structures the i18n capability so that it can be called
in UI code in a clearly defined fashion with minimum code. It also fixes
potential race conditions.

This commit updates review comments:
- elastic#7545 (diff)
- elastic#7545 (diff)
- elastic#7545 (diff)

* Fill any missing translations using translations from default locale

The default language translations are loaded and are compared against the selected
language translations. The comparison can then highlight any missing translation
keys and can load the default translations keys as needed. This helps to unsure
where possible that a translation string is available in most scenarios even if not
in the locale requested.

This commit resolves review comments:
- elastic#7545 (comment)
- elastic#7545 (comment)

* Add unit tests for the i18n UI wrapper functions

* Fix issues after rebase with master

* Add translation keys verification tool

This tool helps to check that translation keys are translated. This tool can be
used for non-angular translation constructs like the Jade templates.

* Updates after review comments

Updates for review comments:
elastic#7545 (review)

* Update after review comments

Update for review comments:
elastic#7545 (review)

To be done:
- Update of unit tests for UI and server
- Call of verify translations

* Update unit tests after review changes

There was a number of changes to the i18n module and the ui i18n wrapper
following review comments. This commit is to update the unit tests with
respect.

* Add build task for verify translations

* Update the kibana i18n IDs to be prefixed with kibana

* Update verify translations to test registered translations

It was testing the static translation files. It is now updated to
test the translations registered when Kibana server is started and
the plugins have initialized.

* Update after review comments

Updates following review comments:
elastic#7545 (review)

* Update after review

This commit contain updates after the following review:
elastic#7545 (review)

* Updates after review

Updates for review comments:
elastic#7545 (review)

* Update after review

Updates for the following review comments:
elastic#7545 (review)

* Update after review

Updates after the following review comments:
elastic#7545 (review)

* Update unit tests to use expect throwError

* Update after rebase with master

Loading message changed following merge of commit
elastic@26c53e8#diff-e25d7fee746a4f249e17f87c02fd95f8R55
This required update to the welcome message and how it is called.

* Update following review

Updated the following review comments:
elastic#7545 (review)

* Update the algorithm to return the locale

The algorithm to return which locale to use for translations based on the user
locale list and the regsitered locales is updated in this commit. The algorithm
previously did an exact match on all the user locales first before (by priority)
then checking for best case match. The algorithm is now modified to check each
user locale starting with the highest priority first for an exact match and then
for best case match. If no match it then moves to the next user locale with
the next highest priority. This is to follow the priority list that a user
browser is configured for where there maybe a locale translation available
but might not be the exact match with regard to the locale code and/or script.
An example of this is that the highest priority locale of the user is 'en-US'
but the locale translation available is 'en'. It is better select the 'en'
locale rather than select the next highest locale which is an exact match.

* Update after review comments

Updates after the following reviews:
elastic#7545 (review)
elastic#7545 (review)

* Fix after merge with master

Change in the flo and layout of ui index meant that acceptLanguages were not
being passed. This commit is an update to fix this so that the welcome
messages are loaded.

* Update after review comments

This commit is for updates after the following review:
elastic#7545 (review)

* Fix issue when unit test run in CI as core translations are registered

When unit tests are run on a test server (like in the CI), it will start
Kibana server and register the core translations. This means that the i18n
unit tests need to be able to store the existing registration prior to
testing and replace after testing.

* [server/ui] move i18n into ui module

* [server/ui] restore renderApp() method signature

* [server/ui] unify i18n logic in UiI18n class

* [server] move translation files into "translations" dir

* Update i18n module to loaded by multiple server instances within the one process

* Update i18n module to a class

Moving the i18n module into a class so as to encapsulate the registered
translations which means there can be different and distinct instances per process.
This is to accomodate the user case where there might be multiple Kibana server
instances in a process and the localization should be at the server level.

* Identify private members in a class with underscore-prefix convention

* Remove redundant translation from core translation file

Message starting with 'Give me a moment...' is no longer part of loading
message folowing a rebase with master.

* [ui/i18n] reject translations files that do not use absolute paths

* Update config item locale to defaultLocale

* Update after review comments

- Update after following review: elastic#7545 (review)
- Also, fix syntax mess following rebase with master of src/optimize/index.js

* Fix rebase with master error

* Add task for verifying translations in CI

* Fix lint errors

# Conflicts:
#	config/kibana.yml
#	src/ui/views/ui_app.jade
@tangyong
Copy link

Great work so far! @srl295

@epixa epixa added the Meta label Aug 8, 2017
@elkusr
Copy link

elkusr commented Sep 18, 2017

Thanks for all the great work! When can we expect phase 3 to be available?

@epixa
Copy link
Contributor

epixa commented Sep 18, 2017

@elkusr There's no target yet for phase 3, and there's still some work in phase 2 to complete first now that Kibana is using react so much.

@zzvara
Copy link

zzvara commented Nov 13, 2017

How are these efforts progressing now?

Is that correct, that the tasks presented in (https://srl295.github.io/2017/03/17/translating-kibana/) can be used to add any language to a Kibana instance (for example 5.6)? In other words, adding another language to Kibana is feasible as of this writing, it just takes a "little" effort, and that Phase 3 is about to ease this effort?

@epixa
Copy link
Contributor

epixa commented Nov 13, 2017

@zzvara It isn't possible to translate Kibana right now. Phases 2 and 3 need to be completed before you can translate templates across Kibana.

@sunneyoy
Copy link

sunneyoy commented Jan 8, 2018

Looking forward to the Chinese version.

@udyr-f
Copy link

udyr-f commented Feb 23, 2018

Have you already supported Chinese?
config/kibana.yml
i18n.defaultLocale: "zh_CN"
This configuration does not work, so I use this tool now.
Kibana_Hanization

@kimjoar
Copy link
Contributor

kimjoar commented Feb 23, 2018

@sunmaolin Sorry, we currently only support English.

@epixa
Copy link
Contributor

epixa commented Apr 4, 2018

I'm going to close this issue out in favor of #17201. While that is a much newer issue than this one, it contains a lot of information about our current localization effort, and it's best if we don't fragment the discussion across multiple issues.

Many of the details in that other ticket are based on all the things we learned from this effort in particular, so thank you to the great folks at IBM and everyone else that has been involved in getting us to this point!

@epixa epixa closed this as completed Apr 4, 2018
cee-chen added a commit that referenced this issue Jan 13, 2023
## Summary

`eui@71.1.0` ⏩ `eui@72.2.0`

---

- Added `onFullScreen` callback to expose the `isFullScreen` state of
the `EuiImage` ([#6504](elastic/eui#6504))
- Added an extra spacing between the title and subtitle to `EuiTour`
([#6512](elastic/eui#6512))
- Updated `EuiText.blockquote` styles to match the
`EuiMarkdownFormat.blockquote` styles
([#6514](elastic/eui#6514))
- Added the `repositionOnScroll` prop to `EuiToolTip`
([#6515](elastic/eui#6515))

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jason Stoltzfus <jason.stoltzfus@elastic.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests