]> source.dussan.org Git - sonarqube.git/commitdiff
use intl to format numbers, drop numeral (#2770)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Fri, 27 Oct 2017 16:02:15 +0000 (18:02 +0200)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 30 Oct 2017 08:20:37 +0000 (09:20 +0100)
18 files changed:
server/sonar-web/.eslintrc
server/sonar-web/package.json
server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs
server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs
server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs
server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-type-facet.hbs
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/GraphsTooltipsContentCoverage-test.js.snap
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/LanguageDistribution-test.tsx.snap
server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts
server/sonar-web/src/main/js/helpers/handlebars/d.js
server/sonar-web/src/main/js/helpers/handlebars/dt.js
server/sonar-web/src/main/js/helpers/handlebars/fromNow.js
server/sonar-web/src/main/js/helpers/handlebars/numberShort.js [deleted file]
server/sonar-web/src/main/js/helpers/l10n.ts
server/sonar-web/src/main/js/helpers/measures.ts
server/sonar-web/yarn.lock
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index edb289cdb23e131b27f5d4b417a3b9af1e278c76..e62bb03029617cf44d9ca07f5e76da9c666962c3 100644 (file)
@@ -94,7 +94,6 @@
     "no-array-constructor": "warn",
     "no-bitwise": "warn",
     "no-lonely-if": "error",
-    "no-mixed-operators": "warn",
     "no-multi-assign": "warn",
     "no-nested-ternary": "warn",
     "no-new-object": "warn",
index 67eaef0baae8106fe39eb6a7897260fd58d5c2a2..d6cdd87ec695de7f962fe8fee141ce16094234a0 100644 (file)
@@ -24,7 +24,6 @@
     "jquery": "2.2.0",
     "keymaster": "1.6.2",
     "lodash": "4.17.4",
-    "numeral": "1.5.3",
     "prop-types": "15.6.0",
     "rc-tooltip": "3.5.0",
     "react": "15.6.2",
@@ -53,7 +52,6 @@
     "@types/jest": "21.1.5",
     "@types/jquery": "3.2.11",
     "@types/lodash": "4.14.79",
-    "@types/numeral": "0.0.22",
     "@types/prop-types": "15.5.2",
     "@types/react": "16.0.2",
     "@types/react-dom": "15.5.2",
index 60db5dc6d7a12ff03b1f59e22069750f1f32b527..c9b5d3d034a21e59d2179c1b61df6173c80a10d6 100644 (file)
@@ -4,7 +4,7 @@
   {{#each values}}
     <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
       <span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
+      <span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span>
     </a>
   {{/each}}
 </div>
index 9f24a8d450cef1c0843601c06e4148fd5b42040b..f527fac710859cd9a8ac6582a62bb4c153a79224 100644 (file)
@@ -4,7 +4,7 @@
   {{#each values}}
     <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
       <span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
+      <span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span>
     </a>
   {{/each}}
 
index 8550a95df84f8b236ec92cd4dfb58c10e2e7714a..972a2608f8707139fa5090e51b434738c3d1d209 100644 (file)
@@ -4,7 +4,7 @@
   {{#each values}}
     <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-value="{{val}}" title="{{t 'severity' val}}">
       <span class="facet-name">{{severityIcon val}} {{t 'severity' val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
+      <span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span>
     </a>
   {{/each}}
 </div>
index b985ee45f17c7341b0c47ca2d1114aec9a111c85..cfd295f60cf45aa423c42f26d855846e10bb3910 100644 (file)
@@ -5,9 +5,7 @@
     <a class="facet search-navigator-facet js-facet"
        data-value="{{val}}">
       <span class="facet-name">{{issueTypeIcon val}} {{t 'issue.type' val}}</span>
-      <span class="facet-stat">
-        {{numberShort count}}
-      </span>
+      <span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span>
     </a>
   {{/each}}
 </div>
index e40f09b8da2bfe7387bb381c88ad89623b735c0e..96f0e80e56fba44341a226b66a6ab9a12606bc82 100644 (file)
@@ -17,7 +17,7 @@ exports[`should render correctly 1`] = `
       className="project-activity-graph-tooltip-value text-right spacer-right thin"
       colSpan="2"
     >
-      10k
+      10short_number_suffix.k
     </td>
     <td>
       metric.uncovered_lines.name
@@ -56,7 +56,7 @@ exports[`should render correctly when data is missing 1`] = `
       className="project-activity-graph-tooltip-value text-right spacer-right thin"
       colSpan="2"
     >
-      41k
+      41short_number_suffix.k
     </td>
     <td>
       metric.uncovered_lines.name
index d6d546c49317c03fb3e1d8ee1fc489b4625d359e..505f5276139d09ecf23aea4b65306a73d2da5630 100644 (file)
@@ -23,7 +23,7 @@ exports[`renders 1`] = `
             },
           },
           "size": 1734,
-          "tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.reliability_rating.name: –</div><div>metric.security_rating.name: –</div><div>metric.coverage.name: 53.5%</div><div>metric.sqale_index.name: –</div><div>metric.ncloc.name: 1.7k</div></div>",
+          "tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.reliability_rating.name: –</div><div>metric.security_rating.name: –</div><div>metric.coverage.name: 53.5%</div><div>metric.sqale_index.name: –</div><div>metric.ncloc.name: 1.7short_number_suffix.k</div></div>",
           "x": 0,
           "y": 53.5,
         },
index 1d2a6e2c0ebc829d1e7b88d8d45006b5ea296e8c..3397b187c6436d61ab3444b294be4c7d0bde7d1c 100644 (file)
@@ -38,7 +38,7 @@ exports[`renders 1`] = `
   }
   yValues={
     Array [
-      "1.7k",
+      "1.7short_number_suffix.k",
       "845",
       "73",
       "15",
index 875b3eb9f75ef9aa9336fafc43c1380922fc10e1..e0795b485f2f2c31e9470e0a07652086d0eeee24 100644 (file)
@@ -33,7 +33,10 @@ beforeEach(() => {
     'work_duration.about': '~ {0}',
     'metric.level.ERROR': 'Error',
     'metric.level.WARN': 'Warning',
-    'metric.level.OK': 'Ok'
+    'metric.level.OK': 'Ok',
+    'short_number_suffix.k': 'k',
+    'short_number_suffix.m': 'm',
+    'short_number_suffix.b': 'b'
   });
 });
 
index ab4bda610971a0a26fe29e2875e42256d4c46f71..94a4c9e1627d7f768d6cce2a8333baa2665c0f47 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 const { parseDate } = require('../../helpers/dates');
-const { DEFAULT_LANGUAGE } = require('../../helpers/l10n');
+const { getCurrentLocale } = require('../../helpers/l10n');
 
 module.exports = function(date) {
   if (!date) {
     return '';
   }
-  return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE, {
+  return new Intl.DateTimeFormat(getCurrentLocale(), {
     year: 'numeric',
     month: 'long',
     day: 'numeric'
index e5f8e95f1c921a7f5ccd20a88b28aba523b0b605..443c38cb552aad1ad95d361220b16c4b0ccac6ce 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 const { parseDate } = require('../../helpers/dates');
-const { DEFAULT_LANGUAGE } = require('../../helpers/l10n');
+const { getCurrentLocale } = require('../../helpers/l10n');
 
 module.exports = function(date) {
   if (!date) {
     return '';
   }
-  return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE, {
+  return new Intl.DateTimeFormat(getCurrentLocale(), {
     year: 'numeric',
     month: 'long',
     day: 'numeric',
index 590a1dd113d90dd4bcac0f98ec9a08e6c4eefb37..1cf348cba62297a10b9291dc1f2291b4626e4d98 100644 (file)
  */
 const IntlRelativeFormat = require('intl-relativeformat');
 const { parseDate } = require('../../helpers/dates');
-const { DEFAULT_LANGUAGE } = require('../../helpers/l10n');
+const { getCurrentLocale } = require('../../helpers/l10n');
 
 module.exports = function(date) {
-  return new IntlRelativeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE).format(
-    parseDate(date)
-  );
+  return new IntlRelativeFormat(getCurrentLocale()).format(parseDate(date));
 };
diff --git a/server/sonar-web/src/main/js/helpers/handlebars/numberShort.js b/server/sonar-web/src/main/js/helpers/handlebars/numberShort.js
deleted file mode 100644 (file)
index 0683bd5..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-const numeral = require('numeral');
-
-module.exports = function(number) {
-  let format = '0,0';
-  if (number >= 10000) {
-    format = '0.[0]a';
-  }
-  if (number >= 100000) {
-    format = '0a';
-  }
-  return numeral(number).format(format);
-};
index aa54768bea1b77412e3e75ac25539f02bf300127..bdcac774a28a758313366eb729ce5912b395d1e9 100644 (file)
@@ -175,3 +175,11 @@ export function getLocalizedMetricDomain(domainName: string) {
   const fromBundle = translate(bundleKey);
   return fromBundle !== bundleKey ? fromBundle : domainName;
 }
+
+export function getCurrentLocale() {
+  // check `window && window.localStorage` for tests
+  return (
+    (window && window.localStorage && window.localStorage.getItem('l10n.locale')) ||
+    DEFAULT_LANGUAGE
+  );
+}
index c0e89838f5971097b647095c5da05c2045bc72c1..e1c4c46e58ee31b6133ef7bdcebd40905443c3bf 100644 (file)
@@ -17,8 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import * as numeral from 'numeral';
-import { translate, translateWithParameters } from './l10n';
+import { translate, translateWithParameters, getCurrentLocale } from './l10n';
 
 const HOURS_IN_DAY = 8;
 
@@ -134,8 +133,16 @@ function getVariationFormatter(type: string): Formatter {
   return FORMATTERS[type] || noFormatter;
 }
 
-function genericNumberFormatter(value: number, formatValue?: string): string {
-  return numeral(value).format(formatValue);
+function numberFormatter(
+  value: number,
+  minimumFractionDigits = 0,
+  maximumFractionDigits = minimumFractionDigits
+) {
+  const { format } = new Intl.NumberFormat(getCurrentLocale(), {
+    minimumFractionDigits,
+    maximumFractionDigits
+  });
+  return format(value);
 }
 
 function noFormatter(value: string | number): string | number {
@@ -147,22 +154,26 @@ function emptyFormatter(): string {
 }
 
 function intFormatter(value: number): string {
-  return genericNumberFormatter(value, '0,0');
+  return numberFormatter(value);
 }
 
 function intVariationFormatter(value: number): string {
-  return genericNumberFormatter(value, '+0,0');
+  const prefix = value < 0 ? '-' : '+';
+  return prefix + intFormatter(Math.abs(value));
 }
 
 function shortIntFormatter(value: number): string {
-  let format = '0,0';
-  if (value >= 1000) {
-    format = '0.[0]a';
-  }
-  if (value >= 10000) {
-    format = '0a';
+  if (value >= 1e9) {
+    return numberFormatter(value / 1e9) + translate('short_number_suffix.b');
+  } else if (value >= 1e6) {
+    return numberFormatter(value / 1e6) + translate('short_number_suffix.m');
+  } else if (value >= 1e4) {
+    return numberFormatter(value / 1e3) + translate('short_number_suffix.k');
+  } else if (value >= 1e3) {
+    return numberFormatter(value / 1e3, 0, 1) + translate('short_number_suffix.k');
+  } else {
+    return numberFormatter(value);
   }
-  return genericNumberFormatter(value, format);
 }
 
 function shortIntVariationFormatter(value: number): string {
@@ -171,11 +182,12 @@ function shortIntVariationFormatter(value: number): string {
 }
 
 function floatFormatter(value: number): string {
-  return genericNumberFormatter(value, '0,0.0[0000]');
+  return numberFormatter(value, 1, 5);
 }
 
 function floatVariationFormatter(value: number): string {
-  return value === 0 ? '+0.0' : genericNumberFormatter(value, '+0,0.0[0000]');
+  const prefix = value < 0 ? '-' : '+';
+  return prefix + floatFormatter(Math.abs(value));
 }
 
 function percentFormatter(value: string | number, options: { decimals?: number } = {}): string {
@@ -183,9 +195,9 @@ function percentFormatter(value: string | number, options: { decimals?: number }
     value = parseFloat(value);
   }
   if (options.decimals) {
-    return genericNumberFormatter(value / 100, `0,0.${'0'.repeat(options.decimals)}%`);
+    return numberFormatter(value, options.decimals) + '%';
   }
-  return value === 100 ? '100%' : genericNumberFormatter(value / 100, '0,0.0%');
+  return value === 100 ? '100%' : numberFormatter(value, 1) + '%';
 }
 
 function percentVariationFormatter(
@@ -195,10 +207,8 @@ function percentVariationFormatter(
   if (typeof value === 'string') {
     value = parseFloat(value);
   }
-  if (options.decimals) {
-    return genericNumberFormatter(value / 100, `+0,0.${'0'.repeat(options.decimals)}%`);
-  }
-  return value === 0 ? '+0.0%' : genericNumberFormatter(value / 100, '+0,0.0%');
+  const prefix = value < 0 ? '-' : '+';
+  return prefix + percentFormatter(Math.abs(value), options);
 }
 
 function ratingFormatter(value: string | number): string {
index d7a273aa92a11f3b6f98f10452a75e9b22c72296..cb2f50f3c135f6c46f6d67845daa7d6a6f1a1aa8 100644 (file)
   version "8.0.22"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.22.tgz#9c6bfee1f45f5e9952ff6b487e657ecca48c7777"
 
-"@types/numeral@0.0.22":
-  version "0.0.22"
-  resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93"
-
 "@types/prop-types@15.5.2":
   version "15.5.2"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.2.tgz#3c6b8dceb2906cc87fe4358e809f9d20c8d59be1"
@@ -5258,10 +5254,6 @@ number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
 
-numeral@1.5.3:
-  version "1.5.3"
-  resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62"
-
 "nwmatcher@>= 1.3.9 < 2.0.0":
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f"
index 764787f4ee0f4cca6259c902e9af3da465bf67a7..becdeecb63de6d202a7b06671984fc2b39332016 100644 (file)
@@ -217,6 +217,9 @@ no_results_search.favorites.2=Would you like to search among {url} projects?
 page_extension_failed=Page extension failed.
 set_as_default=Set as Default
 unset_as_default=Unset as Default
+short_number_suffix.k=k
+short_number_suffix.m=m
+short_number_suffix.b=b
 show_more=Show More
 should_be_unique=Should be unique
 since_x=since {0}