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

add support for number format internationalization #14019

Merged
merged 1 commit into from
Nov 22, 2017

Conversation

fbaligand
Copy link
Contributor

@fbaligand fbaligand commented Sep 16, 2017

This PR adds a new advanced setting named "format:language".
This setting allows to choose a language to format numbers, among all numeraljs supported languages.
Number format is about decimal delimiter, thousand delimiter and currency symbol.

This feature is particularly useful to format every language specific number formats (not only english).

Precision : given how numeraljs works, it could only be a global option, this can't be customized for each field. Up to me, not a problem since it would be weird to display on a same dashboard different numeral fields with different language formats.

Fixes #9228.
Partially fixes #8983.

value: 'en',
type: 'select',
options: ['be-nl', 'chs', 'cs', 'da-dk', 'de-ch', 'de', 'en', 'en-gb', 'es-ES', 'es', 'et', 'fi', 'fr-CA', 'fr-ch', 'fr', 'hu', 'it', 'ja', 'nl-nl', 'pl', 'pt-br', 'pt-pt', 'ru-UA', 'ru', 'sk', 'th', 'tr', 'uk-UA'],
description: '<a href="http://numeraljs.com/" target="_blank">numeral language</a>'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add rel="noopener" to this link.

@@ -26,6 +26,15 @@ export class Numeral extends FieldFormat {
}

getParamDefaults() {
var language = this.getConfig('format:language');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var -> const

@@ -26,6 +26,15 @@ export class Numeral extends FieldFormat {
}

getParamDefaults() {
var language = this.getConfig('format:language');
if (language != numeral.language()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!=!==

var language = this.getConfig('format:language');
if (language != numeral.language()) {
if (language != 'en') {
var languageData = require('@elastic/numeral/languages/' + language);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a template string here.

@@ -26,6 +26,15 @@ export class Numeral extends FieldFormat {
}

getParamDefaults() {
var language = this.getConfig('format:language');
if (language != numeral.language()) {
if (language != 'en') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!=!==

@fbaligand
Copy link
Contributor Author

@timroes
Done !

Copy link
Contributor

@timroes timroes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -226,6 +226,12 @@ export function getUiSettingDefaults() {
value: '($0,0.[00])',
description: 'Default <a href="http://numeraljs.com/" target="_blank">numeral format</a> for the "currency" format'
},
'format:language': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider moving this to format:numeral:language as this is only where it's used currently?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 to making this setting more specific. I'm also slightly concerned about how this is a global setting. Perhaps format:number:defaultLanguage will keep it in line with format:number:defaultPattern and leaves room for a per-field language setting should we want to add one down the road.

Copy link
Contributor Author

@fbaligand fbaligand Sep 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

format:number:defaultLanguage looks a good idea.
That said, few points about that :

  • first, this setting will apply on every type format concerned by numeraljs, so : number, bytes, percent, and currency. Not only number.
  • then, currently (and since a long time), numeraljs allows only to define a global static language, not one for each formatter.
  • would it make sense to have a dashboard with different numeric fields formatted with different languages ?
  • in the future, an interesting evolution could be to detect browser language.

=> Given these points, what setting name do you choose ? :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numeraljs allows only to define a global static language

ugh, bummer. Is it unrealistic to call numeral.language() before every conversion?

would it make sense to have a dashboard with different numeric fields formatted with different languages?

The numeral language defines the currency symbol right? If so I think it makes sense to have a unique language per field if two fields represent different currencies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like numeral.language() is just setting an internal value inside the library, so if we called it before calling numeralInst.set().format(this.param('pattern')) we could use a different language for each field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do move forward with applying this language to all instances of numeral, it'd be more consistent to do so in the manner described in this comment

Copy link
Contributor Author

@fbaligand fbaligand Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger I agree with you, it's fine to have defaultLanguage here to be able to add per-field value later.
And yes, numeral language defines the currency symbol.
That said, in original numeraljs project (https://github.com/adamwdraper/Numeral-js), the last version use the "locale" term instead of "language" term.
So question is : do you prefer format:number:defaultLanguage or format:number:defaultLocale

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too concerned about updates in numeral; I don't think we'll ever upgrade because of it's breaking changes and how that would impact everyone who has saved formats already. It's more likely that we will do something like we have done with kuery and offer a choice between legacy numeral and some custom solution we could build in the future.

That said, I think that locale is more accurate description of the choice, so I would go with format:number:defaultLocale

Copy link
Contributor Author

@fbaligand fbaligand Sep 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, for this new name format:number:defaultLocale, I will commit it.

Copy link
Contributor Author

@fbaligand fbaligand Sep 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger
new name format:number:defaultLocale is commited

const language = this.getConfig('format:language');
if (language !== numeral.language()) {
if (language !== 'en') {
var languageData = require(`@elastic/numeral/languages/${language}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kobelb
Sorry for this mistalke. I just pushed the fix.

@@ -26,6 +26,15 @@ export class Numeral extends FieldFormat {
}

getParamDefaults() {
const language = this.getConfig('format:language');
Copy link
Contributor

@kobelb kobelb Sep 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really shouldn't be setting the numeral language here, this method is only meant to return the param defaults. This situation appears to be very similar to setting the timezone for moment which is being done in the KibanaRootController here but then we're also having to do so in other app controllers, example here.

Perhaps @spalger can comment on a better place to put logic like this without these deficiencies.

Additionally, I have hesitations to setting the language for the global instance of numeral as this could have unintended side-effects outside of modifying the behavior of field formatters specifically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it's probably not the best place to set this code.
If you find a better place, I would be glad to hear it.
I set it there because this method is called at app loading beginning and is rarely called then.

Concerning your hesitations, currently, numeraljs allows only to set this setting globally, not locally for each formatter. Then, would it make sense to have several language formats on the same kibana app ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thread is kind of merging with #14019 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger do you see a better place (method) to put the javascript code which changes numeral language ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just put it in the _convert function, then adding per-field params is just about exposing the UI to do so.

Copy link
Contributor Author

@fbaligand fbaligand Sep 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I set it there, it would require static import of all languages.
Because, dynamic require call shouldn't be called at convert time, as it is an asynchronous load.

'format:language': {
value: 'en',
type: 'select',
options: ['be-nl', 'chs', 'cs', 'da-dk', 'de-ch', 'de', 'en', 'en-gb', 'es-ES', 'es', 'et', 'fi', 'fr-CA', 'fr-ch', 'fr', 'hu', 'it', 'ja', 'nl-nl', 'pl', 'pt-br', 'pt-pt', 'ru-UA', 'ru', 'sk', 'th', 'tr', 'uk-UA'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a shame that we're hard-coding these, but I'm not seeing a way to access all possible languages...

Copy link
Contributor Author

@fbaligand fbaligand Sep 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kobelb
I think exactly like you :)
I'm sad to hard-code these values, but I didn't find another way to do that.
That said, this array is likely to change very rarely (maybe never)...

const language = this.getConfig('format:language');
if (language !== numeral.language()) {
if (language !== 'en') {
const languageData = require(`@elastic/numeral/languages/${language}`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger @kobelb
Do you see a better way to load language data ?
Ideally, I would like to load statically all languages data to store them in numeral language map.
I try import from @elastic/numeral/languages'; but it doesn't work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fbaligand feel free to add the ability to do so at https://github.com/elastic/numeral-js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger
One option for that is to use v3.0.0 that is already released. This version is compatible with static import. We can change the package.json file to depend on this version.
Are you OK to depend on this version ? Or do you consider this major version implies so much breaking changes ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger
I just commited migration to @elastic/numeral v3.0.0
I had no particular problem, it works fine !
And now I can import successfully all languages using a static way.
And so, I moved the code to _convert method as you wish.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger
What do you think about ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should have deprecated @elastic/[email protected] months ago. Unfortunately the way that numeral transforms format strings in v3 is not backwards compatible with v2. This means that in order to upgrade to numeral v3 we would either have to track down all the differences between the two versions, or users to recreate/double-check all of their numeric field formatters. We're currently not planning to do either, so this isn't going to work. I've updated elastic/numeral-js to reflect the current state of the package and changed https://github.com/elastic/numeral-js/blob/kibana-fork/languages.js

The file now exports the languages as an array. each item has an id, name, and lang property.

  • id: the filename of the lang
  • name: the english description of the language, extracted from each language file
  • lang: the value that should be passed to numeral.language(id, lang)

The file doesn't automatically register the languages, maybe we want it to do that, but it should be a suitable replacement for the current languages import, right? To test it out install @elastic/numeral from github:

npm install https://github.com/elastic/numeral-js#kibana-fork

Copy link
Contributor Author

@fbaligand fbaligand Sep 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger
OK.
Thanks for all your work to transform "languages.js" in a way it can be imported easily !
So I'm ready to cancel my upgrade to 3.0.0 and write the code to register all languages in _numeral.js using your new languages.js version.
Hope to test it tonight.

@spalger
Copy link
Contributor

spalger commented Sep 27, 2017

The conversation is flattened in my view of the PR, but I responded here

@fbaligand
Copy link
Contributor Author

@spalger
As you, I answered to the "old" conversation here.
Next time, if you prefer that I write a new comment, tell me.

@fbaligand
Copy link
Contributor Author

@spalger
OK, I just cancelled my upgrade to 3.0.0 and wrote the code to register at startup, all languages in _numeral.js using your new languages.js version.
=> what do you think about ?

'format:number:defaultLocale': {
value: 'en',
type: 'select',
options: ['be-nl', 'chs', 'cs', 'da-dk', 'de-ch', 'de', 'en', 'en-gb', 'es-ES', 'es', 'et', 'fi', 'fr-CA', 'fr-ch', 'fr', 'hu', 'it', 'ja', 'nl-nl', 'pl', 'pt-br', 'pt-pt', 'ru-UA', 'ru', 'sk', 'th', 'tr', 'uk-UA'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to replace this array with the ids from @elastic/numeral/languages right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be great to dynamically inject all language ids from "languages.js" array.
That said, this configuration file seems to be completely static.
=> do you know how to dynamically object values here ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can require files at the top just like everything else, it's not actually static, just doesn't have any dependencies yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, sorry for this remark. Even more, I just saw that there is already one dependency.
I will fix this file to dynamically inject array from "languages.js".

@spalger
Copy link
Contributor

spalger commented Sep 29, 2017

Jenkins, test this

@fbaligand
Copy link
Contributor Author

@spalger
So that Jenkins build works, it should require a new release of "numeral" package, right ?

@spalger
Copy link
Contributor

spalger commented Sep 29, 2017

Looks like jenkins build failed for linting reasons (run npm run lint locally) but you're right, I need to publish that.

@spalger
Copy link
Contributor

spalger commented Sep 29, 2017

Just published @elastic/numeral 2.3.0

@fbaligand
Copy link
Contributor Author

Thanks for the release !
I will import it, and fix the lint issues detected by Jenkins

@fbaligand
Copy link
Contributor Author

fbaligand commented Sep 29, 2017

@spalger
I just fixed jenkins lint issues, changed dependency to '@elastic/numeral' version 2.3.0, and injected dynamically language ids into format:number:defaultLocale setting options.

I guess I can't launch Jenkins tests for myself. Could you launch Jenkins tests ?

@spalger
Copy link
Contributor

spalger commented Sep 29, 2017

Jenkins, test this

@fbaligand
Copy link
Contributor Author

@spalger
Given Jenkins build logs, there are not lint errors anymore.

There are errors during tests. The main problem seems to be :

22:29:22 Chrome 59.0.3071 (Linux 0.0.0) Range Agg formating formats bucket keys properly FAILED
22:29:22 	TypeError: this.getConfig is not a function
22:29:22 	    at Class._convert (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:420651:33)
22:29:22 	    at recurse (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:191577:25)
22:29:22 	    at FieldFormatFromConverter._convert (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:308585:46)
22:29:22 	    at recurse (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:191577:25)
22:29:22 	    at format (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:473843:37)
22:29:22 	    at Context.<anonymous> (http://localhost:5610/bundles/tests.bundle.js?shards=4&shard_num=2:473845:30)

I don't arrive to localize this test in Kibana sources, given the test label.
But it seems this error is because FieldFormatFromConverter calls directly FieldFormat, so it doesn't call _numeral Class constructor with getConfig parameter. That explains error above.

Honestly, I don't know how to fix this test. Any idea ?

@fbaligand
Copy link
Contributor Author

fbaligand commented Sep 30, 2017

@spalger
A way to work anyway would be to put this code line :
const defaultLocale = this.getConfig && this.getConfig('format:number:defaultLocale') || 'en';

=> What do you think about ?

@fbaligand
Copy link
Contributor Author

@spalger
I just published const defaultLocale = this.getConfig && this.getConfig('format:number:defaultLocale') || 'en'; to fix jenkins tests.
=> could you launch jenkins tests ?

@kobelb
Copy link
Contributor

kobelb commented Oct 10, 2017

jenkins, test this

@fbaligand
Copy link
Contributor Author

@spalger @kobelb @nreese
Yes, jenkins build is OK !

=> Is this OK to merge the PR ?

Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kobelb
Copy link
Contributor

kobelb commented Oct 11, 2017

I believe I brought this up earlier, and it might've already been addressed... are we not concerned that we're modifying the global numeral language from within the numeral field_formatter? It's conceivable that another Kibana application would be relying upon Numeral to do it's own formatting, and this would affect it's behavior as well.

This could be incredibly confusing to the user in the following scenario. If there were two applications Kibana: App-1 relies upon numeraljs and NOT our field formatters; App-2 relies upon our field formatters and in turn numeraljs. When the user first opens Kibana they go directly to App-1, bypassing the field-formatter code that sets the default language. They then navigate to App-2 which is setting the global numeral language. Later if they go back to App-1 they see the number formats change.

@spalger
Copy link
Contributor

spalger commented Nov 21, 2017

Ah, sorry about that @fbaligand go ahead and keep your version.

Can you try running the tests locally and see if they fail?

In one terminal tab run:

npm run test:ui:server

Once the Kibana server has started up in the first terminal run this in a second tab:

node scripts/functional_test_runner --grep "management scripted fields"

That will run the tests in test/functional/apps/management/_scripted_fields.js and might give you an indication if this is caused be the number formatters changing (a possibility if you ask me)

@fbaligand
Copy link
Contributor Author

@spalger
I just pushed my rebased code, which fixes the removed code "opts.afterConvert" from your merge.
Maybe it was the cause of the failed selenium test ?
=> Could you run jenkins tests to see if build is fixed by my last push ?

Now, I have to go to sleep ;)

@spalger
Copy link
Contributor

spalger commented Nov 21, 2017

Okay, that failure wasn't because of your code, for sure. One more jenkins run should do it

@fbaligand
Copy link
Contributor Author

@kobelb @spalger
Yeah! Jenkins build is successful!

=> so now, what's the next step?

@fbaligand
Copy link
Contributor Author

Thanks @kobelb for the approval !

@fbaligand
Copy link
Contributor Author

@spalger @kobelb
So is it OK to merge the PR ?

@kobelb
Copy link
Contributor

kobelb commented Nov 22, 2017

@fbaligand yup! I can merge this a little later today. Thanks for working through this with us and your patience.

@fbaligand
Copy link
Contributor Author

Great !
It was a pleasure to work with all of you on this PR !

@fbaligand
Copy link
Contributor Author

@spalger
Thanks for the merge !!
Very happy that this feature is now merged and soon part of Kibana 6.2 !

@fbaligand
Copy link
Contributor Author

Is it too late to merge it in Kibana 6.1 ?

@nreese
Copy link
Contributor

nreese commented Nov 25, 2017

Is it too late to merge it in Kibana 6.1 ?

Yes

@fbaligand
Copy link
Contributor Author

Ok. Thanks for the answer @nreese.

@fbaligand fbaligand deleted the numeral-language branch November 25, 2017 18:04
@spalger
Copy link
Contributor

spalger commented Nov 30, 2017

6.x/6.2: 91777d2

@leeway23
Copy link

Hi all,

would anyone know from which Kibana version a problem previously discussed in (and later merged with this discussion) :
#7543
is going to be fixed ?

Thanks & Cheers, J.

@MarcelZOI
Copy link

MarcelZOI commented Aug 28, 2018

Hey, i currently looking how to change the number format of a column in visualize to german format. what is the current state? Is there a possibility to achive this?
Greetings

Marcel

(currently i use kibana 5.1.1 and i wanna know is this feature released and it is worth to upgrade?)

@fbaligand
Copy link
Contributor Author

Hi @MarcelZOI

As you can see in this PR tags, this feature is available since Kibana 6.2.0.

Important point : this setting is a Kibana advanced setting, so it is global. You currently can't have a custom locale per field.

@MarcelZOI
Copy link

@fbaligand okay, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add format:defaultLanguage in Advanced Settings Field format : add language option
8 participants