diff --git a/packages/core/src/components/axes/axis.ts b/packages/core/src/components/axes/axis.ts
index 8d7f09ce2a..7f62726878 100644
--- a/packages/core/src/components/axes/axis.ts
+++ b/packages/core/src/components/axes/axis.ts
@@ -224,6 +224,7 @@ export class Axis extends Component {
// create the right ticks formatter
let formatter: any
const userProvidedFormatter = getProperty(axisOptions, 'ticks', 'formatter')
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
if (isTimeScaleType) {
const timeInterval = computeTimeIntervalName(
axis.tickValues(),
@@ -232,7 +233,7 @@ export class Axis extends Component {
if (userProvidedFormatter === null) {
formatter = (t: number, i: number) =>
- formatTick(t, i, axis.tickValues(), timeInterval, timeScaleOptions)
+ formatTick(t, i, axis.tickValues(), timeInterval, timeScaleOptions, options.locale)
} else {
formatter = (t: number, i: number) => {
const defaultFormattedValue = formatTick(
@@ -240,7 +241,8 @@ export class Axis extends Component {
i,
axis.tickValues(),
timeInterval,
- timeScaleOptions
+ timeScaleOptions,
+ options.locale
)
return userProvidedFormatter(t, i, defaultFormattedValue)
}
@@ -248,7 +250,7 @@ export class Axis extends Component {
} else {
if (userProvidedFormatter === null) {
if (scaleType === ScaleTypes.LINEAR) {
- formatter = (t: any) => t.toLocaleString()
+ formatter = (t: any) => numberFormatter(t, localeCode)
}
} else {
formatter = userProvidedFormatter
diff --git a/packages/core/src/components/axes/ruler-binned.ts b/packages/core/src/components/axes/ruler-binned.ts
index 0c135fc098..6b060285ac 100644
--- a/packages/core/src/components/axes/ruler-binned.ts
+++ b/packages/core/src/components/axes/ruler-binned.ts
@@ -100,7 +100,10 @@ export class BinnedRuler extends Ruler {
...(getProperty(options, 'tooltip', 'showTotal') === true
? [
{
- label: get(options, 'tooltip.totalLabel') || 'Total',
+ label:
+ get(options, 'locale.translations.total') ||
+ get(options, 'tooltip.totalLabel') ||
+ 'Total',
value: activeDataGroupNames.reduce(
(accum: number, currentValue: any) =>
accum + parseFloat(get(sampleMatchData, `data.${currentValue}`)),
diff --git a/packages/core/src/components/axes/toolbar.ts b/packages/core/src/components/axes/toolbar.ts
index db334ed624..095d6b5ce8 100644
--- a/packages/core/src/components/axes/toolbar.ts
+++ b/packages/core/src/components/axes/toolbar.ts
@@ -159,7 +159,7 @@ export class Toolbar extends Component {
.classed('cds--overflow-menu-options__option--disabled', (d: any) => d.shouldBeDisabled())
.attr('aria-disabled', (d: any) => d.shouldBeDisabled())
.selectAll('button')
- .text((d: any) => d.text)
+ .text((d: any) => d.title)
}
isOverflowMenuOpen() {
@@ -367,7 +367,6 @@ export class Toolbar extends Component {
getControlConfigs() {
const numberOfIcons = getProperty(this.getOptions(), 'toolbar', 'numberOfIcons') - 1
const controls = getProperty(this.getOptions(), 'toolbar', 'controls')
-
const overflowSpecificControls: any[] = []
const buttonList: any[] = []
const overflowList: any[] = []
@@ -468,6 +467,13 @@ export class Toolbar extends Component {
!this.services.zoom.isEmptyState()
const displayData = this.model.getDisplayData()
+ const options = this.model.getOptions()
+ const { exportAsCSV, exportAsJPG, exportAsPNG } = getProperty(
+ options,
+ 'locale',
+ 'translations',
+ 'toolbar'
+ )
let controlConfig: any
switch (controlType) {
@@ -539,7 +545,7 @@ export class Toolbar extends Component {
case ToolbarControlTypes.EXPORT_CSV:
controlConfig = {
id: 'toolbar-export-CSV',
- title: 'Export as CSV',
+ title: exportAsCSV,
shouldBeDisabled: () => false,
iconSVG: {
content: this.getControlIconByType(controlType)
@@ -550,7 +556,7 @@ export class Toolbar extends Component {
case ToolbarControlTypes.EXPORT_PNG:
controlConfig = {
id: 'toolbar-export-PNG',
- title: 'Export as PNG',
+ title: exportAsPNG,
shouldBeDisabled: () => false,
iconSVG: {
content: this.getControlIconByType(controlType)
@@ -561,7 +567,7 @@ export class Toolbar extends Component {
case ToolbarControlTypes.EXPORT_JPG:
controlConfig = {
id: 'toolbar-export-JPG',
- title: 'Export as JPG',
+ title: exportAsJPG,
shouldBeDisabled: () => false,
iconSVG: {
content: this.getControlIconByType(controlType)
diff --git a/packages/core/src/components/essentials/color-scale-legend.ts b/packages/core/src/components/essentials/color-scale-legend.ts
index 87aeee5586..c828cccfcd 100644
--- a/packages/core/src/components/essentials/color-scale-legend.ts
+++ b/packages/core/src/components/essentials/color-scale-legend.ts
@@ -157,9 +157,12 @@ export class ColorScaleLegend extends Legend {
// Create scale & ticks
const linearScale = scaleLinear().domain(domain).range([0, barWidth])
-
const legendAxis = axisBottom(linearScale).tickSize(0).tickValues(quant)
+ //translating ticks into given locale language
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
+ legendAxis.tickFormat(d => numberFormatter(d, localeCode))
+
let rangeStart: any // avoid unexpected lexical declaration in case block
switch (colorScaleType) {
case ColorLegendType.LINEAR:
diff --git a/packages/core/src/components/essentials/modal.ts b/packages/core/src/components/essentials/modal.ts
index 5eb8a1d824..4dfa28d732 100644
--- a/packages/core/src/components/essentials/modal.ts
+++ b/packages/core/src/components/essentials/modal.ts
@@ -73,6 +73,8 @@ export class Modal extends Component {
const options = this.model.getOptions()
+ const { title, downloadAsCSV } = getProperty(options, 'locale', 'translations', 'tabularRep')
+
const chartprefix = getProperty(options, 'style', 'prefix')
const tableArray = this.model.getTabularDataArray()
@@ -80,7 +82,8 @@ export class Modal extends Component {
return `
`
}
diff --git a/packages/core/src/components/essentials/threshold.ts b/packages/core/src/components/essentials/threshold.ts
index 63c666b104..419ddb0535 100644
--- a/packages/core/src/components/essentials/threshold.ts
+++ b/packages/core/src/components/essentials/threshold.ts
@@ -201,7 +201,7 @@ export class Threshold extends Component {
const { value, axisPosition } = datum
const options = this.getOptions()
const scaleType = this.services.cartesianScales.getScaleTypeByPosition(axisPosition)
-
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
// If scale is time, format the threshold date as the ticks format
if (scaleType === ScaleTypes.TIME) {
const isVertical = [AxisPositions.LEFT, AxisPositions.RIGHT].includes(axisPosition)
@@ -215,10 +215,10 @@ export class Threshold extends Component {
getProperty(timeScaleOptions, 'timeInterval')
)
- return formatTick(value, 0, scale.ticks(), timeInterval, timeScaleOptions)
+ return formatTick(value, 0, scale.ticks(), timeInterval, timeScaleOptions, options.locale)
}
- return value.toLocaleString('en')
+ return numberFormatter(value, localeCode)
}
appendThresholdLabel() {
diff --git a/packages/core/src/components/essentials/title-meter.ts b/packages/core/src/components/essentials/title-meter.ts
index 07afaea82e..ebe6a45e22 100644
--- a/packages/core/src/components/essentials/title-meter.ts
+++ b/packages/core/src/components/essentials/title-meter.ts
@@ -18,7 +18,7 @@ export class MeterTitle extends Title {
const options = this.getOptions()
const svg = this.getComponentContainer()
const { groupMapsTo } = options.data
-
+ const meterTitle = options.locale.translations.meter.title
const proportional = getProperty(options, 'meter', 'proportional')
if (proportional) {
@@ -26,8 +26,9 @@ export class MeterTitle extends Title {
this.displayBreakdownTitle()
} else {
// the title for a meter, is the label for that dataset
- const title = svg.selectAll('text.meter-title').data([dataset[groupMapsTo]])
-
+ const title = svg
+ .selectAll('text.meter-title')
+ .data(meterTitle ? [meterTitle] : [dataset[groupMapsTo]])
title
.enter()
.append('text')
@@ -73,13 +74,14 @@ export class MeterTitle extends Title {
const difference = total !== null ? total - datasetsTotal : datasetsTotal
//breakdownFormatter
const breakdownFormatter = getProperty(options, 'meter', 'proportional', 'breakdownFormatter')
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
data =
breakdownFormatter !== null
? breakdownFormatter({
datasetsTotal: datasetsTotal,
total: total
})
- : `${datasetsTotal} ${unit} used (${difference} ${unit} available)`
+ : `${numberFormatter(datasetsTotal, localeCode)} ${unit} used (${numberFormatter(difference, localeCode)} ${unit} available)`
}
// the breakdown part to whole of the datasets to the overall total
@@ -121,9 +123,12 @@ export class MeterTitle extends Title {
// totalFormatter function
const totalFormatter = getProperty(options, 'meter', 'proportional', 'totalFormatter')
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
const totalString =
- totalFormatter !== null ? totalFormatter(totalValue) : `${total} ${unit} total`
+ totalFormatter !== null
+ ? totalFormatter(totalValue)
+ : `${numberFormatter(total, localeCode)} ${unit} total`
const containerBounds = DOMUtils.getHTMLElementSize(this.services.domUtils.getMainContainer())
@@ -214,7 +219,7 @@ export class MeterTitle extends Title {
*/
appendPercentage() {
const dataValue = getProperty(this.model.getDisplayData(), 0, 'value')
-
+ const { code: localeCode, number: numberFormatter } = getProperty(this.getOptions(), 'locale')
// use the title's position to append the percentage to the end
const svg = this.getComponentContainer()
const title = DOMUtils.appendOrSelect(svg, 'text.meter-title')
@@ -237,7 +242,7 @@ export class MeterTitle extends Title {
.append('text')
.classed('percent-value', true)
.merge(percentage as any)
- .text((d: any) => `${d}%`)
+ .text((d: any) => `${numberFormatter(d, localeCode)}%`)
.attr('x', +title.attr('x') + title.node().getComputedTextLength() + offset) // set the position to after the title
.attr('y', title.attr('y'))
diff --git a/packages/core/src/components/essentials/tooltip-axis.ts b/packages/core/src/components/essentials/tooltip-axis.ts
index 184d1af5a5..21b83b2470 100644
--- a/packages/core/src/components/essentials/tooltip-axis.ts
+++ b/packages/core/src/components/essentials/tooltip-axis.ts
@@ -73,7 +73,7 @@ export class AxisChartsTooltip extends Tooltip {
}
items.push({
- label: options.tooltip.groupLabel,
+ label: get(options, 'locale.translations.group') || get(options, 'tooltip.groupLabel'),
value: datum[groupMapsTo],
color: this.model.getFillColor(datum[groupMapsTo]),
class: this.model.getColorClassName({
@@ -113,7 +113,10 @@ export class AxisChartsTooltip extends Tooltip {
// use the primary/only range id
const rangeIdentifier = cartesianScales.getRangeIdentifier()
items.push({
- label: get(options, 'tooltip.totalLabel') || 'Total',
+ label:
+ get(options, 'locale.translations.total') ||
+ get(options, 'tooltip.totalLabel') ||
+ 'Total',
value: data.reduce(
(accumulator: number, datum: any) => accumulator + datum[rangeIdentifier],
0
diff --git a/packages/core/src/components/essentials/tooltip.ts b/packages/core/src/components/essentials/tooltip.ts
index 5385375181..6bb006b398 100644
--- a/packages/core/src/components/essentials/tooltip.ts
+++ b/packages/core/src/components/essentials/tooltip.ts
@@ -1,5 +1,4 @@
import { select, pointer } from 'd3'
-import { format } from 'date-fns/format'
import Position, { PLACEMENTS } from '@carbon/utils-position' // position service
import { getProperty, truncateLabel } from '@/tools'
import { zoomBar as zoomBarConfigs, tooltips as tooltipConfigs } from '@/configuration'
@@ -182,25 +181,35 @@ export class Tooltip extends Component {
valueFormatter(value: any, label: string) {
const options = this.getOptions()
const valueFormatter = getProperty(options, 'tooltip', 'valueFormatter')
+ const {
+ code: localeCode,
+ number: numberFormatter,
+ date: dateFormatter
+ } = getProperty(options, 'locale')
if (valueFormatter) {
return valueFormatter(value, label)
}
if (typeof value.getTime === 'function') {
- return format(value, 'MMM d, yyyy')
+ return dateFormatter(value, localeCode, { month: 'short', day: 'numeric', year: 'numeric' })
}
try {
// it's a correct ISO format Date string
if (typeof value === 'string' && /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(value)) {
- return format(Date.parse(value), 'MMM d, yyyy')
+ const newDate = new Date(value)
+ return dateFormatter(newDate, localeCode, {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ })
}
} catch (e) {
// not a valid ISO format string
}
- return value.toLocaleString()
+ return numberFormatter(value, localeCode)
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
diff --git a/packages/core/src/components/graphs/alluvial.ts b/packages/core/src/components/graphs/alluvial.ts
index 443b935fef..cb2c1f4823 100644
--- a/packages/core/src/components/graphs/alluvial.ts
+++ b/packages/core/src/components/graphs/alluvial.ts
@@ -252,6 +252,7 @@ export class Alluvial extends Component {
this.services.domUtils.generateElementIDString(`alluvial-node-title-${d.index}`)
)
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
// Node title - text
textNode
.append('text')
@@ -267,7 +268,7 @@ export class Alluvial extends Component {
// shift 13 pixels down to fit background container
.attr('dy', 13)
.text((d: any) => {
- return `${d.name} (${d.value})`
+ return `${d.name} (${numberFormatter(d.value, localeCode)})`
})
.attr('aria-label', (d: any) => {
return `${d.name} (${d.value})`
@@ -327,6 +328,7 @@ export class Alluvial extends Component {
addLineEventListener() {
const options = this.getOptions()
const self = this
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
// Set delay to counter flashy behaviour
const debouncedLineHighlight = debounce((link, event = 'mouseover') => {
@@ -379,7 +381,10 @@ export class Alluvial extends Component {
items: [
{
label: datum.target.name,
- value: datum.value + (options.alluvial.units ? ` ${options.alluvial.units}` : ''),
+ value:
+ (numberFormatter(datum.value, localeCode)
+ ? `${numberFormatter(datum.value, localeCode)}`
+ : '-') + (options.alluvial.units ? ` ${options.alluvial.units}` : ''),
color: strokeColor,
labelIcon: self.getRightArrowIcon()
}
diff --git a/packages/core/src/components/graphs/boxplot.ts b/packages/core/src/components/graphs/boxplot.ts
index 26e6fece2c..5d4f08e624 100644
--- a/packages/core/src/components/graphs/boxplot.ts
+++ b/packages/core/src/components/graphs/boxplot.ts
@@ -1,5 +1,5 @@
import { select } from 'd3'
-import { flipDomainAndRangeBasedOnOrientation, generateSVGPathString } from '@/tools'
+import { flipDomainAndRangeBasedOnOrientation, generateSVGPathString, getProperty } from '@/tools'
import { boxplot as boxplotConfigs } from '@/configuration'
import { BoxplotChartModel } from '@/model/boxplot'
import { Component } from '@/components/component'
@@ -334,7 +334,10 @@ export class Boxplot extends Component {
hoveredElement,
items: [
{
- label: options.tooltip.groupLabel,
+ label:
+ getProperty(options, 'locale', 'translations', 'group') ||
+ getProperty(options, 'tooltip', 'groupLabel') ||
+ 'Group',
value: datum[groupMapsTo],
class: self.model.getColorClassName({
classNameTypes: [ColorClassNameTypes.TOOLTIP]
@@ -442,7 +445,10 @@ export class Boxplot extends Component {
hoveredElement,
items: [
{
- label: options.tooltip.groupLabel,
+ label:
+ getProperty(options, 'locale', 'translations', 'group') ||
+ getProperty(options, 'tooltip', 'groupLabel') ||
+ 'Group',
value: datum[groupMapsTo],
class: self.model.getColorClassName({
classNameTypes: [ColorClassNameTypes.TOOLTIP]
diff --git a/packages/core/src/components/graphs/bullet.ts b/packages/core/src/components/graphs/bullet.ts
index 0515c5539b..13f1018ee7 100644
--- a/packages/core/src/components/graphs/bullet.ts
+++ b/packages/core/src/components/graphs/bullet.ts
@@ -322,6 +322,8 @@ export class Bullet extends Component {
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier()
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
+
this.parent
.selectAll('path.bar')
.on('mouseover', function (event: MouseEvent, datum: any) {
@@ -345,7 +347,10 @@ export class Bullet extends Component {
hoveredElement,
items: [
{
- label: options.tooltip.groupLabel || 'Group',
+ label:
+ getProperty(options, 'locale', 'translations', 'group') ||
+ getProperty(options, 'tooltip', 'groupLabel') ||
+ 'Group',
value: datum[groupMapsTo],
class: self.model.getColorClassName({
classNameTypes: [ColorClassNameTypes.TOOLTIP],
@@ -362,7 +367,7 @@ export class Bullet extends Component {
},
{
label: 'Percentage',
- value: `${Math.floor((datum[rangeIdentifier] / datum.marker) * 100)}%`
+ value: `${numberFormatter(Math.floor((datum[rangeIdentifier] / datum.marker) * 100), localeCode)}%`
},
{
label: 'Performance',
diff --git a/packages/core/src/components/graphs/circle-pack.ts b/packages/core/src/components/graphs/circle-pack.ts
index 9b48d1725f..58f4f512c8 100644
--- a/packages/core/src/components/graphs/circle-pack.ts
+++ b/packages/core/src/components/graphs/circle-pack.ts
@@ -260,7 +260,10 @@ export class CirclePack extends Component {
const options = self.model.getOptions()
totalValue = [
{
- label: get(options, 'tooltip.totalLabel') || 'Total',
+ label:
+ get(options, 'locale.translations.total') ||
+ get(options, 'tooltip.totalLabel') ||
+ 'Total',
value: datum.value,
bold: true
}
diff --git a/packages/core/src/components/graphs/donut.ts b/packages/core/src/components/graphs/donut.ts
index 2912b1bafb..45d38e9717 100644
--- a/packages/core/src/components/graphs/donut.ts
+++ b/packages/core/src/components/graphs/donut.ts
@@ -93,8 +93,8 @@ export class Donut extends Pie {
const i = interpolateFunction(currentValue, donutCenterFigure)
return (t: any) => {
- const { numberFormatter } = options.donut.center
- d3Ref.text(numberFormatter(i(t)))
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
+ d3Ref.text(numberFormatter(i(t), localeCode))
}
}
}
diff --git a/packages/core/src/components/graphs/gauge.ts b/packages/core/src/components/graphs/gauge.ts
index 50165171ad..2a7382226e 100644
--- a/packages/core/src/components/graphs/gauge.ts
+++ b/packages/core/src/components/graphs/gauge.ts
@@ -199,7 +199,7 @@ export class Gauge extends Component {
const fontSize = valueFontSize(radius)
// Add the big number
const valueNumberGroup = DOMUtils.appendOrSelect(numbersGroup, 'g.gauge-value-number')
-
+ const { code: localeCode, number: localeNumberFormatter } = getProperty(options, 'locale')
const numberFormatter = getProperty(options, 'gauge', 'numberFormatter')
const valueNumber = valueNumberGroup.selectAll('text.gauge-value-number').data([value])
@@ -210,7 +210,7 @@ export class Gauge extends Component {
.merge(valueNumber as any)
.style('font-size', `${fontSize}px`)
.attr('text-anchor', 'middle')
- .text((d: any) => numberFormatter(d))
+ .text((d: any) => localeNumberFormatter(Number(numberFormatter(d)), localeCode))
// add the percentage symbol beside the valueNumber
const { width: valueNumberWidth } = DOMUtils.getSVGElementSize(
@@ -245,7 +245,7 @@ export class Gauge extends Component {
const svg = this.getComponentContainer()
const options = this.getOptions()
const delta = this.getDelta()
-
+ const { code: localeCode, number: localeNumberFormatter } = getProperty(options, 'locale')
if (!delta) {
const deltaGroup = svg.select('g.gauge-delta')
@@ -287,7 +287,10 @@ export class Gauge extends Component {
.merge(deltaNumber)
.attr('text-anchor', 'middle')
.style('font-size', `${deltaFontSize(radius)}px`)
- .text((d: any) => `${numberFormatter(d)}${gaugeSymbol}`)
+ .text(
+ (d: any) =>
+ `${localeNumberFormatter(Number(numberFormatter(d)), localeCode)}${gaugeSymbol}`
+ )
// Add the caret for the delta number
const { width: deltaNumberWidth } = DOMUtils.getSVGElementSize(
diff --git a/packages/core/src/components/graphs/heatmap.ts b/packages/core/src/components/graphs/heatmap.ts
index 40c0d13d3d..30427b2282 100644
--- a/packages/core/src/components/graphs/heatmap.ts
+++ b/packages/core/src/components/graphs/heatmap.ts
@@ -203,7 +203,8 @@ export class Heatmap extends Component {
const self = this
const { cartesianScales } = this.services
const options = this.getOptions()
- const totalLabel = get(options, 'tooltip.totalLabel')
+ const totalLabel =
+ get(options, 'locale.translations.total') || get(options, 'tooltip.totalLabel') || 'Total'
const domainIdentifier = cartesianScales.getDomainIdentifier()
const rangeIdentifier = cartesianScales.getRangeIdentifier()
@@ -253,7 +254,7 @@ export class Heatmap extends Component {
value: datum[rangeIdentifier]
},
{
- label: totalLabel || 'Total',
+ label: totalLabel,
value: datum['value'],
color: hoveredElement.style('fill')
}
diff --git a/packages/core/src/components/graphs/histogram.ts b/packages/core/src/components/graphs/histogram.ts
index 30b625d1ea..b710f1b182 100644
--- a/packages/core/src/components/graphs/histogram.ts
+++ b/packages/core/src/components/graphs/histogram.ts
@@ -153,7 +153,7 @@ export class Histogram extends Component {
addEventListeners() {
const options = this.model.getOptions()
const { groupMapsTo } = options.data
-
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
const self = this
this.parent
.selectAll('path.bar')
@@ -162,8 +162,8 @@ export class Histogram extends Component {
hoveredElement.classed('hovered', true)
- const x0 = parseFloat(get(datum, 'data.x0'))
- const x1 = parseFloat(get(datum, 'data.x1'))
+ const x0 = numberFormatter(parseFloat(get(datum, 'data.x0')), localeCode)
+ const x1 = numberFormatter(parseFloat(get(datum, 'data.x1')), localeCode)
const rangeAxisPosition = self.services.cartesianScales.getRangeAxisPosition()
const rangeScaleLabel = self.services.cartesianScales.getScaleLabel(rangeAxisPosition)
diff --git a/packages/core/src/components/graphs/pie.ts b/packages/core/src/components/graphs/pie.ts
index 2f2cf3b450..e32d5aeae1 100644
--- a/packages/core/src/components/graphs/pie.ts
+++ b/packages/core/src/components/graphs/pie.ts
@@ -130,6 +130,7 @@ export class Pie extends Component {
})
// Draw the slice labels
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
const renderLabels = options.pie.labels.enabled
const labelData = renderLabels
? pieLayoutData.filter(x => (x.data as any)[valueMapsTo] > 0)
@@ -165,7 +166,12 @@ export class Pie extends Component {
)
})
}
- return convertValueToPercentage(d.data[valueMapsTo], displayData, valueMapsTo) + '%'
+ return (
+ numberFormatter(
+ convertValueToPercentage(d.data[valueMapsTo], displayData, valueMapsTo),
+ localeCode
+ ) + '%'
+ )
})
// Calculate dimensions in order to transform
.datum(function (d: any) {
diff --git a/packages/core/src/components/graphs/radar.ts b/packages/core/src/components/graphs/radar.ts
index cc5d4fe7ca..531cff1676 100644
--- a/packages/core/src/components/graphs/radar.ts
+++ b/packages/core/src/components/graphs/radar.ts
@@ -487,6 +487,7 @@ export class Radar extends Component {
.attr('transform', (key: any) => `rotate(${radToDeg(xScale(key))}, ${c.x}, ${c.y})`)
// y labels (show only the min and the max labels)
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
const yLabels = DOMUtils.appendOrSelect(svg, 'g.y-labels').attr('role', Roles.GROUP)
const yLabelUpdate = yLabels.selectAll('text').data(extent(yTicks))
yLabelUpdate.join(
@@ -494,7 +495,7 @@ export class Radar extends Component {
enter
.append('text')
.attr('opacity', 0)
- .text((tick: any) => tick)
+ .text((tick: any) => numberFormatter(tick, localeCode))
.attr(
'x',
(tick: any) => polarToCartesianCoords(-Math.PI / 2, yScale(tick), c).x + yLabelPadding
diff --git a/packages/core/src/components/graphs/wordcloud.ts b/packages/core/src/components/graphs/wordcloud.ts
index 77428dab39..a2e167f7f4 100644
--- a/packages/core/src/components/graphs/wordcloud.ts
+++ b/packages/core/src/components/graphs/wordcloud.ts
@@ -1,6 +1,6 @@
import { extent, scaleLinear, select } from 'd3'
import cloud from 'd3-cloud'
-import { debounce } from 'lodash-es'
+import { debounce, get } from 'lodash-es'
import { Component } from '@/components/component'
import { DOMUtils } from '@/services/essentials/dom-utils'
import { Events, ColorClassNameTypes, RenderTypes } from '@/interfaces/enums'
@@ -217,7 +217,10 @@ export class WordCloud extends Component {
value: datum.value
},
{
- label: options.tooltip.groupLabel,
+ label:
+ get(options, 'locale.translations.group') ||
+ get(options, 'tooltip.groupLabel') ||
+ 'Group',
value: datum[groupMapsTo],
class: self.model.getColorClassName({
classNameTypes: [ColorClassNameTypes.TOOLTIP],
diff --git a/packages/core/src/configuration.ts b/packages/core/src/configuration.ts
index 40ba2444ec..3c65904bd0 100644
--- a/packages/core/src/configuration.ts
+++ b/packages/core/src/configuration.ts
@@ -1,6 +1,7 @@
import { enUS as localeObject } from 'date-fns/locale'
import { merge } from 'lodash-es'
import { circlePack } from './configuration-non-customizable'
+
import type {
AlluvialChartOptions,
AreaChartOptions,
@@ -52,7 +53,8 @@ import type {
LegendOptions,
StackedBarOptions,
ToolbarOptions,
- ZoomBarsOptions
+ ZoomBarsOptions,
+ Locale
} from '@/interfaces/components'
/*
@@ -70,6 +72,187 @@ const standardTruncationOptions = {
numCharacter: 14
}
+/**
+ * Locale options
+ */
+const locale: Locale = {
+ code: navigator.language, // read from browser's navigator.language
+ number: (value, language = navigator.language) => value.toLocaleString(language), // based on code property if specified
+ date: (value, language = navigator.language, options = {}, preformattedLocaleValue = null) =>
+ preformattedLocaleValue ? preformattedLocaleValue : value.toLocaleDateString(language, options), // based on code property if specified
+ time: (value, language = navigator.language, options = {}, preformattedLocaleValue = null) =>
+ preformattedLocaleValue ? preformattedLocaleValue : value.toLocaleTimeString(language, options), // based on code property if specified
+ optionsObject: {
+ '15seconds': {
+ primary: {
+ 'MMM d, pp': {
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ second: '2-digit',
+ hourCycle: 'h12'
+ },
+ 'MMM d, h:mm:ss.SSS a': {
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ fractionalSecondDigits: 3,
+ hourCycle: 'h12'
+ }
+ },
+ secondary: {
+ pp: {
+ hour: 'numeric',
+ minute: '2-digit',
+ second: '2-digit',
+ hourCycle: 'h12'
+ },
+ 'h:mm:ss.SSS a': {
+ hour: 'numeric',
+ minute: '2-digit',
+ fractionalSecondDigits: 3,
+ hourCycle: 'h12'
+ }
+ },
+ type: 'time'
+ },
+ minute: {
+ primary: {
+ 'MMM d, p': {
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ secondary: {
+ p: {
+ hour: 'numeric',
+ minute: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ type: 'time'
+ },
+ '30minutes': {
+ primary: {
+ 'MMM d, p': {
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ secondary: {
+ p: {
+ hour: 'numeric',
+ minute: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ type: 'time'
+ },
+ hourly: {
+ primary: {
+ 'MMM d, hh a': {
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ secondary: {
+ 'hh a': {
+ hour: '2-digit',
+ hourCycle: 'h12'
+ }
+ },
+ type: 'time'
+ },
+ daily: {
+ primary: {
+ 'MMM d': {
+ month: 'short',
+ day: 'numeric'
+ }
+ },
+ secondary: {
+ d: {
+ day: 'numeric'
+ }
+ },
+ type: 'date'
+ },
+ weekly: {
+ primary: {
+ 'eee, MMM d': {
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric'
+ }
+ },
+ secondary: {
+ eee: {
+ weekday: 'short'
+ }
+ },
+ type: 'date'
+ },
+ monthly: {
+ primary: {
+ 'MMM yyyy': {
+ month: 'short',
+ year: 'numeric'
+ }
+ },
+ secondary: {
+ MMM: {
+ month: 'short'
+ }
+ },
+ type: 'date'
+ },
+ quarterly: {
+ primary: {},
+ secondary: {},
+ type: 'date'
+ },
+ yearly: {
+ primary: {
+ yyyy: {
+ year: 'numeric'
+ }
+ },
+ secondary: {
+ yyyy: {
+ year: 'numeric'
+ }
+ },
+ type: 'date'
+ }
+ },
+ translations: {
+ group: 'Group',
+ total: 'Total',
+ meter: {
+ title: '' //default is emply string as meter title is dataset label
+ },
+ tabularRep: {
+ title: 'Tabular representation',
+ downloadAsCSV: 'Download as CSV'
+ },
+ toolbar: {
+ exportAsCSV: 'Export to CSV',
+ exportAsJPG: 'Export to JPG',
+ exportAsPNG: 'Export to PNG'
+ }
+ }
+}
+
/**
* Legend options
*/
@@ -179,6 +362,7 @@ const chart: BaseChartOptions = {
theme: ChartTheme.WHITE,
tooltip: baseTooltip,
legend,
+ locale,
style: {
prefix: 'cc'
},
@@ -519,8 +703,7 @@ const radarChart: RadarChartOptions = merge({}, chart, {
tooltip: {
gridline: {
enabled: true
- },
- valueFormatter: value => (value !== null && value !== undefined ? value : 'N/A')
+ }
}
} as RadarChartOptions)
diff --git a/packages/core/src/demo/charts/bar.ts b/packages/core/src/demo/charts/bar.ts
index fc95b89023..0a0a8aa90a 100644
--- a/packages/core/src/demo/charts/bar.ts
+++ b/packages/core/src/demo/charts/bar.ts
@@ -495,6 +495,91 @@ export const simpleBarTurkishLocaleOptions = {
}
}
+//using locale interface to reformat everything to Arabic
+export const simpleBarArabicLocaleOptions = {
+ title: 'Arabic locale using Locale Interface',
+ axes: {
+ left: {
+ mapsTo: 'value'
+ },
+ bottom: {
+ mapsTo: 'date',
+ scaleType: ScaleTypes.TIME
+ }
+ },
+ locale: {
+ code: 'ar-SA'
+ }
+}
+
+//using locale interface to reformat everything to Iranian
+export const simpleBarIranianLocaleOptions = {
+ title: 'Iranian locale using Locale Interface',
+ axes: {
+ left: {
+ mapsTo: 'value'
+ },
+ bottom: {
+ mapsTo: 'date',
+ scaleType: ScaleTypes.TIME
+ }
+ },
+ locale: {
+ code: 'fa-IR'
+ }
+}
+
+//using locale interface to reformat everything to Japanese
+export const simpleBarJapaneseLocaleOptions = {
+ title: 'Japanese locale using Locale Interface',
+ axes: {
+ left: {
+ mapsTo: 'value'
+ },
+ bottom: {
+ mapsTo: 'date',
+ scaleType: ScaleTypes.TIME
+ }
+ },
+ locale: {
+ code: 'ja-JP'
+ }
+}
+
+//using locale interface to reformat everything to Hindi
+export const simpleBarHindiLocaleOptions = {
+ title: 'Hindi locale using Locale Interface',
+ axes: {
+ left: {
+ mapsTo: 'value'
+ },
+ bottom: {
+ mapsTo: 'date',
+ scaleType: ScaleTypes.TIME
+ }
+ },
+ locale: {
+ code: 'hi-IN'
+ }
+}
+
+//using locale interface to reformat everything to Bangla
+export const simpleBarBanglaLocaleOptions = {
+ title: 'Bangla locale using Locale Interface',
+ axes: {
+ left: {
+ mapsTo: 'value'
+ },
+ bottom: {
+ mapsTo: 'date',
+ scaleType: ScaleTypes.TIME
+ }
+ },
+ locale: {
+ code: 'bn-BD'
+ }
+}
+
// Horizontal simple time series
export const simpleHorizontalBarTimeSeriesOptions = {
title: 'Horizontal simple bar (time series)',
diff --git a/packages/core/src/demo/charts/index.ts b/packages/core/src/demo/charts/index.ts
index 3226be8d2e..e364948b50 100644
--- a/packages/core/src/demo/charts/index.ts
+++ b/packages/core/src/demo/charts/index.ts
@@ -287,6 +287,31 @@ const utilityDemoGroups: DemoGroup[] = [
options: barDemos.simpleBarTurkishLocaleOptions,
data: barDemos.simpleBarTurkishLocaleData,
chartType: chartTypes.SimpleBarChart
+ },
+ {
+ options: barDemos.simpleBarArabicLocaleOptions,
+ data: barDemos.simpleBarTurkishLocaleData,
+ chartType: chartTypes.SimpleBarChart
+ },
+ {
+ options: barDemos.simpleBarIranianLocaleOptions,
+ data: barDemos.simpleBarTurkishLocaleData,
+ chartType: chartTypes.SimpleBarChart
+ },
+ {
+ options: barDemos.simpleBarJapaneseLocaleOptions,
+ data: barDemos.simpleBarTurkishLocaleData,
+ chartType: chartTypes.SimpleBarChart
+ },
+ {
+ options: barDemos.simpleBarHindiLocaleOptions,
+ data: barDemos.simpleBarTurkishLocaleData,
+ chartType: chartTypes.SimpleBarChart
+ },
+ {
+ options: barDemos.simpleBarBanglaLocaleOptions,
+ data: barDemos.simpleBarTurkishLocaleData,
+ chartType: chartTypes.SimpleBarChart
}
]
},
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 67a28ac49d..273d6762e7 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -90,6 +90,8 @@ export type {
ComboChartAxisOptions,
GridOptions,
LegendOptions,
+ Locale,
+ LocaleTimeScaleOptions,
RulerOptions,
StackedBarOptions,
TimeScaleOptions,
diff --git a/packages/core/src/interfaces/charts.ts b/packages/core/src/interfaces/charts.ts
index 75461fedc4..591f220b25 100644
--- a/packages/core/src/interfaces/charts.ts
+++ b/packages/core/src/interfaces/charts.ts
@@ -18,6 +18,7 @@ import type {
ToolbarOptions,
TooltipOptions,
ZoomBarsOptions,
+ Locale,
TabularRepCustomizationOptions
} from './components'
import type {
@@ -36,6 +37,10 @@ export interface BaseChartOptions {
* Optionally specify a title for the chart
*/
title?: string
+ /**
+ * Locale configuration
+ */
+ locale?: Locale
/**
* boolean to disable animations (enabled by default)
*/
diff --git a/packages/core/src/interfaces/components.ts b/packages/core/src/interfaces/components.ts
index f9a3e70d78..2b1a62d7fe 100644
--- a/packages/core/src/interfaces/components.ts
+++ b/packages/core/src/interfaces/components.ts
@@ -8,6 +8,89 @@ import type {
import type { Component } from '../components'
import type { TruncationOptions } from './truncation'
+/**
+ *Locale Options Interface
+ */
+export interface Locale {
+ code?: string // BCP 47 language tag
+ number?: (value: number, language: string) => string
+ date?: (
+ value: Date,
+ language: string,
+ options: Intl.DateTimeFormatOptions,
+ preformattedLocaleValue?: string
+ ) => string
+ time?: (
+ value: Date,
+ language: string,
+ options: Intl.DateTimeFormatOptions,
+ preformattedLocaleValue?: string
+ ) => string
+ optionsObject?: {
+ '15seconds'?: LocaleTimeScaleOptions
+ minute?: LocaleTimeScaleOptions
+ '30minutes'?: LocaleTimeScaleOptions
+ hourly?: LocaleTimeScaleOptions
+ daily?: LocaleTimeScaleOptions
+ weekly?: LocaleTimeScaleOptions
+ monthly?: LocaleTimeScaleOptions
+ quarterly?: LocaleTimeScaleOptions
+ yearly?: LocaleTimeScaleOptions
+ }
+ translations?: {
+ group?: string // used by Tooltip and Toolbar / Tabular Representation
+ total?: string // ditto
+ meter?: {
+ title?: string
+ }
+ tabularRep: {
+ title?: string
+ downloadAsCSV?: string
+ }
+ toolbar: {
+ exportAsCSV?: string
+ exportAsJPG?: string
+ exportAsPNG?: string
+ }
+ }
+}
+
+export interface LocaleTimeScaleOptions {
+ primary?: Record<
+ string,
+ {
+ month?: string
+ day?: string
+ hour?: string
+ minute?: string
+ second?: string
+ fractionalSecondDigits?: number
+ weekday?: string
+ year?: string
+ hourCycle?: string
+ hour12?: boolean
+ dayPeriod?: string
+ }
+ >
+ secondary?: Record<
+ string,
+ {
+ month?: string
+ day?: string
+ hour?: string
+ minute?: string
+ second?: string
+ fractionalSecondDigits?: number
+ weekday?: string
+ year?: string
+ hourCycle?: string
+ hour12?: boolean
+ dayPeriod?: string
+ }
+ >
+ type?: string
+}
+
/**
* customize the overlay contents
*/
diff --git a/packages/core/src/interfaces/index.ts b/packages/core/src/interfaces/index.ts
index bac323f3a6..374da2aa43 100644
--- a/packages/core/src/interfaces/index.ts
+++ b/packages/core/src/interfaces/index.ts
@@ -52,6 +52,8 @@ export type {
LayoutComponentChild,
LegendItem,
LegendOptions,
+ Locale,
+ LocaleTimeScaleOptions,
RulerOptions,
StackedBarOptions,
ThresholdOptions,
diff --git a/packages/core/src/model/alluvial.ts b/packages/core/src/model/alluvial.ts
index 0393e5e0c7..c6f2551b85 100644
--- a/packages/core/src/model/alluvial.ts
+++ b/packages/core/src/model/alluvial.ts
@@ -1,6 +1,6 @@
// Internal Imports
import { ChartModelCartesian } from './cartesian-charts'
-
+import { getProperty } from '@/tools'
/**
* Alluvial chart model layer
*/
@@ -11,12 +11,17 @@ export class AlluvialChartModel extends ChartModelCartesian {
getTabularDataArray() {
const displayData = this.getDisplayData()
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
// Sort array by source to get a close depiction of the alluvial chart
displayData.sort((a: any, b: any) => a['source'].localeCompare(b['source']))
const headers = ['Source', 'Target', 'Value']
const cells = [
- ...displayData.map((datum: any) => [datum['source'], datum['target'], datum['value']])
+ ...displayData.map((datum: any) => [
+ datum['source'],
+ datum['target'],
+ numberFormatter(datum['value'], localeCode)
+ ])
]
return super.formatTable({ headers, cells })
diff --git a/packages/core/src/model/binned-charts.ts b/packages/core/src/model/binned-charts.ts
index 91728d40a9..a2434af13e 100644
--- a/packages/core/src/model/binned-charts.ts
+++ b/packages/core/src/model/binned-charts.ts
@@ -1,6 +1,6 @@
// Internal Imports
import { ChartModelCartesian } from './cartesian-charts'
-
+import { getProperty } from '@/tools'
import { get } from 'lodash-es'
/**
@@ -10,17 +10,22 @@ export class ChartModelBinned extends ChartModelCartesian {
getTabularDataArray() {
const options = this.getOptions()
const { groupMapsTo } = options.data
-
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
const binnedStackedData = this.getBinnedStackedData()
- const headers = [
+ const headers = [
get(options, 'bins.rangeLabel') || 'Range',
...binnedStackedData.map(datum => get(datum, `0.${groupMapsTo}`))
]
const cells = [
...get(binnedStackedData, 0).map((d, i) => [
- `${get(d, 'data.x0')} – ${get(d, 'data.x1')}`,
- ...binnedStackedData.map(datum => get(datum[i], `data.${get(datum[i], groupMapsTo)}`))
+ `${numberFormatter(Number(get(d, 'data.x0')), localeCode)} – ${numberFormatter(
+ Number(get(d, 'data.x1')),
+ localeCode
+ )}`,
+ ...binnedStackedData.map(datum =>
+ numberFormatter(get(datum[i], `data.${get(datum[i], groupMapsTo)}`), localeCode)
+ )
])
]
diff --git a/packages/core/src/model/boxplot.ts b/packages/core/src/model/boxplot.ts
index a402d0951b..91d1359b84 100644
--- a/packages/core/src/model/boxplot.ts
+++ b/packages/core/src/model/boxplot.ts
@@ -80,8 +80,8 @@ export class BoxplotChartModel extends ChartModelCartesian {
getTabularDataArray() {
const options = this.getOptions()
const { groupMapsTo } = options.data
-
const boxplotData = this.getBoxplotData()
+ const { number: numberFormatter, code: localeCode } = getProperty(options, 'locale')
const headers = ['Group', 'Minimum', 'Q1', 'Median', 'Q3', 'Maximum', 'IQR', 'Outlier(s)']
const cells = [
@@ -93,27 +93,28 @@ export class BoxplotChartModel extends ChartModelCartesian {
return [
datum[groupMapsTo],
getProperty(datum, 'whiskers', 'min') !== null
- ? getProperty(datum, 'whiskers', 'min').toLocaleString()
+ ? numberFormatter(getProperty(datum, 'whiskers', 'min'), localeCode)
: '–',
getProperty(datum, 'quartiles', 'q_25') !== null
- ? getProperty(datum, 'quartiles', 'q_25').toLocaleString()
+ ? numberFormatter(getProperty(datum, 'quartiles', 'q_25'), localeCode)
: '–',
getProperty(datum, 'quartiles', 'q_50') !== null
- ? getProperty(datum, 'quartiles', 'q_50').toLocaleString()
+ ? numberFormatter(getProperty(datum, 'quartiles', 'q_50'), localeCode)
: '–',
getProperty(datum, 'quartiles', 'q_75') !== null
- ? getProperty(datum, 'quartiles', 'q_75').toLocaleString()
+ ? numberFormatter(getProperty(datum, 'quartiles', 'q_75'), localeCode)
: '–',
getProperty(datum, 'whiskers', 'max') !== null
- ? getProperty(datum, 'whiskers', 'max').toLocaleString()
+ ? numberFormatter(getProperty(datum, 'whiskers', 'max'), localeCode)
: '–',
getProperty(datum, 'quartiles', 'q_75') !== null &&
getProperty(datum, 'quartiles', 'q_25') !== null
- ? (
+ ? (numberFormatter(
getProperty(datum, 'quartiles', 'q_75') - getProperty(datum, 'quartiles', 'q_25')
- ).toLocaleString()
+ ),
+ localeCode)
: '–',
- outliers.map((d: any) => d.toLocaleString()).join(',')
+ outliers.map((d: any) => numberFormatter(d, localeCode)).join(',')
]
})
]
diff --git a/packages/core/src/model/bullet.ts b/packages/core/src/model/bullet.ts
index 7557bc9050..61e70d039a 100644
--- a/packages/core/src/model/bullet.ts
+++ b/packages/core/src/model/bullet.ts
@@ -33,6 +33,7 @@ export class BulletChartModel extends ChartModelCartesian {
const options = this.getOptions()
const { groupMapsTo } = options.data
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier()
+ const { number: numberFormatter, code: localeCode } = getProperty(options, 'locale')
const performanceAreaTitles = getProperty(options, 'bullet', 'performanceAreaTitles')
const headers = ['Title', 'Group', 'Value', 'Target', 'Percentage', 'Performance']
@@ -40,11 +41,13 @@ export class BulletChartModel extends ChartModelCartesian {
...displayData.map((datum: any) => [
datum['title'],
datum[groupMapsTo],
- datum['value'] === null ? '–' : datum['value'],
- getProperty(datum, 'marker') === null ? '–' : datum['marker'],
+ datum['value'] === null ? '–' : numberFormatter(datum['value'], localeCode),
getProperty(datum, 'marker') === null
? '–'
- : `${Math.floor((datum[rangeIdentifier] / datum.marker) * 100)}%`,
+ : numberFormatter(datum['marker'], localeCode),
+ getProperty(datum, 'marker') === null
+ ? '–'
+ : `${numberFormatter(Math.floor((datum[rangeIdentifier] / datum.marker) * 100), localeCode)}%`,
performanceAreaTitles[this.getMatchingRangeIndexForDatapoint(datum)]
])
]
diff --git a/packages/core/src/model/cartesian-charts.ts b/packages/core/src/model/cartesian-charts.ts
index d3f4af328b..5f670e132f 100644
--- a/packages/core/src/model/cartesian-charts.ts
+++ b/packages/core/src/model/cartesian-charts.ts
@@ -55,8 +55,9 @@ export class ChartModelCartesian extends ChartModel {
const { groupMapsTo } = options.data
const { primaryDomain, primaryRange, secondaryDomain, secondaryRange } =
this.assignRangeAndDomains()
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
- const headers = [
+ const headers = [
'Group',
primaryDomain.label,
primaryRange.label,
@@ -68,20 +69,20 @@ export class ChartModelCartesian extends ChartModel {
datum[primaryDomain.identifier] === null ? '–' : datum[primaryDomain.identifier],
datum[primaryRange.identifier] === null || isNaN(datum[primaryRange.identifier])
? '–'
- : datum[primaryRange.identifier].toLocaleString(),
+ : numberFormatter(datum[primaryRange.identifier], localeCode),
...(secondaryDomain
? [
datum[secondaryDomain.identifier] === null
? '–'
: datum[secondaryDomain.identifier]
- ]
+ ]
: []),
...(secondaryRange
? [
datum[secondaryRange.identifier] === null || isNaN(datum[secondaryRange.identifier])
? '–'
: datum[secondaryRange.identifier]
- ]
+ ]
: [])
])
diff --git a/packages/core/src/model/choropleth.ts b/packages/core/src/model/choropleth.ts
index 4485d5a226..7b710499b9 100644
--- a/packages/core/src/model/choropleth.ts
+++ b/packages/core/src/model/choropleth.ts
@@ -68,12 +68,14 @@ export class ChoroplethModel extends ChartModel {
*/
getTabularDataArray() {
const displayData = this.getDisplayData()
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
+
const headers = ['Country ID', 'Country Name', 'Value']
const cells = [
...displayData.map(datum => [
datum['id'] === null ? '–' : datum['id'],
datum['name'],
- datum['value']
+ numberFormatter(datum['value'], localeCode)
])
]
diff --git a/packages/core/src/model/circle-pack.ts b/packages/core/src/model/circle-pack.ts
index bbc6267781..5ba55f1d03 100644
--- a/packages/core/src/model/circle-pack.ts
+++ b/packages/core/src/model/circle-pack.ts
@@ -127,6 +127,7 @@ export class CirclePackChartModel extends ChartModel {
getTabularDataArray() {
const displayData = this.getDisplayData()
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
const headers = ['Child', 'Parent', 'Value']
const cells = []
@@ -137,7 +138,7 @@ export class CirclePackChartModel extends ChartModel {
// Call recursive function
value += this.getChildrenDatums(datum.children, datum.name, cells, 0)
}
- cells.push(['–', datum.name, value])
+ cells.push(['–', datum.name, numberFormatter(value, localeCode)])
})
return super.formatTable({ headers, cells })
@@ -153,6 +154,7 @@ export class CirclePackChartModel extends ChartModel {
*/
private getChildrenDatums(children: any, parent: any, result: string[][] = [], totalSum = 0) {
const grandParent = parent
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
children.forEach((child: any) => {
const parentWithinIteration = child.name
@@ -165,7 +167,7 @@ export class CirclePackChartModel extends ChartModel {
}
sum += this.getChildrenDatums(child.children, parentWithinIteration, result, sum)
- result.push([parentWithinIteration, grandParent, sum])
+ result.push([parentWithinIteration, grandParent, numberFormatter(sum, localeCode)])
totalSum += sum
}
} else {
@@ -174,7 +176,7 @@ export class CirclePackChartModel extends ChartModel {
value = child.value
totalSum += child.value
}
- result.push([child.name, grandParent, value])
+ result.push([child.name, grandParent, numberFormatter(value, localeCode)])
}
})
diff --git a/packages/core/src/model/gauge.ts b/packages/core/src/model/gauge.ts
index b82e5d8f44..f820ccde19 100644
--- a/packages/core/src/model/gauge.ts
+++ b/packages/core/src/model/gauge.ts
@@ -1,4 +1,5 @@
import { ChartModel } from './model'
+import { getProperty } from '@/tools'
/**
* The gauge chart model layer
@@ -16,11 +17,13 @@ export class GaugeChartModel extends ChartModel {
const displayData = this.getDisplayData()
const options = this.getOptions()
const { groupMapsTo } = options.data
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
+
const headers = ['Group', 'Value']
const cells = [
...displayData.map((datum: any) => [
datum[groupMapsTo],
- datum['value'] === null ? '–' : datum['value'].toLocaleString()
+ datum['value'] === null ? '–' : numberFormatter(datum['value'], localeCode)
])
]
diff --git a/packages/core/src/model/heatmap.ts b/packages/core/src/model/heatmap.ts
index 1635c55609..b476a47fd3 100644
--- a/packages/core/src/model/heatmap.ts
+++ b/packages/core/src/model/heatmap.ts
@@ -236,10 +236,10 @@ export class HeatmapModel extends ChartModelCartesian {
*/
getTabularDataArray() {
const displayData = this.getDisplayData()
-
const { primaryDomain, primaryRange } = this.assignRangeAndDomains()
-
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
let domainValueFormatter: any
+
const headers = [primaryDomain.label, primaryRange.label, 'Value']
const cells = [
...displayData.map((datum: any) => [
@@ -249,10 +249,8 @@ export class HeatmapModel extends ChartModelCartesian {
? domainValueFormatter(datum[primaryDomain.identifier])
: datum[primaryDomain.identifier],
- datum[primaryRange.identifier] === null
- ? '–'
- : datum[primaryRange.identifier].toLocaleString(),
- datum['value']
+ datum[primaryRange.identifier] === null ? '–' : datum[primaryRange.identifier],
+ numberFormatter(datum['value'], localeCode)
])
]
diff --git a/packages/core/src/model/meter.ts b/packages/core/src/model/meter.ts
index c22124cb6e..d234935a8a 100644
--- a/packages/core/src/model/meter.ts
+++ b/packages/core/src/model/meter.ts
@@ -73,6 +73,8 @@ export class MeterChartModel extends ChartModel {
const { groupMapsTo } = options.data
const status = this.getStatus()
const proportional = getProperty(options, 'meter', 'proportional')
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
+
let headers = []
let cells: ChartTabularData = []
let domainMax: number
@@ -81,17 +83,27 @@ export class MeterChartModel extends ChartModel {
domainMax = 100
const datum = displayData[0]
headers = ['Group', 'Value', ...(status ? ['Status'] : [])]
- cells = [[datum[groupMapsTo], datum['value'], ...(status ? [status] : [])]]
+ cells = [
+ [
+ datum[groupMapsTo],
+ numberFormatter(datum['value'], localeCode),
+ ...(status ? [status] : [])
+ ]
+ ]
} else {
const total = getProperty(proportional, 'total')
domainMax = total ? total : this.getMaximumDomain(displayData)
headers = ['Group', 'Value', 'Percentage of total']
cells = [
- ...displayData.map((datum: any) => [
- datum[groupMapsTo],
- datum['value'],
- ((datum['value'] / domainMax) * 100).toFixed(2) + ' %'
- ])
+ ...displayData.map((datum: any) => {
+ const value = datum['value']
+ const percentValue = Number(((datum['value'] / domainMax) * 100).toFixed(2))
+ return [
+ datum[groupMapsTo],
+ numberFormatter(value, localeCode),
+ numberFormatter(percentValue, localeCode) + ' %'
+ ]
+ })
]
}
diff --git a/packages/core/src/model/model.ts b/packages/core/src/model/model.ts
index 50e2855a08..089c2b6323 100644
--- a/packages/core/src/model/model.ts
+++ b/packages/core/src/model/model.ts
@@ -1,4 +1,3 @@
-import { format } from 'date-fns'
import { bin as d3Bin, scaleOrdinal, stack, stackOffsetDiverging } from 'd3'
import { cloneDeep, fromPairs, groupBy, merge, uniq } from 'lodash-es'
import { getProperty, updateLegendAdditionalItems } from '@/tools'
@@ -57,6 +56,11 @@ export class ChartModel {
formatTable({ headers, cells }) {
const options = this.getOptions()
+ const {
+ code: localeCode,
+ date: dateFormatter,
+ number: numberFormatter
+ } = getProperty(options, 'locale')
const tableHeadingFormatter = getProperty(options, 'tabularRepModal', 'tableHeadingFormatter')
const tableCellFormatter = getProperty(options, 'tabularRepModal', 'tableCellFormatter')
const { cartesianScales } = this.services
@@ -64,8 +68,10 @@ export class ChartModel {
let domainValueFormatter: any
if (domainScaleType === ScaleTypes.TIME) {
- domainValueFormatter = (d: any) => format(d, 'MMM d, yyyy')
+ domainValueFormatter = (d: any) =>
+ dateFormatter(d, localeCode, { month: 'short', day: 'numeric', year: 'numeric' })
}
+
const result = [
typeof tableHeadingFormatter === 'function' ? tableHeadingFormatter(headers) : headers,
...(typeof tableCellFormatter === 'function'
@@ -74,6 +80,12 @@ export class ChartModel {
if (domainValueFormatter) {
data[1] = domainValueFormatter(data[1]) as string
}
+ for (let i in data) {
+ let val = data[i]
+ if (typeof val === 'number') {
+ data[i] = numberFormatter(val, localeCode)
+ }
+ }
return data
}))
]
diff --git a/packages/core/src/model/pie.ts b/packages/core/src/model/pie.ts
index 425a62249b..3bc1064cf6 100644
--- a/packages/core/src/model/pie.ts
+++ b/packages/core/src/model/pie.ts
@@ -1,4 +1,5 @@
import { ChartModel } from './model'
+import { getProperty } from '@/tools'
/** The charting model layer which includes mainly the chart data and options,
* as well as some misc. information to be shared among components */
@@ -29,12 +30,13 @@ export class PieChartModel extends ChartModel {
const options = this.getOptions()
const { groupMapsTo } = options.data
const { valueMapsTo } = options.pie
+ const { number: numberFormatter, code: localeCode } = getProperty(options, 'locale')
const headers = ['Group', 'Value']
const cells = [
...displayData.map((datum: any) => [
datum[groupMapsTo],
- datum[valueMapsTo] === null ? '–' : datum[valueMapsTo].toLocaleString()
+ datum[valueMapsTo] === null ? '–' : numberFormatter(datum[valueMapsTo], localeCode)
])
]
diff --git a/packages/core/src/model/radar.ts b/packages/core/src/model/radar.ts
index 2ae2e69687..3ebdb83c3b 100644
--- a/packages/core/src/model/radar.ts
+++ b/packages/core/src/model/radar.ts
@@ -10,10 +10,9 @@ export class RadarChartModel extends ChartModelCartesian {
getTabularDataArray() {
const options = this.getOptions()
-
const groupedData = this.getGroupedData()
-
const { angle, value } = getProperty(options, 'radar', 'axes')
+ const { number: numberFormatter, code: localeCode } = getProperty(options, 'locale')
const additionalHeaders = getProperty(groupedData, '0', 'data').map((d: any) => d[angle])
const headers = ['Group', ...additionalHeaders]
@@ -23,7 +22,7 @@ export class RadarChartModel extends ChartModelCartesian {
datum['name'],
...additionalHeaders.map((_: any, i: number) =>
getProperty(datum, 'data', i, value) !== null
- ? getProperty(datum, 'data', i, value).toLocaleString()
+ ? numberFormatter(getProperty(datum, 'data', i, value), localeCode)
: '–'
)
]
diff --git a/packages/core/src/model/treemap.ts b/packages/core/src/model/treemap.ts
index b99f678519..5a408f7844 100644
--- a/packages/core/src/model/treemap.ts
+++ b/packages/core/src/model/treemap.ts
@@ -11,16 +11,17 @@ export class TreemapChartModel extends ChartModel {
getTabularDataArray() {
const displayData = this.getDisplayData()
+ const { number: numberFormatter, code: localeCode } = getProperty(this.getOptions(), 'locale')
const headers = ['Child', 'Group', 'Value']
const cells = []
displayData.forEach((datum: any) => {
if (Array.isArray(datum.children)) {
datum.children.forEach((child: any) => {
- cells.push([child.name, datum.name, child.value])
+ cells.push([child.name, datum.name, numberFormatter(child.value, localeCode)])
})
} else if (getProperty(datum.name) !== null && getProperty(datum.value)) {
- cells.push(['–', datum.name, datum.value])
+ cells.push(['–', datum.name, numberFormatter(datum.value, localeCode)])
}
})
diff --git a/packages/core/src/model/wordcloud.ts b/packages/core/src/model/wordcloud.ts
index e3f984b435..b1da778a63 100644
--- a/packages/core/src/model/wordcloud.ts
+++ b/packages/core/src/model/wordcloud.ts
@@ -1,3 +1,4 @@
+import { getProperty } from '@/tools'
import { ChartModel } from './model'
/** The charting model layer which includes mainly the chart data and options,
@@ -9,16 +10,17 @@ export class WordCloudModel extends ChartModel {
getTabularDataArray() {
const displayData = this.getDisplayData()
-
const options = this.getOptions()
const { fontSizeMapsTo, wordMapsTo } = options.wordCloud
const { groupMapsTo } = options.data
+ const { code: localeCode, number: numberFormatter } = getProperty(options, 'locale')
+
const headers = [options.tooltip.wordLabel, 'Group', options.tooltip.valueLabel]
const cells = [
...displayData.map((datum: any) => [
datum[wordMapsTo],
datum[groupMapsTo],
- datum[fontSizeMapsTo]
+ numberFormatter(datum[fontSizeMapsTo], localeCode)
])
]
diff --git a/packages/core/src/services/time-series.ts b/packages/core/src/services/time-series.ts
index df6f0679fa..f2ab9545a0 100644
--- a/packages/core/src/services/time-series.ts
+++ b/packages/core/src/services/time-series.ts
@@ -1,6 +1,6 @@
import { min } from 'd3'
import { format } from 'date-fns/format'
-
+import { Locale } from '../interfaces/components'
import { getProperty } from '@/tools'
import { TimeIntervalFormats, TimeIntervalNames, TimeScaleOptions } from '@/interfaces/axis-scales'
@@ -84,7 +84,8 @@ export function formatTick(
i: number,
allTicks: Array,
interval: string,
- timeScaleOptions: TimeScaleOptions
+ timeScaleOptions: TimeScaleOptions,
+ localeOptions?: Locale
): string {
const showDayName = timeScaleOptions.showDayName
const intervalConsideringAlsoShowDayNameOption =
@@ -95,7 +96,8 @@ export function formatTick(
]
const primary = getProperty(formats, 'primary')
const secondary = getProperty(formats, 'secondary')
- let formatString = isTickPrimary(tick, i, allTicks, interval, showDayName) ? primary : secondary
+ const primaryTickFlag = isTickPrimary(tick, i, allTicks, interval, showDayName)
+ let formatString = primaryTickFlag ? primary : secondary
// if the interval, and the timestamp includes milliseconds value
if (interval === '15seconds' && date.getMilliseconds() !== 0) {
@@ -104,8 +106,25 @@ export function formatTick(
}
const locale = timeScaleOptions.localeObject
-
- return format(date, formatString, { locale })
+ const { code: localeCode, optionsObject } = localeOptions
+ const formatterType = optionsObject[interval]['type']
+ const formatterOptions =
+ optionsObject[interval][primaryTickFlag ? 'primary' : 'secondary'][formatString]
+
+ if (interval === 'quarterly' || !formatterOptions) {
+ const formattedDate = format(date, formatString, { locale })
+ const formatArr = formattedDate.split('').map(val => {
+ let num = Number(val)
+ if (val !== ' ' && !Number.isNaN(num)) {
+ return num.toLocaleString(localeCode)
+ } else {
+ return val
+ }
+ })
+ return localeOptions[formatterType](date, localeCode, {}, formatArr.join(''))
+ } else {
+ return localeOptions[formatterType](date, localeCode, formatterOptions)
+ }
}
// Given a timestamp, return an object of useful time formats