diff --git a/packages/core/src/components/axes/axis.ts b/packages/core/src/components/axes/axis.ts index 8d7f09ce2a..e4dde5de8c 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, 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) } 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 ) 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, code) } } else { formatter = userProvidedFormatter diff --git a/packages/core/src/components/essentials/threshold.ts b/packages/core/src/components/essentials/threshold.ts index 63c666b104..e4271cc03e 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, 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) } - return value.toLocaleString('en') + return numberFormatter(value, code) } appendThresholdLabel() { diff --git a/packages/core/src/components/graphs/pie.ts b/packages/core/src/components/graphs/pie.ts index 2f2cf3b450..e62437b9df 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, 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), + code + ) + '%' + ) }) // 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..7dd6565b94 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, 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, code)) .attr( 'x', (tick: any) => polarToCartesianCoords(-Math.PI / 2, yScale(tick), c).x + yLabelPadding diff --git a/packages/core/src/configuration.ts b/packages/core/src/configuration.ts index c2cddbc39a..c62ac20abd 100644 --- a/packages/core/src/configuration.ts +++ b/packages/core/src/configuration.ts @@ -80,6 +80,115 @@ const locale: Locale = { number: (value, language = navigator.language) => value.toLocaleString(language), // based on code property if specified date: (value, language = navigator.language, options = {}) => value.toLocaleDateString(language, options), // based on code property if specified + time: (value, language = navigator.language, options) => + value.toLocaleTimeString(language, options), // based on code property if specified + optionsObject: { + 'MMM d, pp': { + obj: { + month: 'short', + day: 'numeric' + }, + type: 'time' + }, + pp: { + obj: {}, + type: 'time' + }, + 'MMM d, p': { + obj: { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit' + }, + type: 'time' + }, + p: { + obj: { + hour: 'numeric', + minute: '2-digit' + }, + type: 'time' + }, + 'MMM d, h:mm:ss.SSS a': { + obj: { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit', + fractionalSecondDigits: 3 + }, + type: 'time' + }, + 'h:mm:ss.SSS a': { + obj: { + hour: 'numeric', + minute: '2-digit', + fractionalSecondDigits: 3 + }, + type: 'time' + }, + 'MMM d': { + obj: { + month: 'short', + day: 'numeric' + }, + type: 'date' + }, + d: { + obj: { + day: 'numeric' + }, + type: 'date' + }, + 'MMM d, hh a': { + obj: { + month: 'short', + day: 'numeric', + hour: '2-digit' + }, + type: 'time' + }, + 'hh a': { + obj: { + hour: '2-digit' + }, + type: 'time' + }, + 'MMM yyyy': { + obj: { + month: 'short', + year: 'numeric' + }, + type: 'date' + }, + MMM: { + obj: { + month: 'short' + }, + type: 'date' + }, + 'eee, MMM d': { + obj: { + weekday: 'short', + month: 'short', + day: 'numeric' + }, + type: 'date' + }, + eee: { + obj: { + weekday: 'short' + }, + type: 'date' + }, + yyyy: { + obj: { + year: 'numeric' + }, + type: 'date' + } + }, translations: { group: 'Group', total: 'Total', diff --git a/packages/core/src/interfaces/components.ts b/packages/core/src/interfaces/components.ts index d760d7b552..33883dd7bd 100644 --- a/packages/core/src/interfaces/components.ts +++ b/packages/core/src/interfaces/components.ts @@ -8,7 +8,6 @@ import type { import type { Component } from '../components' import type { TruncationOptions } from './truncation' - /** *Locale Options Interface */ @@ -16,6 +15,22 @@ export interface Locale { code?: string // BCP 47 language tag number?: (value: number, language: string) => string date?: (value: Date, language: string, options) => string + time?: (value: Date, language: string, options) => string + optionsObject?: Record< + string, + { + obj?: { + month?: string + day?: string + hour?: string + minute?: string + fractionalSecondDigits?: number + weekday?: string + year?: string + } + type?: string + } + > translations?: { group?: string // used by Tooltip and Toolbar / Tabular Representation total?: string // ditto @@ -24,7 +39,7 @@ export interface Locale { toolbarExportAsJPG?: string toolbarExportAsPNG?: string tabularDownloadAsCSV?: string - meterTitle?:string + meterTitle?: string } } diff --git a/packages/core/src/services/time-series.ts b/packages/core/src/services/time-series.ts index df6f0679fa..0c6d7eb1f7 100644 --- a/packages/core/src/services/time-series.ts +++ b/packages/core/src/services/time-series.ts @@ -84,8 +84,10 @@ export function formatTick( i: number, allTicks: Array, interval: string, - timeScaleOptions: TimeScaleOptions + timeScaleOptions: TimeScaleOptions, + options ): string { + const localeObject = getProperty(options, 'locale') const showDayName = timeScaleOptions.showDayName const intervalConsideringAlsoShowDayNameOption = interval === 'daily' && showDayName ? 'weekly' : interval @@ -104,8 +106,22 @@ export function formatTick( } const locale = timeScaleOptions.localeObject - - return format(date, formatString, { locale }) + const { code, optionsObject } = localeObject + if (interval === 'quarterly') { + const formattedDate = format(date, formatString, { locale }) + const formatArr = formattedDate.split('').map(val => { + let num = Number(val) + if (!Number.isNaN(num)) { + return localeObject.number(num, code) + } else { + return val + } + }) + return formatArr.join('') + } else { + const { type, obj } = optionsObject[formatString] + return localeObject[type](date, code, obj) + } } // Given a timestamp, return an object of useful time formats