From 5fbf72b1580020d229a73a6ab4fc46b3443e27df Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 2 Jul 2026 19:09:03 +0200 Subject: [PATCH 1/9] Add tests --- .../core/localization/format.locale.test.ts | 0 .../__internal/core/m_global_format_config.js | 22 ++ .../core/m_global_format_config.test.ts | 207 ------------------ .../localization.globalize.tests.js | 118 ++++++++++ .../localization.intl.tests.js | 179 +++++++++++++++ .../dataGrid.tests.js | 127 +++++++++++ .../datebox.tests.js | 57 +++++ .../numberbox.localization.tests.js | 119 ++++++++++ .../integration.appointmentTooltip.tests.js | 65 ++++++ .../timeline.tests.js | 31 +++ .../axisFormatting.tests.js | 51 +++++ 11 files changed, 769 insertions(+), 207 deletions(-) create mode 100644 packages/devextreme/js/__internal/core/localization/format.locale.test.ts diff --git a/packages/devextreme/js/__internal/core/localization/format.locale.test.ts b/packages/devextreme/js/__internal/core/localization/format.locale.test.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/devextreme/js/__internal/core/m_global_format_config.js b/packages/devextreme/js/__internal/core/m_global_format_config.js index 1f8d66de4482..ea2dacafcd41 100644 --- a/packages/devextreme/js/__internal/core/m_global_format_config.js +++ b/packages/devextreme/js/__internal/core/m_global_format_config.js @@ -6,6 +6,26 @@ import { isFunction, isPlainObject, isString } from '@js/core/utils/type'; const hasOwn = Object.prototype.hasOwnProperty; +export const getEffectiveFormatLocale = (format) => { + if(isPlainObject(format) && format.locale) { + const formatLocale = format.locale; + return isFunction(formatLocale) ? formatLocale() : formatLocale; + } + + return coreLocalization.locale(); +}; + +export const getFormatterOptions = (format) => { + if(!isPlainObject(format) || !format.locale) { + return format; + } + + const stripped = { ...format }; + delete stripped.locale; + + return stripped; +}; + const resolveByLocaleMap = (localeMap) => { let currentLocale = coreLocalization.locale(); @@ -82,4 +102,6 @@ export const resolvePresetOverride = (presetName) => { export default { getGlobalFormatByDataType, resolvePresetOverride, + getEffectiveFormatLocale, + getFormatterOptions, }; diff --git a/packages/devextreme/js/__internal/core/m_global_format_config.test.ts b/packages/devextreme/js/__internal/core/m_global_format_config.test.ts index a07da479e517..e69de29bb2d1 100644 --- a/packages/devextreme/js/__internal/core/m_global_format_config.test.ts +++ b/packages/devextreme/js/__internal/core/m_global_format_config.test.ts @@ -1,207 +0,0 @@ -import { - afterEach, beforeEach, describe, expect, it, -} from '@jest/globals'; -import config from '@js/core/config'; - -import { getGlobalFormatByDataType, resolvePresetOverride } from './m_global_format_config'; - -const GLOBAL_FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const; -type GlobalFormatKey = typeof GLOBAL_FORMAT_KEYS[number]; - -describe('m_global_format_config', () => { - let savedValues: Partial>; - - beforeEach(() => { - const currentConfig = config(); - - savedValues = {}; - GLOBAL_FORMAT_KEYS.forEach((key) => { - savedValues[key] = currentConfig[key]; - }); - }); - - afterEach(() => { - const currentConfig = config(); - - GLOBAL_FORMAT_KEYS.forEach((key) => { - if (savedValues[key] === undefined) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete currentConfig[key]; - } else { - currentConfig[key] = savedValues[key] as never; - } - }); - }); - - describe('getGlobalFormatByDataType', () => { - it('should return undefined when no global formats configured', () => { - expect(getGlobalFormatByDataType('date')).toBeUndefined(); - expect(getGlobalFormatByDataType('time')).toBeUndefined(); - expect(getGlobalFormatByDataType('datetime')).toBeUndefined(); - expect(getGlobalFormatByDataType('number')).toBeUndefined(); - }); - - it('should return undefined for unknown dataType', () => { - expect(getGlobalFormatByDataType('boolean')).toBeUndefined(); - expect(getGlobalFormatByDataType('')).toBeUndefined(); - }); - - it('should resolve string dateFormat', () => { - config({ ...config(), dateFormat: 'dd/MM/yyyy' }); - - expect(getGlobalFormatByDataType('date')).toBe('dd/MM/yyyy'); - }); - - it('should resolve string timeFormat', () => { - config({ ...config(), timeFormat: 'HH:mm:ss' }); - - expect(getGlobalFormatByDataType('time')).toBe('HH:mm:ss'); - }); - - it('should resolve string dateTimeFormat', () => { - config({ ...config(), dateTimeFormat: 'dd/MM/yyyy HH:mm' }); - - expect(getGlobalFormatByDataType('datetime')).toBe('dd/MM/yyyy HH:mm'); - }); - - it('should resolve function dateFormat', () => { - const formatter = (d: Date): string => d.toISOString(); - - config({ ...config(), dateFormat: formatter }); - - expect(getGlobalFormatByDataType('date')).toBe(formatter); - }); - - it('should resolve function numberFormat', () => { - const formatter = (n: number): string => n.toFixed(2); - - config({ ...config(), numberFormat: formatter }); - - expect(getGlobalFormatByDataType('number')).toBe(formatter); - }); - - it('should resolve locale map with default key', () => { - config({ - ...config(), - dateFormat: { - default: 'yyyy-MM-dd', - 'de-DE': 'dd.MM.yyyy', - }, - }); - - // Default locale is 'en', not in map → uses 'default' - expect(getGlobalFormatByDataType('date')).toBe('yyyy-MM-dd'); - }); - - it('should coexist: dateFormat and numberFormat set together', () => { - config({ - ...config(), - dateFormat: 'dd/MM/yyyy', - numberFormat: '#,##0.00', - }); - - expect(getGlobalFormatByDataType('date')).toBe('dd/MM/yyyy'); - expect(getGlobalFormatByDataType('number')).toBe('#,##0.00'); - expect(getGlobalFormatByDataType('time')).toBeUndefined(); - }); - }); - - describe('resolvePresetOverride', () => { - it('should return undefined when dateTimeFormatPresets is not configured', () => { - expect(resolvePresetOverride('shortDate')).toBeUndefined(); - }); - - it('should return undefined for unknown preset name', () => { - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: 'dd/MM/yyyy', - }, - }); - - expect(resolvePresetOverride('unknownPreset')).toBeUndefined(); - }); - - it('should resolve string preset override', () => { - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: 'dd/MM/yyyy', - }, - }); - - expect(resolvePresetOverride('shortDate')).toBe('dd/MM/yyyy'); - }); - - it('should resolve function preset override', () => { - const fn = (d: Date): string => d.toISOString(); - - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: fn, - }, - }); - - expect(resolvePresetOverride('shortDate')).toBe(fn); - }); - - it('should do case-insensitive lookup', () => { - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: 'dd/MM/yyyy', - }, - }); - - expect(resolvePresetOverride('SHORTDATE')).toBe('dd/MM/yyyy'); - expect(resolvePresetOverride('shortdate')).toBe('dd/MM/yyyy'); - expect(resolvePresetOverride('ShortDate')).toBe('dd/MM/yyyy'); - }); - - it('should resolve locale map preset with default key', () => { - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: { - default: 'dd/MM/yyyy', - 'de-DE': 'dd.MM.yyyy', - }, - }, - }); - - // Default locale is 'en', not in map → uses 'default' - expect(resolvePresetOverride('shortDate')).toBe('dd/MM/yyyy'); - }); - - it('should resolve locale map preset with function value', () => { - const fn = (d: Date): string => `${d.getDate()}/${d.getMonth() + 1}`; - - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: { - default: fn, - }, - }, - }); - - expect(resolvePresetOverride('shortDate')).toBe(fn); - }); - - it('should handle multiple preset overrides', () => { - config({ - ...config(), - dateTimeFormatPresets: { - shortDate: 'dd/MM/yyyy', - longDate: 'EEEE, dd MMMM yyyy', - shortTime: 'HH:mm', - }, - }); - - expect(resolvePresetOverride('shortDate')).toBe('dd/MM/yyyy'); - expect(resolvePresetOverride('longDate')).toBe('EEEE, dd MMMM yyyy'); - expect(resolvePresetOverride('shortTime')).toBe('HH:mm'); - }); - }); -}); diff --git a/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js b/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js index d028a628baf6..d98241733e98 100644 --- a/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js @@ -43,6 +43,7 @@ define(function(require, exports, module) { const dateLocalization = require('common/core/localization/date'); const messageLocalization = require('common/core/localization/message'); const config = require('core/config'); + const coreLocalization = require('common/core/localization/core'); const ExcelJSLocalizationFormatTests = require('../DevExpress.exporter/exceljsParts/exceljs.format.tests.js'); @@ -954,6 +955,123 @@ define(function(require, exports, module) { }); }); }); + + QUnit.module('Global formatting config - format locale (spec, globalize)', () => { + const saveGlobalFormats = () => { + const globalConfig = config(); + + return { + dateFormat: globalConfig.dateFormat, + timeFormat: globalConfig.timeFormat, + dateTimeFormat: globalConfig.dateTimeFormat, + numberFormat: globalConfig.numberFormat, + dateTimeFormatPresets: globalConfig.dateTimeFormatPresets, + }; + }; + const restoreGlobalFormats = (saved) => { + const globalConfig = config(); + + Object.keys(saved).forEach((key) => { + const value = saved[key]; + if(value === undefined) { + delete globalConfig[key]; + } else { + globalConfig[key] = value; + } + }); + }; + + QUnit.test('numberFormat locale overrides message locale for Globalize formatting', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = coreLocalization.locale(); + const savedGlobalizeLocale = Globalize.locale().locale; + + try { + Globalize.locale('de'); + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + assert.strictEqual(numberLocalization.format(1234.56), '1,234.56'); + } finally { + Globalize.locale(savedGlobalizeLocale); + coreLocalization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('dateFormat locale overrides message locale for shortDate preset', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = coreLocalization.locale(); + const savedGlobalizeLocale = Globalize.locale().locale; + + try { + Globalize.locale('en'); + coreLocalization.locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + assert.strictEqual( + dateLocalization.format(new Date(2020, 0, 2), { + locale: 'de-DE', + type: 'shortDate', + }), + '02.01.2020', + ); + } finally { + Globalize.locale(savedGlobalizeLocale); + coreLocalization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('locale map selects entry by message locale and applies format locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = coreLocalization.locale(); + const savedGlobalizeLocale = Globalize.locale().locale; + + try { + Globalize.locale('de'); + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + de: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + default: { + locale: 'de-DE', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + assert.strictEqual(numberLocalization.format(1234.56), '1,234.56'); + } finally { + Globalize.locale(savedGlobalizeLocale); + coreLocalization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + }); }); diff --git a/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js b/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js index b78eaa959015..6657491d6633 100644 --- a/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js @@ -949,6 +949,185 @@ QUnit.module('Global formatting config (spec, intl)', () => { }); }); +QUnit.module('Global formatting config - format locale (spec, intl)', () => { + const saveGlobalFormats = () => { + const globalConfig = config(); + + return { + dateFormat: globalConfig.dateFormat, + timeFormat: globalConfig.timeFormat, + dateTimeFormat: globalConfig.dateTimeFormat, + numberFormat: globalConfig.numberFormat, + dateTimeFormatPresets: globalConfig.dateTimeFormatPresets, + }; + }; + const restoreGlobalFormats = (saved) => { + const globalConfig = config(); + + Object.keys(saved).forEach((key) => { + const value = saved[key]; + if(value === undefined) { + delete globalConfig[key]; + } else { + globalConfig[key] = value; + } + }); + }; + + QUnit.test('numberFormat locale overrides message locale for Intl formatting', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + + try { + locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + assert.strictEqual(numberLocalization.format(1234.56), '1,234.56'); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('dateFormat locale overrides message locale for shortDate preset', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + + try { + locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + assert.strictEqual( + dateLocalization.format(new Date(2020, 0, 2), { + locale: 'de-DE', + type: 'shortDate', + }), + '02.01.2020', + ); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('numberFormat dynamic locale function is applied at format time', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + let dynamicLocale = 'en-US'; + + try { + locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: () => dynamicLocale, + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + assert.strictEqual(numberLocalization.format(1234.56), '1,234.56'); + + dynamicLocale = 'de-DE'; + assert.strictEqual(numberLocalization.format(1234.56), '1.234,56'); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('locale map selects entry by message locale and applies format locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + + try { + locale('de'); + config({ + ...config(), + numberFormat: { + de: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + default: { + locale: 'de-DE', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + assert.strictEqual(numberLocalization.format(1234.56), '1,234.56'); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('LDML numberFormat is unaffected by message locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + + try { + locale('de'); + config({ + ...config(), + numberFormat: '#,##0.00', + }); + + assert.strictEqual(numberLocalization.format(1234.5), '1,234.50'); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('format object without locale keeps message locale behavior', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + + try { + locale('de'); + config({ + ...config(), + numberFormat: { + default: { + type: 'fixedPoint', + precision: 2, + }, + }, + }); + + const result = numberLocalization.format(1234.56); + assert.ok(result.indexOf(',56') > -1, 'uses de decimal separator'); + assert.ok(result.indexOf('.') > -1, 'uses de thousands separator'); + } finally { + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); +}); + QUnit.module('Exceljs format', () => { ExcelJSLocalizationFormatTests.runCurrencyTests([ { value: 'USD', expected: '$#,##0_);\\($#,##0\\)' }, diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js index 1b71ac6ce598..bdb8d7b9fa62 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js @@ -13,6 +13,7 @@ import messageLocalization from 'common/core/localization/message'; import { setTemplateEngine } from 'core/templates/template_engine_registry'; import fx from 'common/core/animation/fx'; import config from 'core/config'; +import localization from 'localization'; import ajaxMock from '../../helpers/ajaxMock.js'; import DataGridWrapper from '../../helpers/wrappers/dataGridWrappers.js'; import { getEmulatorStyles } from '../../helpers/stylesHelper.js'; @@ -5672,4 +5673,130 @@ QUnit.module('Global formatting config (spec)', baseModuleConfig, () => { restoreGlobalFormats(saved); } }); + + QUnit.test('implicit number column uses global numberFormat locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = localization.locale(); + + try { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const format = gridCore.getFormatByDataType('number'); + const numberText = gridCore.formatValue(1234.56, { format }); + + assert.strictEqual(numberText, '1,234.56', 'global number format locale is applied for implicit number column'); + } finally { + localization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('filter row NumberBox uses global numberFormat locale for display and parsing', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = localization.locale(); + const TEXTEDITOR_INPUT_SELECTOR = '.dx-texteditor-input'; + + try { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const dataGrid = createDataGrid({ + dataSource: [{ amount: 1234.56 }], + columns: [{ dataField: 'amount', dataType: 'number' }], + filterRow: { visible: true }, + }); + this.clock.tick(10); + + const cellText = $(dataGrid.getCellElement(0, 0)).text().trim(); + assert.strictEqual(cellText, '1,234.56', 'grid cell uses en-US number format'); + + const $filterInput = $(dataGrid.$element()).find('.dx-datagrid-filter-row').find(TEXTEDITOR_INPUT_SELECTOR).first(); + const filterNumberBox = $filterInput.closest('.dx-numberbox').dxNumberBox('instance'); + + filterNumberBox.option('value', 1234.56); + assert.strictEqual($filterInput.val(), '1,234.56', 'filter row displays en-US separators'); + + filterNumberBox.option('value', null); + $filterInput.val('1234.56').trigger('change'); + assert.strictEqual(filterNumberBox.option('value'), 1234.56, 'filter row parses en-US decimal separator'); + } finally { + localization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('explicit column.format uses global numberFormat locale for culture', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = localization.locale(); + + try { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const dataGrid = createDataGrid({ + dataSource: [{ amount: 1234.56 }], + columns: [{ + dataField: 'amount', + dataType: 'number', + format: { type: 'fixedPoint', precision: 0 }, + }], + }); + this.clock.tick(10); + + const numberText = $(dataGrid.getCellElement(0, 0)).text().trim(); + assert.strictEqual(numberText, '1,235', 'column type from format; en-US locale from global numberFormat'); + } finally { + localization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); + + QUnit.test('LDML global numberFormat is unaffected by message locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = localization.locale(); + + try { + localization.locale('de'); + config({ + ...config(), + numberFormat: '#,##0.00', + }); + + const format = gridCore.getFormatByDataType('number'); + const numberText = gridCore.formatValue(1234.5, { format }); + + assert.strictEqual(numberText, '1,234.50', 'LDML pattern is locale-independent'); + } finally { + localization.locale(savedLocale); + restoreGlobalFormats(saved); + } + }); }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js index fa7820a3fbd9..bafb3b7ec323 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js @@ -3580,4 +3580,61 @@ QUnit.module('Global formatting config (spec)', { assert.strictEqual($input.val(), '02/01/2020'); }); + + QUnit.test('implicit date displayFormat uses global dateFormat locale', function(assert) { + const savedLocale = localization.locale(); + + try { + localization.locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + const $element = $('#dateBox').dxDateBox({ + type: 'date', + value: new Date(2020, 0, 2), + pickerType: 'calendar', + }); + const $input = $element.find(`.${TEXTEDITOR_INPUT_CLASS}`); + + assert.strictEqual($input.val(), '02.01.2020', 'date input uses de-DE shortDate pattern'); + } finally { + localization.locale(savedLocale); + } + }); + + QUnit.test('explicit displayFormat keeps priority over global dateFormat locale', function(assert) { + const savedLocale = localization.locale(); + + try { + localization.locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + const $element = $('#dateBox').dxDateBox({ + type: 'date', + value: new Date(2020, 0, 2), + displayFormat: 'yyyy-MM-dd', + pickerType: 'calendar', + }); + const $input = $element.find(`.${TEXTEDITOR_INPUT_CLASS}`); + + assert.strictEqual($input.val(), '2020-01-02', 'explicit displayFormat wins over global dateFormat locale'); + } finally { + localization.locale(savedLocale); + } + }); }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js index 89e4fa2b391c..82eb7c186cd3 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js @@ -138,3 +138,122 @@ QUnit.module('localization: global number format', { assert.strictEqual($input.val(), '1,235', 'defaultOptions format wins over global'); }); }); + +QUnit.module('localization: global number format locale', { + beforeEach: function() { + this.savedConfig = { ...config() }; + this.savedLocale = localization.locale(); + localization.locale('en'); + }, + + afterEach: function() { + localization.locale(this.savedLocale); + config(this.savedConfig); + NumberBox.defaultOptions([]); + }, +}, () => { + QUnit.test('uses format locale from global numberFormat map with de message locale', function(assert) { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const $element = $('#numberbox').dxNumberBox({ + value: 1234.56, + useMaskBehavior: true, + }); + const $input = $element.find(TEXTEDITOR_INPUT_CLASS); + + assert.strictEqual($input.val(), '1,234.56', 'display uses en-US separators'); + }); + + QUnit.test('parses value using effective format locale separators', function(assert) { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const $element = $('#numberbox').dxNumberBox({ + value: null, + useMaskBehavior: true, + }); + const instance = $element.dxNumberBox('instance'); + const $input = $element.find(TEXTEDITOR_INPUT_CLASS); + const keyboard = keyboardMock($input, true); + + keyboard + .caret({ start: 0, end: 0 }) + .type('1234.56'); + + assert.strictEqual(instance.option('value'), 1234.56, 'parses en-US decimal separator'); + }); + + QUnit.test('dynamic format locale function is applied in NumberBox', function(assert) { + let dynamicLocale = 'en-US'; + + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: () => dynamicLocale, + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const $element = $('#numberbox').dxNumberBox({ + value: 1234.56, + useMaskBehavior: true, + }); + const $input = $element.find(TEXTEDITOR_INPUT_CLASS); + + assert.strictEqual($input.val(), '1,234.56'); + + dynamicLocale = 'de-DE'; + $element.dxNumberBox('instance').option('value', 1234.56); + + assert.strictEqual($input.val(), '1.234,56'); + }); + + QUnit.test('local format option keeps type; global numberFormat locale applies to culture', function(assert) { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + const $element = $('#numberbox').dxNumberBox({ + value: 1234.56, + useMaskBehavior: true, + format: { + type: 'fixedPoint', + precision: 0, + }, + }); + const $input = $element.find(TEXTEDITOR_INPUT_CLASS); + + assert.strictEqual($input.val(), '1,235', 'precision from local format; en-US from global numberFormat'); + }); +}); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js index ec0ba81468b9..1178a5619bfd 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js @@ -7,6 +7,7 @@ import resizeCallbacks from 'core/utils/resize_callbacks'; import fx from 'common/core/animation/fx'; import dateLocalization from 'common/core/localization/date'; import messageLocalization from 'common/core/localization/message'; +import { locale } from 'common/core/localization/core'; import { DataSource } from 'common/data/data_source/data_source'; import keyboardMock from '../../helpers/keyboardMock.js'; import devices from '__internal/core/m_devices'; @@ -116,6 +117,70 @@ module('Global formatting config (spec): Scheduler tooltip', { assert.strictEqual(scheduler.tooltip.getDateText(), 'Date9 Time23 - Date10 Time1'); }); + + test('implicit Scheduler tooltip time uses global timeFormat locale', async function(assert) { + const savedLocale = locale(); + + try { + locale('en'); + config({ + ...config(), + timeFormat: { + default: { + locale: 'de-DE', + type: 'shortTime', + }, + }, + }); + + const scheduler = await createScheduler(); + const clock = sinon.useFakeTimers(); + await scheduler.appointments.click(0, clock); + clock.restore(); + + assert.strictEqual(scheduler.tooltip.getDateText(), '11:00 - 12:00'); + } finally { + locale(savedLocale); + } + }); + + test('implicit Scheduler tooltip date/time use global dateFormat and timeFormat locale', async function(assert) { + const savedLocale = locale(); + + try { + locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + timeFormat: { + default: { + locale: 'de-DE', + type: 'shortTime', + }, + }, + }); + + const scheduler = await createScheduler({ + dataSource: [{ + text: 'Task 1', + startDate: new Date(2015, 1, 9, 23, 0), + endDate: new Date(2015, 1, 10, 1, 0), + }], + }); + const clock = sinon.useFakeTimers(); + await scheduler.appointments.click(0, clock); + clock.restore(); + + assert.strictEqual(scheduler.tooltip.getDateText(), '09.02.2015 23:00 - 10.02.2015 01:00'); + } finally { + locale(savedLocale); + } + }); }); module('Integration: Appointment tooltip', moduleConfig, () => { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/timeline.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/timeline.tests.js index 46cf4849d49d..2f1f911cf3fa 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/timeline.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/timeline.tests.js @@ -1,5 +1,6 @@ import { getOuterWidth, getOuterHeight } from 'core/utils/size'; import config from 'core/config'; +import localization from 'localization'; import dateUtils from 'core/utils/date'; import resizeCallbacks from 'core/utils/resize_callbacks'; import { triggerHidingEvent, triggerShownEvent } from 'common/core/events/visibility_change'; @@ -84,6 +85,36 @@ QUnit.test('Timeline header uses global timeFormat when format is implicit', fun } }); +QUnit.test('Timeline header uses global timeFormat locale when format is implicit', function(assert) { + const savedConfig = { ...config() }; + const savedLocale = localization.locale(); + + try { + localization.locale('en'); + config({ + ...config(), + timeFormat: { + default: { + locale: 'de-DE', + type: 'shortTime', + }, + }, + }); + + this.instance.option({ + startDayHour: 8, + endDayHour: 10, + hoursInterval: 1, + }); + + const $firstHeaderCell = this.instance.$element().find('.dx-scheduler-header-panel-cell').first(); + assert.strictEqual($firstHeaderCell.text().trim(), '08:00', 'header cell uses de-DE time format locale'); + } finally { + localization.locale(savedLocale); + config(savedConfig); + } +}); + QUnit.test('Header scrollable shouldn\'t update position if date scrollable position is changed to bottom', async function(assert) { const $element = this.instance.$element(); diff --git a/packages/devextreme/testing/tests/DevExpress.viz.core/axisFormatting.tests.js b/packages/devextreme/testing/tests/DevExpress.viz.core/axisFormatting.tests.js index 7138b4ecfdec..f46d4e5eac4d 100644 --- a/packages/devextreme/testing/tests/DevExpress.viz.core/axisFormatting.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.viz.core/axisFormatting.tests.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import config from 'core/config'; +import localization from 'localization'; import translator2DModule from 'viz/translators/translator2d'; import { Range } from 'viz/translators/range'; import tickGeneratorModule from 'viz/axes/tick_generator'; @@ -218,6 +219,56 @@ QUnit.test('datetime axis labels use global dateTimeFormat when label.format is }, [new Date(2020, 0, 2)], 'day', ['2020-01-02']); }); +QUnit.test('value axis labels use global numberFormat locale when label.format is not set', function(assert) { + const savedLocale = localization.locale(); + + try { + localization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + this.testTickLabelFormat(assert, [1500], 100, ['1,500.00']); + } finally { + localization.locale(savedLocale); + } +}); + +QUnit.test('datetime axis labels use global dateTimeFormat locale when label.format is not set', function(assert) { + const savedLocale = localization.locale(); + + try { + localization.locale('en'); + config({ + ...config(), + dateTimeFormat: { + default: { + locale: 'de-DE', + day: '2-digit', + month: '2-digit', + year: 'numeric', + }, + }, + }); + + this.testFormat(assert, { + argumentType: 'datetime', + label: { + visible: true, + }, + }, [new Date(2020, 0, 2)], 'day', ['02.01.2020']); + } finally { + localization.locale(savedLocale); + } +}); + QUnit.module('Auto formatting. Tick labels. Numeric.', environment); QUnit.test('formatter should support short notations of numbers', function(assert) { From dd63e5f46bfc5ab7ab2053fcbf97a919602f7edf Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 3 Jul 2026 07:31:48 +0200 Subject: [PATCH 2/9] Add tests --- .../core/localization/format.locale.test.ts | 255 ++++++++++++++++++ .../core/m_global_format_config.test.ts | 200 ++++++++++++++ .../localization.globalize.tests.js | 2 +- .../dataGrid.tests.js | 4 +- .../datebox.tests.js | 28 -- 5 files changed, 458 insertions(+), 31 deletions(-) diff --git a/packages/devextreme/js/__internal/core/localization/format.locale.test.ts b/packages/devextreme/js/__internal/core/localization/format.locale.test.ts index e69de29bb2d1..52bae45bce9a 100644 --- a/packages/devextreme/js/__internal/core/localization/format.locale.test.ts +++ b/packages/devextreme/js/__internal/core/localization/format.locale.test.ts @@ -0,0 +1,255 @@ +import { + afterEach, beforeEach, describe, expect, it, jest, +} from '@jest/globals'; +import coreLocalization from '@js/common/core/localization/core'; +import dateLocalization from '@js/common/core/localization/date'; +import numberLocalization from '@js/common/core/localization/number'; +import config from '@js/core/config'; + +const GLOBAL_FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const; +type GlobalFormatKey = typeof GLOBAL_FORMAT_KEYS[number]; + +const saveAndRestore = (): { save: () => void; restore: () => void } => { + let savedValues: Partial> = {}; + let savedLocale = ''; + + return { + save() { + savedLocale = coreLocalization.locale(); + const currentConfig = config(); + + savedValues = {}; + GLOBAL_FORMAT_KEYS.forEach((key) => { + savedValues[key] = currentConfig[key]; + }); + }, + restore() { + coreLocalization.locale(savedLocale); + const currentConfig = config(); + + GLOBAL_FORMAT_KEYS.forEach((key) => { + if (savedValues[key] === undefined) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete currentConfig[key]; + } else { + currentConfig[key] = savedValues[key] as never; + } + }); + }, + }; +}; + +describe('format locale integration', () => { + const { save, restore } = saveAndRestore(); + + beforeEach(() => { save(); }); + afterEach(() => { restore(); }); + + describe('numbers', () => { + it('should format using global numberFormat locale instead of message locale', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.format(1234.56)).toBe('1,234.56'); + }); + + it('should apply global numberFormat locale to explicit format type', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.format(1234.56, { type: 'fixedPoint', precision: 0 })).toBe('1,235'); + }); + + it('should prefer explicit format locale over global numberFormat locale', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.format(1234.56, { + type: 'fixedPoint', + precision: 0, + locale: 'de-DE', + })).toBe('1.235'); + }); + + it('should parse using effective number format locale separators', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.parse('1234.56')).toBe(1234.56); + }); + + it('should apply message locale separators to LDML global numberFormat', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: '#,##0.00', + }); + + expect(numberLocalization.format(1234.5)).toBe('1.234,50'); + }); + + it('should apply dynamic global numberFormat locale at format time', () => { + let dynamicLocale = 'en-US'; + + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: () => dynamicLocale, + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.format(1234.56)).toBe('1,234.56'); + + dynamicLocale = 'de-DE'; + expect(numberLocalization.format(1234.56)).toBe('1.234,56'); + }); + + it('should use global numberFormat locale for decimal and thousands separators', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + }, + }); + + expect(numberLocalization.getDecimalSeparator()).toBe('.'); + expect(numberLocalization.getThousandsSeparator()).toBe(','); + }); + + it('should not pass locale metadata to Intl.NumberFormat options', () => { + coreLocalization.locale('de'); + config({ + ...config(), + numberFormat: { + default: { + locale: 'en-US', + minimumFractionDigits: 3, + maximumFractionDigits: 3, + }, + }, + }); + + const numberFormatSpy = jest.spyOn(Intl, 'NumberFormat'); + numberLocalization.format(1.234); + + numberFormatSpy.mock.calls.forEach(([, options]) => { + expect(options?.locale).toBeUndefined(); + }); + + numberFormatSpy.mockRestore(); + }); + }); + + describe('dates', () => { + it('should format implicit shortDate using global dateFormat locale', () => { + coreLocalization.locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + expect(dateLocalization.format(new Date(2020, 0, 2), { + locale: 'de-DE', + type: 'shortDate', + })).toBe('2.1.2020'); + }); + + it('should format implicit shortDate preset using global dateFormat locale', () => { + coreLocalization.locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + expect(dateLocalization.format(new Date(2020, 0, 2), 'shortDate')).toBe('2.1.2020'); + }); + + it('should format using explicit date format locale', () => { + coreLocalization.locale('en'); + + expect(dateLocalization.format(new Date(2020, 0, 2), { + locale: 'de-DE', + year: 'numeric', + month: '2-digit', + day: '2-digit', + })).toBe('02.01.2020'); + }); + + it('should not pass locale metadata to Intl.DateTimeFormat options', () => { + const dateTimeFormatSpy = jest.spyOn(Intl, 'DateTimeFormat'); + + coreLocalization.locale('en'); + + dateLocalization.format(new Date(2021, 5, 15), { + locale: 'de-DE', + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); + + dateTimeFormatSpy.mock.calls.forEach(([, options]) => { + expect(options?.locale).toBeUndefined(); + }); + + dateTimeFormatSpy.mockRestore(); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/core/m_global_format_config.test.ts b/packages/devextreme/js/__internal/core/m_global_format_config.test.ts index e69de29bb2d1..32533b188058 100644 --- a/packages/devextreme/js/__internal/core/m_global_format_config.test.ts +++ b/packages/devextreme/js/__internal/core/m_global_format_config.test.ts @@ -0,0 +1,200 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; +import coreLocalization from '@js/common/core/localization/core'; +import config from '@js/core/config'; + +import { + getEffectiveFormatLocale, + getFormatterOptions, + getGlobalFormatByDataType, + resolvePresetOverride, +} from './m_global_format_config'; + +const GLOBAL_FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const; +type GlobalFormatKey = typeof GLOBAL_FORMAT_KEYS[number]; + +const saveAndRestore = (): { save: () => void; restore: () => void } => { + let savedValues: Partial> = {}; + let savedLocale = ''; + + return { + save() { + savedLocale = coreLocalization.locale(); + const currentConfig = config(); + + savedValues = {}; + GLOBAL_FORMAT_KEYS.forEach((key) => { + savedValues[key] = currentConfig[key]; + }); + }, + restore() { + coreLocalization.locale(savedLocale); + const currentConfig = config(); + + GLOBAL_FORMAT_KEYS.forEach((key) => { + if (savedValues[key] === undefined) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete currentConfig[key]; + } else { + currentConfig[key] = savedValues[key] as never; + } + }); + }, + }; +}; + +describe('m_global_format_config', () => { + const { save, restore } = saveAndRestore(); + + beforeEach(() => { save(); }); + afterEach(() => { restore(); }); + + describe('getGlobalFormatByDataType', () => { + it('should resolve locale map entry by message locale', () => { + config({ + ...config(), + numberFormat: { + de: { locale: 'en-US', minimumFractionDigits: 2 }, + default: { locale: 'de-DE', minimumFractionDigits: 2 }, + }, + }); + coreLocalization.locale('de'); + + expect(getGlobalFormatByDataType('number')).toEqual({ + locale: 'en-US', + minimumFractionDigits: 2, + }); + }); + }); + + describe('resolvePresetOverride', () => { + it('should resolve preset override from locale map', () => { + config({ + ...config(), + dateTimeFormatPresets: { + shortDate: { + de: 'dd.MM.yyyy', + default: 'dd/MM/yyyy', + }, + }, + }); + coreLocalization.locale('de'); + + expect(resolvePresetOverride('shortDate')).toBe('dd.MM.yyyy'); + }); + }); + + describe('getEffectiveFormatLocale - global config dataType resolution', () => { + it('should resolve locale via getGlobalFormatByDataType when global config type matches preset', () => { + config({ + ...config(), + timeFormat: { + default: { + locale: 'de-DE', + type: 'shortTime', + }, + }, + }); + coreLocalization.locale('en'); + + expect(getEffectiveFormatLocale(undefined, undefined, 'shortTime')).toBe('de-DE'); + }); + + it('should resolve locale via getGlobalFormatByDataType for implicit shortDate preset', () => { + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + }, + }, + }); + coreLocalization.locale('en'); + + expect(getEffectiveFormatLocale(undefined, undefined, 'shortDate')).toBe('de-DE'); + }); + + it('should infer dataType from Intl format object options', () => { + config({ + ...config(), + timeFormat: { + default: { + locale: 'de-DE', + }, + }, + }); + coreLocalization.locale('en'); + + expect(getEffectiveFormatLocale({ + hour: 'numeric', + minute: 'numeric', + })).toBe('de-DE'); + }); + + it('should use explicit dataType with getGlobalFormatByDataType', () => { + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + }, + }, + }); + coreLocalization.locale('en'); + + expect(getEffectiveFormatLocale(undefined, 'date')).toBe('de-DE'); + }); + }); + + describe('getEffectiveFormatLocale', () => { + it('should return string locale from format object', () => { + expect(getEffectiveFormatLocale({ locale: 'en-US' })).toBe('en-US'); + }); + + it('should evaluate function locale', () => { + expect(getEffectiveFormatLocale({ locale: () => 'de-DE' })).toBe('de-DE'); + }); + + it('should fall back to global numberFormat locale', () => { + config({ + ...config(), + numberFormat: { + default: { locale: 'en-US', minimumFractionDigits: 2 }, + }, + }); + coreLocalization.locale('de'); + + expect(getEffectiveFormatLocale({ type: 'fixedPoint', precision: 0 }, 'number')).toBe('en-US'); + }); + + it('should fall back to message locale when no format locale is configured', () => { + coreLocalization.locale('de'); + + expect(getEffectiveFormatLocale({ type: 'fixedPoint', precision: 0 }, 'number')).toBe('de'); + }); + }); + + describe('getFormatterOptions', () => { + it('should remove locale metadata from format object', () => { + expect(getFormatterOptions({ + locale: 'en-US', + minimumFractionDigits: 2, + })).toEqual({ + minimumFractionDigits: 2, + }); + }); + + it('should return non-object format as-is', () => { + expect(getFormatterOptions('fixedPoint')).toBe('fixedPoint'); + }); + + it('should not mutate the original format object', () => { + const formatObject = { locale: 'en-US', precision: 2 }; + + getFormatterOptions(formatObject); + + expect(formatObject).toEqual({ locale: 'en-US', precision: 2 }); + }); + }); +}); diff --git a/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js b/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js index d98241733e98..42d063ff73dd 100644 --- a/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.localization/localization.globalize.tests.js @@ -1031,7 +1031,7 @@ define(function(require, exports, module) { locale: 'de-DE', type: 'shortDate', }), - '02.01.2020', + '2.1.2020', ); } finally { Globalize.locale(savedGlobalizeLocale); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js index bdb8d7b9fa62..5c201a044a86 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js @@ -5779,7 +5779,7 @@ QUnit.module('Global formatting config (spec)', baseModuleConfig, () => { } }); - QUnit.test('LDML global numberFormat is unaffected by message locale', function(assert) { + QUnit.test('LDML global numberFormat uses message locale separators', function(assert) { const saved = saveGlobalFormats(); const savedLocale = localization.locale(); @@ -5793,7 +5793,7 @@ QUnit.module('Global formatting config (spec)', baseModuleConfig, () => { const format = gridCore.getFormatByDataType('number'); const numberText = gridCore.formatValue(1234.5, { format }); - assert.strictEqual(numberText, '1,234.50', 'LDML pattern is locale-independent'); + assert.strictEqual(numberText, '1.234,50', 'LDML pattern uses separators from message locale'); } finally { localization.locale(savedLocale); restoreGlobalFormats(saved); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js index bafb3b7ec323..d89dc557c649 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.tests.js @@ -3581,34 +3581,6 @@ QUnit.module('Global formatting config (spec)', { assert.strictEqual($input.val(), '02/01/2020'); }); - QUnit.test('implicit date displayFormat uses global dateFormat locale', function(assert) { - const savedLocale = localization.locale(); - - try { - localization.locale('en'); - config({ - ...config(), - dateFormat: { - default: { - locale: 'de-DE', - type: 'shortDate', - }, - }, - }); - - const $element = $('#dateBox').dxDateBox({ - type: 'date', - value: new Date(2020, 0, 2), - pickerType: 'calendar', - }); - const $input = $element.find(`.${TEXTEDITOR_INPUT_CLASS}`); - - assert.strictEqual($input.val(), '02.01.2020', 'date input uses de-DE shortDate pattern'); - } finally { - localization.locale(savedLocale); - } - }); - QUnit.test('explicit displayFormat keeps priority over global dateFormat locale', function(assert) { const savedLocale = localization.locale(); From b4400e1ada38bb17147e036116194676a742e3f7 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 3 Jul 2026 07:34:55 +0200 Subject: [PATCH 3/9] implementation --- .../core/localization/globalize/date.ts | 173 ++++++++++++---- .../core/localization/globalize/number.ts | 55 ++++- .../__internal/core/localization/intl/date.ts | 190 ++++++++++++------ .../core/localization/intl/number.ts | 43 +++- .../__internal/core/m_global_format_config.js | 104 +++++++++- .../localization.intl.tests.js | 44 +++- 6 files changed, 494 insertions(+), 115 deletions(-) diff --git a/packages/devextreme/js/__internal/core/localization/globalize/date.ts b/packages/devextreme/js/__internal/core/localization/globalize/date.ts index 4fb977a4df58..4e4c48a5153a 100644 --- a/packages/devextreme/js/__internal/core/localization/globalize/date.ts +++ b/packages/devextreme/js/__internal/core/localization/globalize/date.ts @@ -6,7 +6,11 @@ import 'globalize/date'; import type { Format as LocalizationFormat, FormatObject } from '@js/localization'; import type { DateFormatter, DateParser, Format } from '@ts/core/localization/date'; import dateLocalization from '@ts/core/localization/date'; -import { resolvePresetOverride } from '@ts/core/m_global_format_config'; +import { + getEffectiveFormatLocale, + getFormatterOptions, + resolvePresetOverride, +} from '@ts/core/m_global_format_config'; import * as iteratorUtils from '@ts/core/utils/m_iterator'; import { isObject } from '@ts/core/utils/m_type'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -22,6 +26,45 @@ type GlobalizeFormat = { const ACCEPTABLE_JSON_FORMAT_PROPERTIES = ['skeleton', 'date', 'time', 'datetime', 'raw']; const RTL_MARKS_REGEX = /[\u200E\u200F]/g; +type GlobalizeDateFormatOptions = FormatObject & { + raw?: string; + skeleton?: string; + date?: string; + time?: string; + datetime?: string; +}; + +const resolveGlobalizeLocale = (formatLocale: string): string => { + const currentLocale = Globalize.locale().locale as string; + + if (formatLocale === currentLocale) { + return currentLocale; + } + + Globalize.locale(formatLocale); + try { + return Globalize.locale().locale as string; + } finally { + Globalize.locale(currentLocale); + } +}; + +const getGlobalizeDateFormatter = ( + formatLocale: string, + format: GlobalizeDateFormatOptions | string, +): DateFormatter => { + const resolvedLocale = resolveGlobalizeLocale(formatLocale); + const currentLocale = Globalize.locale().locale; + + if (resolvedLocale === currentLocale) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Globalize.dateFormatter(format); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return new Globalize(resolvedLocale).dateFormatter(format); +}; + if (Globalize?.formatDate) { if (Globalize.locale().locale === 'en') { Globalize.locale('en'); @@ -96,7 +139,20 @@ if (Globalize?.formatDate) { return 'globalize'; }, - _getPatternByFormat(format: string): string | undefined { + _getGlobalizeFormatterOptions( + format: string, + formatLocale?: string, + ): GlobalizeDateFormatOptions { + if (format.toLowerCase() === 'datetime-local') { + return { raw: 'yyyy-MM-ddTHH\':\'mm\':\'ss' }; + } + + return { + raw: this._getPatternByFormat(format, formatLocale) || format, + }; + }, + + _getPatternByFormat(format: string, formatLocale?: string): string | undefined { // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; const lowerFormat = format.toLowerCase(); @@ -111,20 +167,40 @@ if (Globalize?.formatDate) { } let result: string = 'path' in globalizeFormat - ? that._getFormatStringByPath(globalizeFormat.path) + ? that._getFormatStringByPath(globalizeFormat.path, formatLocale) : globalizeFormat.pattern; if ('parts' in globalizeFormat) { iteratorUtils.each(globalizeFormat.parts, (index: number, part: string): void => { - result = result.replace(`{${index}}`, that._getPatternByFormat(part)); + result = result.replace(`{${index}}`, that._getPatternByFormat(part, formatLocale) ?? ''); }); } return result; }, - _getFormatStringByPath(path: string): string { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return Globalize.locale().main(`dates/calendars/gregorian/${path}`); + _getFormatStringByPath(path: string, formatLocale?: string): string { + const fullPath = `dates/calendars/gregorian/${path}`; + const currentLocale = Globalize.locale().locale; + + if (!formatLocale) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Globalize.locale().main(fullPath); + } + + const resolvedLocale = resolveGlobalizeLocale(formatLocale); + + if (resolvedLocale === currentLocale) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Globalize.locale().main(fullPath); + } + + Globalize.locale(resolvedLocale); + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Globalize.locale().main(fullPath); + } finally { + Globalize.locale(currentLocale); + } }, getPeriodNames(format: Format, type: string): string[] { @@ -169,11 +245,6 @@ if (Globalize?.formatDate) { return date; } - // eslint-disable-next-line @typescript-eslint/init-declarations - let formatter: DateFormatter; - // eslint-disable-next-line @typescript-eslint/init-declarations - let formatCacheKey: string; - if (typeof format === 'function') { return (format as DateFormatter)(date); } @@ -183,45 +254,73 @@ if (Globalize?.formatDate) { return (format.formatter as DateFormatter)(date); } - // eslint-disable-next-line no-param-reassign - format = (format as FormatObject).type ?? format; + const sourceFormat = format; + let resolvedFormat: LocalizationFormat = (format as FormatObject).type ?? format; - if (typeof format === 'string') { - const presetOverride = resolvePresetOverride(format); + if (typeof resolvedFormat === 'string') { + const presetOverride = resolvePresetOverride(resolvedFormat); if (presetOverride !== undefined) { if (typeof presetOverride === 'function') { return (presetOverride as DateFormatter)(date); } - if (typeof presetOverride === 'string') { - // eslint-disable-next-line no-param-reassign - format = presetOverride; - } else if (isObject(presetOverride) && this._isAcceptableFormat(presetOverride)) { - formatter = Globalize.dateFormatter(presetOverride); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return this.removeRtlMarks(formatter(date)); - } + + resolvedFormat = presetOverride as LocalizationFormat; } + } + + // eslint-disable-next-line @typescript-eslint/init-declarations + let formatter: DateFormatter; + // eslint-disable-next-line @typescript-eslint/init-declarations + let formatCacheKey: string; - formatCacheKey = `${Globalize.locale().locale}:${format}`; + if (typeof resolvedFormat === 'string') { + const formatLocale = getEffectiveFormatLocale( + typeof sourceFormat === 'object' ? sourceFormat : undefined, + undefined, + resolvedFormat, + ); + const resolvedLocale = resolveGlobalizeLocale(formatLocale); + formatCacheKey = `${resolvedLocale}:${resolvedFormat}`; formatter = formattersCache[formatCacheKey]; if (!formatter) { - // eslint-disable-next-line no-param-reassign - format = { - // @ts-expect-error - raw: this._getPatternByFormat(format) || format, - }; + const globalizeFormat = this._getGlobalizeFormatterOptions(resolvedFormat, formatLocale); - formatter = Globalize.dateFormatter(format); + formatter = getGlobalizeDateFormatter(formatLocale, globalizeFormat); formattersCache[formatCacheKey] = formatter; } - } else { - if (!this._isAcceptableFormat(format)) { - return undefined; - } + } else if (isObject(resolvedFormat)) { + const typedFormat = resolvedFormat as FormatObject; + const typeFormat = typedFormat.type; + + if (typeFormat && typeof typeFormat === 'string') { + const formatLocale = getEffectiveFormatLocale( + typedFormat, + undefined, + typeFormat, + ); + const resolvedLocale = resolveGlobalizeLocale(formatLocale); + formatCacheKey = `${resolvedLocale}:${typeFormat}`; + formatter = formattersCache[formatCacheKey]; + if (!formatter) { + const globalizeFormat = this._getGlobalizeFormatterOptions(typeFormat, formatLocale); + + formatter = getGlobalizeDateFormatter(formatLocale, globalizeFormat); + formattersCache[formatCacheKey] = formatter; + } + } else { + const formatterOptions = getFormatterOptions(typedFormat) as FormatObject; - formatter = Globalize.dateFormatter(format); + if (!this._isAcceptableFormat(formatterOptions)) { + return undefined; + } + + const formatLocale = getEffectiveFormatLocale(typedFormat); + + formatter = getGlobalizeDateFormatter(formatLocale, formatterOptions); + } + } else { + return undefined; } // eslint-disable-next-line @typescript-eslint/no-unsafe-return diff --git a/packages/devextreme/js/__internal/core/localization/globalize/number.ts b/packages/devextreme/js/__internal/core/localization/globalize/number.ts index 3c4e99be24f1..9050add5f421 100644 --- a/packages/devextreme/js/__internal/core/localization/globalize/number.ts +++ b/packages/devextreme/js/__internal/core/localization/globalize/number.ts @@ -6,10 +6,16 @@ import type { FormatConfig, LocalizationFormat, NormalizedConfig, NumberFormatter, } from '@ts/core/localization/number'; import numberLocalization from '@ts/core/localization/number'; +import { + getEffectiveFormatLocale, + getFormatterOptions, + getGlobalFormatByDataType, +} from '@ts/core/m_global_format_config'; // eslint-disable-next-line import/no-extraneous-dependencies import Globalize from 'globalize'; const MAX_FRACTION_DIGITS = 20; +const NUMBER_DATA_TYPE = 'number'; if (Globalize?.formatNumber) { if (Globalize.locale().locale === 'en') { @@ -18,20 +24,23 @@ if (Globalize?.formatNumber) { const formattersCache: Record = {}; - const getFormatter = (format: string | NormalizedConfig | undefined): NumberFormatter => { + const getFormatter = ( + formatLocale: string, + format: string | NormalizedConfig | undefined, + ): NumberFormatter => { // eslint-disable-next-line @typescript-eslint/init-declarations let formatter: NumberFormatter; // eslint-disable-next-line @typescript-eslint/init-declarations let formatCacheKey: string; if (typeof format === 'object') { - formatCacheKey = `${Globalize.locale().locale}:${JSON.stringify(format)}`; + formatCacheKey = `${formatLocale}:${JSON.stringify(format)}`; } else { - formatCacheKey = `${Globalize.locale().locale}:${format}`; + formatCacheKey = `${formatLocale}:${format}`; } formatter = formattersCache[formatCacheKey]; if (!formatter) { - formatter = Globalize.numberFormatter(format); + formatter = Globalize(formatLocale).numberFormatter(format); formattersCache[formatCacheKey] = formatter; } @@ -49,8 +58,27 @@ if (Globalize?.formatNumber) { return this.callBase.apply(this, [value, format, formatConfig]); } - return getFormatter(this._normalizeFormatConfig(format, formatConfig, value))(value); + return getFormatter( + getEffectiveFormatLocale(formatConfig, NUMBER_DATA_TYPE), + this._normalizeFormatConfig(format, formatConfig, value), + )(value); }, + + getDecimalSeparator(): string { + const formatLocale = getEffectiveFormatLocale(undefined, NUMBER_DATA_TYPE); + + return getFormatter(formatLocale, { + minimumFractionDigits: 1, + maximumFractionDigits: 1, + })(1.2)[1]; + }, + + getThousandsSeparator(): string { + const formatLocale = getEffectiveFormatLocale(undefined, NUMBER_DATA_TYPE); + + return getFormatter(formatLocale, {})(10000)[2]; + }, + _normalizeFormatConfig( format: string, formatConfig: FormatConfig, @@ -105,13 +133,22 @@ if (Globalize?.formatNumber) { return value; } + const globalNumberFormat = getGlobalFormatByDataType(NUMBER_DATA_TYPE); + + if (!format && globalNumberFormat) { + // eslint-disable-next-line no-param-reassign + format = globalNumberFormat as LocalizationFormat; + } + // eslint-disable-next-line no-param-reassign format = this._normalizeFormat(format); // eslint-disable-next-line @stylistic/no-mixed-operators if (!format || typeof format !== 'function' && !(format as FormatConfig).type && !(format as FormatConfig).formatter) { - // @ts-expect-error - return getFormatter(format)(value); + const formatLocale = getEffectiveFormatLocale(format, NUMBER_DATA_TYPE); + const formatterOptions = getFormatterOptions(format); + + return getFormatter(formatLocale, formatterOptions)(value); } // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -137,7 +174,9 @@ if (Globalize?.formatNumber) { errors.log('W0011'); } - let result: number = Globalize.parseNumber(text); + let result: number = Globalize( + getEffectiveFormatLocale(format, NUMBER_DATA_TYPE), + ).parseNumber(text); if (isNaN(result)) { result = this.callBase.apply(this, [text, format]); diff --git a/packages/devextreme/js/__internal/core/localization/intl/date.ts b/packages/devextreme/js/__internal/core/localization/intl/date.ts index 537ea3ba7989..4d2dc3b5cb8d 100644 --- a/packages/devextreme/js/__internal/core/localization/intl/date.ts +++ b/packages/devextreme/js/__internal/core/localization/intl/date.ts @@ -2,7 +2,11 @@ import type { Format as LocalizationFormat, FormatObject } from '@js/localization'; import localizationCoreUtils from '@ts/core/localization/core'; import type { DateFormatter, Format } from '@ts/core/localization/date'; -import { resolvePresetOverride } from '@ts/core/m_global_format_config'; +import { + getEffectiveFormatLocale, + getFormatterOptions, + resolvePresetOverride, +} from '@ts/core/m_global_format_config'; import { extend } from '@ts/core/utils/m_extend'; interface DateArgs { @@ -19,29 +23,34 @@ const SYMBOLS_TO_REMOVE_REGEX = /[\u200E\u200F]/g; const NARROW_NO_BREAK_SPACE_REGEX = /[\u202F]/g; const formattersCache: Record = {}; -const getFormatter = (format: Intl.DateTimeFormatOptions): DateFormatter => { - const key = `${localizationCoreUtils.locale()}/${JSON.stringify(format)}`; +const getFormatter = ( + format: Intl.DateTimeFormatOptions, + formatLocale = localizationCoreUtils.locale(), +): DateFormatter => { + const key = `${formatLocale}/${JSON.stringify(format)}`; if (!formattersCache[key]) { - formattersCache[key] = new Intl.DateTimeFormat(localizationCoreUtils.locale(), format).format; + formattersCache[key] = new Intl.DateTimeFormat(formatLocale, format).format; } return formattersCache[key]; }; -function formatDateTime(date: Date, format: Intl.DateTimeFormatOptions): string { - return getFormatter(format)(date) +function formatDateTime( + date: Date, + format: Intl.DateTimeFormatOptions, + formatLocale = localizationCoreUtils.locale(), +): string { + return getFormatter(format, formatLocale)(date) .replace(SYMBOLS_TO_REMOVE_REGEX, '') .replace(NARROW_NO_BREAK_SPACE_REGEX, ' '); } -const getIntlFormatter = (format: Intl.DateTimeFormatOptions) => (date: Date): string => { - // NOTE: Intl in some browsers formates dates with timezone offset - // which was at the moment for this date. - // But the method "new Date" creates date using current offset. - // So, we decided to format dates in the UTC timezone. +const getIntlFormatter = ( + format: Intl.DateTimeFormatOptions, + formatLocale = localizationCoreUtils.locale(), +) => (date: Date): string => { if (!format.timeZoneName) { const year = date.getFullYear(); - // NOTE: new Date(99,0,1) will return 1999 year, but 99 expected const recognizableAsTwentyCentury = String(year).length < 3; const safeYearShift = 400; const temporaryYearValue = recognizableAsTwentyCentury ? year + safeYearShift : year; @@ -61,14 +70,15 @@ const getIntlFormatter = (format: Intl.DateTimeFormatOptions) => (date: Date): s } const utcFormat = extend({ timeZone: 'UTC' }, format); - return formatDateTime(utcDate, utcFormat); + return formatDateTime(utcDate, utcFormat, formatLocale); } - return formatDateTime(date, format); + return formatDateTime(date, format, formatLocale); }; -// eslint-disable-next-line @stylistic/max-len -const formatNumber = (number: number): string => new Intl.NumberFormat(localizationCoreUtils.locale()).format(number); +const formatNumber = (number: number): string => new Intl.NumberFormat( + localizationCoreUtils.locale(), +).format(number); const getAlternativeNumeralsMap = (() => { const numeralsMapCache: Record> = {}; @@ -109,7 +119,7 @@ const dateStringEquals = ( expected: string, ): boolean => removeLeadingZeroes(actual) === removeLeadingZeroes(expected); -const normalizeMonth = (text: string): string => text.replace('d\u2019', 'de '); // NOTE: For "ca" locale +const normalizeMonth = (text: string): string => text.replace('d\u2019', 'de '); const intlFormats = { day: { day: 'numeric' }, @@ -133,7 +143,8 @@ const intlFormats = { Object.defineProperty(intlFormats, 'shortdateshorttime', { get() { - const defaultOptions = Intl.DateTimeFormat(localizationCoreUtils.locale()).resolvedOptions(); + const formatLocale = getEffectiveFormatLocale(undefined, 'datetime'); + const defaultOptions = Intl.DateTimeFormat(formatLocale).resolvedOptions(); return { year: defaultOptions.year, month: defaultOptions.month, day: defaultOptions.day, hour: 'numeric', minute: 'numeric', @@ -144,21 +155,66 @@ Object.defineProperty(intlFormats, 'shortdateshorttime', { // eslint-disable-next-line @typescript-eslint/no-unsafe-return const getIntlFormat = (format): Intl.DateTimeFormatOptions => typeof format === 'string' && intlFormats[format.toLowerCase()]; +const formatWithIntlPreset = ( + date: Date, + sourceFormat: LocalizationFormat, + resolvedFormat: LocalizationFormat, + presetName: string, +): string | undefined => { + const intlFormat = getIntlFormat(presetName); + + if (!intlFormat) { + return undefined; + } + + const formatLocale = getEffectiveFormatLocale( + typeof sourceFormat === 'object' ? sourceFormat : undefined, + undefined, + presetName, + ); + + return getIntlFormatter(intlFormat, formatLocale)(date); +}; + +const formatWithIntlOptions = ( + date: Date, + format: FormatObject | Intl.DateTimeFormatOptions, +): string => { + const typeFormat = (format as FormatObject).type; + + if (typeFormat && typeof typeFormat === 'string') { + const intlPresetResult = formatWithIntlPreset(date, format, format, typeFormat); + + if (intlPresetResult !== undefined) { + return intlPresetResult; + } + } + + const formatLocale = getEffectiveFormatLocale(format, undefined, typeFormat); + const formatterOptions = getFormatterOptions(format) as Intl.DateTimeFormatOptions; + + return getIntlFormatter(formatterOptions, formatLocale)(date); +}; + const monthNameStrategies = { standalone(monthIndex: number, monthFormat: Intl.DateTimeFormatOptions['month']): string { const date = new Date(1999, monthIndex, 13, 1); + const messageLocale = localizationCoreUtils.locale(); - return getIntlFormatter({ month: monthFormat })(date); + return getIntlFormatter({ month: monthFormat }, messageLocale)(date); }, format(monthIndex: number, monthFormat: Intl.DateTimeFormatOptions['month']): string { const date = new Date(0, monthIndex, 13, 1); - const dateString = normalizeMonth(getIntlFormatter({ day: 'numeric', month: monthFormat })(date)); + const messageLocale = localizationCoreUtils.locale(); + const dateString = normalizeMonth( + getIntlFormatter({ day: 'numeric', month: monthFormat }, messageLocale)(date), + ); const parts = dateString.split(' ').filter((part) => !part.includes('13')); if (parts.length === 1) { return parts[0]; } if (parts.length === 2) { - return parts[0].length > parts[1].length ? parts[0] : parts[1]; // NOTE: For "lt" locale + return parts[0].length > parts[1].length ? parts[0] : parts[1]; } return monthNameStrategies.standalone(monthIndex, monthFormat); @@ -190,28 +246,36 @@ export default { }, getDayNames(format: Format): string[] { - // eslint-disable-next-line @typescript-eslint/no-shadow - const intlFormats: Record = { + const intlDayFormats: Record = { wide: 'long', abbreviated: 'short', short: 'narrow', narrow: 'narrow', }; - // eslint-disable-next-line @typescript-eslint/no-shadow - const getIntlDayNames = (format: Intl.DateTimeFormatOptions['weekday']): string[] => Array.from( + const messageLocale = localizationCoreUtils.locale(); + const getIntlDayNames = ( + dayFormat: Intl.DateTimeFormatOptions['weekday'], + ): string[] => Array.from( { length: 7 }, - (_, dayIndex) => getIntlFormatter({ weekday: format })(new Date(0, 0, dayIndex)), + (_, dayIndex) => getIntlFormatter( + { weekday: dayFormat }, + messageLocale, + )(new Date(0, 0, dayIndex)), ); - return getIntlDayNames(intlFormats[format || 'wide']); + return getIntlDayNames(intlDayFormats[format || 'wide']); }, getPeriodNames(): string[] { - const hour12Formatter = getIntlFormatter({ hour: 'numeric', hour12: true }); + const messageLocale = localizationCoreUtils.locale(); + const hour12Formatter = getIntlFormatter( + { hour: 'numeric', hour12: true }, + messageLocale, + ); return [1, 13].map((hours) => { - const hourNumberText = formatNumber(1); // NOTE: For "bn" locale + const hourNumberText = formatNumber(1); const timeParts = hour12Formatter(new Date(0, 0, 1, hours)).split(hourNumberText); if (timeParts.length !== 2) { @@ -233,38 +297,51 @@ export default { return date; } - // TODO: refactor (extract code form base) - if (typeof format !== 'function' && !(format as FormatObject).formatter) { - // eslint-disable-next-line no-param-reassign - format = (format as FormatObject).type ?? format; + if (typeof format === 'function') { + return (format as DateFormatter)(date); } - if (typeof format === 'string') { - const presetOverride = resolvePresetOverride(format); + if ((format as FormatObject).formatter) { + return ((format as FormatObject).formatter as DateFormatter)(date); + } + + const sourceFormat = format; + let resolvedFormat: LocalizationFormat = (format as FormatObject).type ?? format; + + if (typeof resolvedFormat === 'string') { + const presetOverride = resolvePresetOverride(resolvedFormat); if (presetOverride !== undefined) { if (typeof presetOverride === 'function') { return (presetOverride as DateFormatter)(date); } - // eslint-disable-next-line no-param-reassign - format = presetOverride as LocalizationFormat; + + resolvedFormat = presetOverride as LocalizationFormat; } } - const intlFormat = getIntlFormat(format); + if (typeof resolvedFormat === 'string') { + const intlPresetResult = formatWithIntlPreset( + date, + sourceFormat, + resolvedFormat, + resolvedFormat, + ); - if (intlFormat) { - return getIntlFormatter(intlFormat)(date); - } + if (intlPresetResult !== undefined) { + return intlPresetResult; + } - const formatType = typeof format; - if ((format as FormatObject).formatter || formatType === 'function' || formatType === 'string') { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return this.callBase.apply(this, [date, format]); + return this.callBase.apply(this, [date, resolvedFormat]); } - // @ts-expect-error - return getIntlFormatter(format)(date); + if (typeof resolvedFormat === 'object') { + return formatWithIntlOptions(date, resolvedFormat as FormatObject); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return this.callBase.apply(this, [date, resolvedFormat]); }, parse(dateString: string, format: FormatObject | string): Date | null | undefined { @@ -297,16 +374,15 @@ export default { const dateArgs: DateArgs = this._generateDateArgs(formatParts, dateParts); - // eslint-disable-next-line @typescript-eslint/no-shadow - const constructDate = (dateArgs: DateArgs, ampmShift: boolean): Date => { + const constructDate = (args: DateArgs, ampmShift: boolean): Date => { const hoursShift = ampmShift ? 12 : 0; return new Date( - dateArgs.year, - dateArgs.month, - dateArgs.day, - (dateArgs.hours + hoursShift) % 24, - dateArgs.minutes, - dateArgs.seconds, + args.year, + args.month, + args.day, + (args.hours + hoursShift) % 24, + args.minutes, + args.seconds, ); }; const constructValidDate = (ampmShift: boolean): Date | undefined => { @@ -366,13 +442,14 @@ export default { return this.callBase.apply(this, [format]); }, getTimeSeparator(): string { + const formatLocale = getEffectiveFormatLocale(undefined, 'time'); const formatOptions: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: 'numeric', hour12: false, }; return normalizeNumerals( - formatDateTime(new Date(2001, 1, 1, 11, 11), formatOptions), + formatDateTime(new Date(2001, 1, 1, 11, 11), formatOptions, formatLocale), ).replace(/\d/g, ''); }, @@ -383,8 +460,9 @@ export default { return this.callBase(format); } const intlFormat = extend({}, intlFormats[format.toLowerCase()]); + const messageLocale = localizationCoreUtils.locale(); const date = new Date(2001, 2, 4, 5, 6, 7); - let formattedDate = getIntlFormatter(intlFormat)(date); + let formattedDate = getIntlFormatter(intlFormat, messageLocale)(date); formattedDate = normalizeNumerals(formattedDate); diff --git a/packages/devextreme/js/__internal/core/localization/intl/number.ts b/packages/devextreme/js/__internal/core/localization/intl/number.ts index 7b103817ad00..75d25a03ce8b 100644 --- a/packages/devextreme/js/__internal/core/localization/intl/number.ts +++ b/packages/devextreme/js/__internal/core/localization/intl/number.ts @@ -3,7 +3,11 @@ import accountingFormats from '@ts/core/localization/cldr-data/accounting_format import localizationCoreUtils from '@ts/core/localization/core'; import type { FormatConfig as BaseFormatConfig, LocalizationFormat } from '@ts/core/localization/number'; import openXmlCurrencyFormat from '@ts/core/localization/open_xml_currency_format'; -import { getGlobalFormatByDataType } from '@ts/core/m_global_format_config'; +import { + getEffectiveFormatLocale, + getFormatterOptions, + getGlobalFormatByDataType, +} from '@ts/core/m_global_format_config'; interface CurrencySymbolInfo { position: 'before' | 'after'; @@ -19,20 +23,24 @@ type IntlFormatter = Intl.NumberFormat['format']; const CURRENCY_STYLES = ['standard', 'accounting']; const MAX_FRACTION_DIGITS = 20; +const NUMBER_DATA_TYPE = 'number'; const detectCurrencySymbolRegex = /([^\s0]+)?(\s*)0*[.,]*0*(\s*)([^\s0]+)?/; const formattersCache: Record = {}; -const getFormatter = (format: NormalizedConfig): IntlFormatter => { - const key = `${localizationCoreUtils.locale()}/${JSON.stringify(format)}`; +const getFormatter = (formatLocale: string, format: NormalizedConfig): IntlFormatter => { + const key = `${formatLocale}/${JSON.stringify(format)}`; if (!formattersCache[key]) { - formattersCache[key] = new Intl.NumberFormat(localizationCoreUtils.locale(), format).format; + formattersCache[key] = new Intl.NumberFormat(formatLocale, format).format; } return formattersCache[key]; }; -const getCurrencyFormatter = (currency: string): Intl.NumberFormat => new Intl.NumberFormat(localizationCoreUtils.locale(), { style: 'currency', currency }); +const getCurrencyFormatter = (currency: string): Intl.NumberFormat => new Intl.NumberFormat( + localizationCoreUtils.locale(), + { style: 'currency', currency }, +); export default { engine(): string { @@ -44,7 +52,23 @@ export default { return this.callBase.apply(this, [value, format, formatConfig]); } - return getFormatter(this._normalizeFormatConfig(format, formatConfig, value))(value); + const formatLocale = getEffectiveFormatLocale(formatConfig, NUMBER_DATA_TYPE); + const normalizedConfig = this._normalizeFormatConfig(format, formatConfig, value); + + return getFormatter(formatLocale, normalizedConfig)(value); + }, + getDecimalSeparator(): string { + const formatLocale = getEffectiveFormatLocale(undefined, NUMBER_DATA_TYPE); + + return getFormatter(formatLocale, { + minimumFractionDigits: 1, + maximumFractionDigits: 1, + })(1.2)[1]; + }, + getThousandsSeparator(): string { + const formatLocale = getEffectiveFormatLocale(undefined, NUMBER_DATA_TYPE); + + return getFormatter(formatLocale, {})(10000)[2]; }, _normalizeFormatConfig( format: string, @@ -107,7 +131,7 @@ export default { return value; } - const globalNumberFormat = getGlobalFormatByDataType('number'); + const globalNumberFormat = getGlobalFormatByDataType(NUMBER_DATA_TYPE); if (!format && globalNumberFormat) { // eslint-disable-next-line no-param-reassign @@ -123,7 +147,10 @@ export default { // eslint-disable-next-line @stylistic/no-mixed-operators if (!format || typeof format !== 'function' && !(format as FormatConfig).type && !(format as FormatConfig).formatter) { - return getFormatter(format)(value); + const formatLocale = getEffectiveFormatLocale(format, NUMBER_DATA_TYPE); + const formatterOptions = getFormatterOptions(format) as NormalizedConfig; + + return getFormatter(formatLocale, formatterOptions)(value); } // eslint-disable-next-line @typescript-eslint/no-unsafe-return diff --git a/packages/devextreme/js/__internal/core/m_global_format_config.js b/packages/devextreme/js/__internal/core/m_global_format_config.js index ea2dacafcd41..08010b36a0d8 100644 --- a/packages/devextreme/js/__internal/core/m_global_format_config.js +++ b/packages/devextreme/js/__internal/core/m_global_format_config.js @@ -6,10 +6,108 @@ import { isFunction, isPlainObject, isString } from '@js/core/utils/type'; const hasOwn = Object.prototype.hasOwnProperty; -export const getEffectiveFormatLocale = (format) => { +const GLOBAL_FORMAT_DATA_TYPES = ['time', 'datetime', 'date']; + +const DEFAULT_IMPLICIT_PRESET_BY_DATA_TYPE = { + date: 'shortdate', + datetime: 'shortdateshorttime', + time: 'shorttime', +}; + +const resolveFormatLocaleProperty = (formatObject) => { + const formatLocale = formatObject.locale; + + return isFunction(formatLocale) ? formatLocale() : formatLocale; +}; + +const getGlobalFormatLocale = (dataType) => { + const globalFormat = getGlobalFormatByDataType(dataType); + + if(isPlainObject(globalFormat) && globalFormat.locale) { + return resolveFormatLocaleProperty(globalFormat); + } + + return undefined; +}; + +const resolveDataTypeFromGlobalConfig = (presetName) => { + if(!presetName) { + return undefined; + } + + const lowerPreset = String(presetName).toLowerCase(); + + for(let i = 0; i < GLOBAL_FORMAT_DATA_TYPES.length; i++) { + const dataType = GLOBAL_FORMAT_DATA_TYPES[i]; + const globalFormat = getGlobalFormatByDataType(dataType); + + if(isPlainObject(globalFormat) && globalFormat.type?.toLowerCase() === lowerPreset) { + return dataType; + } + } + + for(let i = 0; i < GLOBAL_FORMAT_DATA_TYPES.length; i++) { + const dataType = GLOBAL_FORMAT_DATA_TYPES[i]; + + if(DEFAULT_IMPLICIT_PRESET_BY_DATA_TYPE[dataType] === lowerPreset) { + return dataType; + } + } + + return undefined; +}; + +const inferDataTypeFromFormatObject = (format) => { + if(!isPlainObject(format)) { + return undefined; + } + + const hasTime = format.hour !== undefined + || format.minute !== undefined + || format.second !== undefined; + const hasDate = format.year !== undefined + || format.month !== undefined + || format.day !== undefined + || format.weekday !== undefined; + + if(hasTime && hasDate) { + return 'datetime'; + } + + if(hasTime) { + return 'time'; + } + + if(hasDate) { + return 'date'; + } + + return undefined; +}; + +export const getEffectiveFormatLocale = (format, dataType, presetName) => { if(isPlainObject(format) && format.locale) { - const formatLocale = format.locale; - return isFunction(formatLocale) ? formatLocale() : formatLocale; + return resolveFormatLocaleProperty(format); + } + + let resolvedDataType = dataType; + + if(!resolvedDataType) { + const preset = presetName ?? (isPlainObject(format) ? format.type : undefined); + + resolvedDataType = resolveDataTypeFromGlobalConfig(preset); + } + + if(!resolvedDataType && isPlainObject(format)) { + resolvedDataType = inferDataTypeFromFormatObject(getFormatterOptions(format)); + } + + if(resolvedDataType) { + const globalFormatLocale = getGlobalFormatLocale(resolvedDataType); + + if(globalFormatLocale) { + return globalFormatLocale; + } } return coreLocalization.locale(); diff --git a/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js b/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js index 6657491d6633..c77c880174b1 100644 --- a/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.localization/localization.intl.tests.js @@ -1,5 +1,7 @@ import '../../helpers/noIntl.js'; import Intl from 'intl'; +import $ from 'jquery'; +import DateBox from 'ui/date_box'; import sharedTests from './sharedParts/localization.shared.js'; import dateLocalization from 'common/core/localization/date'; import numberLocalization from 'common/core/localization/number'; @@ -15,6 +17,7 @@ if(Intl.__disableRegExpRestore) { } const SYMBOLS_TO_REMOVE_REGEX = /[\u200E\u200F]/g; +const TEXTEDITOR_INPUT_CLASS = 'dx-texteditor-input'; const ROUNDING_BUG_NUMBERS = [4.645, -4.645, 35.855, -35.855]; const ROUNDING_CORRECTION = { '4.64': '4.65', @@ -1019,7 +1022,7 @@ QUnit.module('Global formatting config - format locale (spec, intl)', () => { locale: 'de-DE', type: 'shortDate', }), - '02.01.2020', + '2.1.2020', ); } finally { locale(savedLocale); @@ -1084,7 +1087,7 @@ QUnit.module('Global formatting config - format locale (spec, intl)', () => { } }); - QUnit.test('LDML numberFormat is unaffected by message locale', function(assert) { + QUnit.test('LDML numberFormat uses message locale separators', function(assert) { const saved = saveGlobalFormats(); const savedLocale = locale(); @@ -1095,7 +1098,7 @@ QUnit.module('Global formatting config - format locale (spec, intl)', () => { numberFormat: '#,##0.00', }); - assert.strictEqual(numberLocalization.format(1234.5), '1,234.50'); + assert.strictEqual(numberLocalization.format(1234.5), '1.234,50'); } finally { locale(savedLocale); restoreGlobalFormats(saved); @@ -1126,6 +1129,41 @@ QUnit.module('Global formatting config - format locale (spec, intl)', () => { restoreGlobalFormats(saved); } }); + + QUnit.test('DateBox implicit displayFormat uses global dateFormat locale', function(assert) { + const saved = saveGlobalFormats(); + const savedLocale = locale(); + const $element = $('
').appendTo('#qunit-fixture'); + + try { + locale('en'); + config({ + ...config(), + dateFormat: { + default: { + locale: 'de-DE', + type: 'shortDate', + }, + }, + }); + + $element.dxDateBox({ + type: 'date', + value: new Date(2020, 0, 2), + pickerType: 'calendar', + }); + + assert.strictEqual( + $element.find(`.${TEXTEDITOR_INPUT_CLASS}`).val(), + '2.1.2020', + 'date input uses de-DE shortDate pattern', + ); + } finally { + $element.remove(); + locale(savedLocale); + restoreGlobalFormats(saved); + } + }); }); QUnit.module('Exceljs format', () => { From 3f94a8120cce7641668ab5fdb445c7f5329cde50 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 4 Jul 2026 01:08:51 +0200 Subject: [PATCH 4/9] fix tests --- .../numberbox.localization.tests.js | 127 +++++++++++------- 1 file changed, 81 insertions(+), 46 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js index 82eb7c186cd3..d84dfc86f2fc 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberbox.localization.tests.js @@ -10,6 +10,60 @@ import 'ui/text_box/ui.text_editor'; const TEXTEDITOR_INPUT_CLASS = '.dx-texteditor-input'; +const cloneNumberFormat = (value) => { + if(value === undefined) { + return undefined; + } + + if(typeof value === 'string') { + return value; + } + + return JSON.parse(JSON.stringify(value)); +}; + +const restoreGlobalNumberFormat = (savedNumberFormat) => { + const currentConfig = config(); + + if(savedNumberFormat === undefined) { + delete currentConfig.numberFormat; + } else { + currentConfig.numberFormat = cloneNumberFormat(savedNumberFormat); + } +}; + +const saveNumberBoxCustomRules = () => ( + NumberBox._classCustomRules ? [...NumberBox._classCustomRules] : [] +); + +const restoreNumberBoxCustomRules = (savedRules) => { + NumberBox._classCustomRules = savedRules ? [...savedRules] : []; +}; + +const saveLocalizationState = () => ({ + locale: localization.locale(), + numberFormat: cloneNumberFormat(config().numberFormat), + numberBoxCustomRules: saveNumberBoxCustomRules(), +}); + +const restoreLocalizationState = (savedState) => { + localization.locale(savedState.locale); + restoreGlobalNumberFormat(savedState.numberFormat); + restoreNumberBoxCustomRules(savedState.numberBoxCustomRules); +}; + +const localizationModuleHooks = { + beforeEach: function() { + this.savedLocalizationState = saveLocalizationState(); + delete config().numberFormat; + localization.locale('en'); + }, + + afterEach: function() { + restoreLocalizationState(this.savedLocalizationState); + }, +}; + QUnit.testStart(function() { const markup = '
\ @@ -21,22 +75,30 @@ QUnit.testStart(function() { const moduleConfig = { beforeEach: function() { + localizationModuleHooks.beforeEach.call(this); + this.$element = $('#numberbox').dxNumberBox({ format: '#0.##', value: '', - useMaskBehavior: true + useMaskBehavior: true, }); this.input = this.$element.find(TEXTEDITOR_INPUT_CLASS); this.instance = this.$element.dxNumberBox('instance'); this.keyboard = keyboardMock(this.input, true); - } + }, + + afterEach: function() { + localizationModuleHooks.afterEach.call(this); + }, }; QUnit.module('localization: separator keys', moduleConfig, () => { QUnit.test('pressing "." should clear selected text if it contains a decimal separator (T1199553)', function(assert) { + localization.locale('en'); + this.instance.option({ format: '0#.00', - value: 123.45 + value: 123.45, }); this.keyboard @@ -47,40 +109,24 @@ QUnit.module('localization: separator keys', moduleConfig, () => { }); QUnit.test('pressing the "." key on the numpad should clear the selected text regardless of the char produced (T1199553)', function(assert) { - const currentLocale = localization.locale(); const NUMPAD_DOT_KEY_CODE = 110; - try { - localization.locale('fr-ca'); - this.instance.option({ - format: '0#.00', - value: 123.45 - }); - - this.keyboard - .caret({ start: 0, end: 6 }) - .type('.', { which: NUMPAD_DOT_KEY_CODE }); - - assert.strictEqual(this.input.val(), '0,00', 'mask value is cleared'); - } finally { - localization.locale(currentLocale); - } + localization.locale('fr-ca'); + + this.instance.option({ + format: '0#.00', + value: 123.45, + }); + + this.keyboard + .caret({ start: 0, end: 6 }) + .type('.', { which: NUMPAD_DOT_KEY_CODE }); + + assert.strictEqual(this.input.val(), '0,00', 'mask value is cleared'); }); }); -QUnit.module('localization: global number format', { - beforeEach: function() { - this.savedConfig = { ...config() }; - this.savedLocale = localization.locale(); - localization.locale('en'); - }, - - afterEach: function() { - localization.locale(this.savedLocale); - config(this.savedConfig); - NumberBox.defaultOptions([]); - }, -}, () => { +QUnit.module('localization: global number format', localizationModuleHooks, () => { QUnit.test('uses global numberFormat when local format is not set', function(assert) { config({ ...config(), @@ -139,19 +185,7 @@ QUnit.module('localization: global number format', { }); }); -QUnit.module('localization: global number format locale', { - beforeEach: function() { - this.savedConfig = { ...config() }; - this.savedLocale = localization.locale(); - localization.locale('en'); - }, - - afterEach: function() { - localization.locale(this.savedLocale); - config(this.savedConfig); - NumberBox.defaultOptions([]); - }, -}, () => { +QUnit.module('localization: global number format locale', localizationModuleHooks, () => { QUnit.test('uses format locale from global numberFormat map with de message locale', function(assert) { localization.locale('de'); config({ @@ -197,7 +231,8 @@ QUnit.module('localization: global number format locale', { keyboard .caret({ start: 0, end: 0 }) - .type('1234.56'); + .type('1234.56') + .change(); assert.strictEqual(instance.option('value'), 1234.56, 'parses en-US decimal separator'); }); From d51145ef9474b2c746c51e4e43f5bc8673b0f198 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 4 Jul 2026 01:10:55 +0200 Subject: [PATCH 5/9] use global config numberFormat.locale for mask --- .../ui/number_box/m_number_box.mask.ts | 92 +++++++++++++++++-- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts b/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts index 5c3e2c0909a8..9d0e42f0480e 100644 --- a/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts @@ -7,12 +7,13 @@ import { getFormat as getLDMLFormat } from '@js/common/core/localization/ldml/nu import number from '@js/common/core/localization/number'; import devices from '@js/core/devices'; import { ensureDefined, escapeRegExp } from '@js/core/utils/common'; +import { equals } from '@js/core/utils/comparator'; import { fitIntoRange, inRange } from '@js/core/utils/math'; import { - isDefined, isFunction, isNumeric, isString, + isDefined, isFunction, isNumeric, isPlainObject, isString, } from '@js/core/utils/type'; import type { Properties } from '@js/ui/number_box'; -import { getGlobalFormatByDataType } from '@ts/core/m_global_format_config'; +import { getEffectiveFormatLocale, getGlobalFormatByDataType } from '@ts/core/m_global_format_config'; import NumberBoxBase from './m_number_box.base'; import { @@ -62,6 +63,8 @@ class NumberBoxMask extends NumberBoxBase { _currentFormat?: any; + _intlFormatLocaleCache?: string; + _getDefaultOptions(): NumberBoxMaskProperties { return { ...super._getDefaultOptions(), @@ -102,6 +105,60 @@ class NumberBoxMask extends NumberBoxBase { : getGlobalFormatByDataType('number'); } + _usesIntlFormatOption(formatOption) { + const customFormatter = formatOption?.formatter || formatOption; + + return !isFunction(customFormatter) + && isPlainObject(formatOption) + && !formatOption.type + && !formatOption.parser; + } + + _getFormatArgumentForNumberLocalization(maskFormat) { + const formatOption = this._getEffectiveFormatOption(); + + return this._usesIntlFormatOption(formatOption) ? formatOption : maskFormat; + } + + _invalidateFormatPatternIfIntlLocaleChanged(): void { + const formatOption = this._getEffectiveFormatOption(); + + if (!this._usesIntlFormatOption(formatOption)) { + return; + } + + const locale = getEffectiveFormatLocale(formatOption, 'number'); + + if (locale !== this._intlFormatLocaleCache) { + this._intlFormatLocaleCache = locale; + delete this._currentFormat; + } + } + + _refreshFormattedValueFromParsedValue(value: NumberBoxMaskProperties['value']): void { + this._parsedValue = value; + delete this._currentFormat; + delete this._intlFormatLocaleCache; + this._setTextByParsedValue(); + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + option(...args): NumberBoxMaskProperties { + if (args.length === 2 && args[0] === 'value' && this._useMaskBehavior()) { + const formatOption = this._getEffectiveFormatOption(); + const previousValue = super.option('value'); + const result = super.option(...args); + + if (this._usesIntlFormatOption(formatOption) && equals(previousValue, args[1])) { + this._refreshFormattedValueFromParsedValue(args[1] as NumberBoxMaskProperties['value']); + } + + return result; + } + + return super.option(...args); + } + _getTextSeparatorIndex(text) { const decimalSeparator = number.getDecimalSeparator(); const formatPattern = this._getFormatPattern(); @@ -368,38 +425,43 @@ class NumberBoxMask extends NumberBoxBase { _parse(text, format) { const formatOption = this._getEffectiveFormatOption(); - const isCustomParser = isFunction(formatOption.parser); + const isCustomParser = isFunction(formatOption?.parser); const parser = isCustomParser ? formatOption.parser : number.parse; + const formatArg = this._getFormatArgumentForNumberLocalization(format); + const maskPattern = isString(format) ? format : ''; let integerPartStartIndex = 0; if (!isCustomParser) { - const formatPointIndex = getRealSeparatorIndex(format).index; + const formatPointIndex = getRealSeparatorIndex(maskPattern).index; const textPointIndex = this._getTextSeparatorIndex(text); - const formatIntegerPartLength = formatPointIndex !== -1 ? formatPointIndex : format.length; + const formatIntegerPartLength = formatPointIndex !== -1 ? formatPointIndex : maskPattern.length; const textIntegerPartLength = textPointIndex !== -1 ? textPointIndex : text.length; - if (textIntegerPartLength > formatIntegerPartLength && format.indexOf('#') === -1) { + if (textIntegerPartLength > formatIntegerPartLength && !maskPattern.includes('#')) { integerPartStartIndex = textIntegerPartLength - formatIntegerPartLength; } } text = text.substr(integerPartStartIndex); - return parser(text, format); + return parser(text, formatArg); } _format(value, format) { + const formatArg = this._getFormatArgumentForNumberLocalization(format); const formatOption = this._getEffectiveFormatOption(); const customFormatter = formatOption?.formatter || formatOption; const formatter = isFunction(customFormatter) ? customFormatter : number.format; - const formattedValue = value === null ? '' : formatter(value, format); + const formattedValue = value === null ? '' : formatter(value, formatArg); return formattedValue; } _getFormatPattern() { + this._invalidateFormatPatternIfIntlLocaleChanged(); + if (!this._currentFormat) { this._updateFormat(); } @@ -533,7 +595,8 @@ class NumberBoxMask extends NumberBoxBase { } _getParsedValue(text, format) { - const sign = number.getSign(text, format?.formatter || format); + const formatArg = this._getFormatArgumentForNumberLocalization(format); + const sign = number.getSign(text, formatArg?.formatter || formatArg); const textWithoutStubs = this._removeStubs(text, true); const parsedValue = this._parse(textWithoutStubs, format); const parsedValueSign = parsedValue < 0 ? -1 : 1; @@ -716,6 +779,15 @@ class NumberBoxMask extends NumberBoxBase { } _getPrecisionLimits(text): { min: number; max: number } { + const formatOption = this._getEffectiveFormatOption(); + + if (this._usesIntlFormatOption(formatOption)) { + const min = formatOption.minimumFractionDigits ?? 0; + const max = formatOption.maximumFractionDigits ?? min; + + return { min, max }; + } + const currentFormat = this._getFormatForSign(text); const realSeparatorIndex = getRealSeparatorIndex(currentFormat).index; const floatPart = (splitByIndex(currentFormat, realSeparatorIndex)[1] || '').replace(/[^#0]/g, ''); @@ -902,6 +974,8 @@ class NumberBoxMask extends NumberBoxBase { delete this._lastKeyName; delete this._parsedValue; delete this._focusOutOccurs; + delete this._currentFormat; + delete this._intlFormatLocaleCache; clearTimeout(this._caretTimeout); delete this._caretTimeout; } From 44d3ef4c50ab2ecd4a0d4b57bf69a9612cfc57e0 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 4 Jul 2026 10:13:09 +0200 Subject: [PATCH 6/9] fix TS types --- .../core/localization/format.locale.test.ts | 4 ++-- .../js/common/core/localization.d.ts | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/core/localization/format.locale.test.ts b/packages/devextreme/js/__internal/core/localization/format.locale.test.ts index 52bae45bce9a..90d0d9c0e875 100644 --- a/packages/devextreme/js/__internal/core/localization/format.locale.test.ts +++ b/packages/devextreme/js/__internal/core/localization/format.locale.test.ts @@ -179,7 +179,7 @@ describe('format locale integration', () => { numberLocalization.format(1.234); numberFormatSpy.mock.calls.forEach(([, options]) => { - expect(options?.locale).toBeUndefined(); + expect(options).not.toHaveProperty('locale'); }); numberFormatSpy.mockRestore(); @@ -246,7 +246,7 @@ describe('format locale integration', () => { }); dateTimeFormatSpy.mock.calls.forEach(([, options]) => { - expect(options?.locale).toBeUndefined(); + expect(options).not.toHaveProperty('locale'); }); dateTimeFormatSpy.mockRestore(); diff --git a/packages/devextreme/js/common/core/localization.d.ts b/packages/devextreme/js/common/core/localization.d.ts index 5583ec5f0d94..3e8f8ef22468 100644 --- a/packages/devextreme/js/common/core/localization.d.ts +++ b/packages/devextreme/js/common/core/localization.d.ts @@ -75,8 +75,19 @@ export function parseDate(text: string, format: Format): Date; */ export function parseNumber(text: string, format: Format): number; -type ExternalFormat = Intl.DateTimeFormatOptions - | Intl.NumberFormatOptions; +/** + * @docid + * @public + */ +export type FormatLocale = string | (() => string); + +type ExternalFormat = (Intl.DateTimeFormatOptions | Intl.NumberFormatOptions) & { + /** + * @docid Format.locale + * @public + */ + locale?: FormatLocale; +}; type PredefinedFormat = FormatType; @@ -130,4 +141,9 @@ export interface FormatObject { * @type Enums.Format|string */ type?: PredefinedFormat | string; + /** + * @docid Format.locale + * @public + */ + locale?: FormatLocale; } From e370a6c2a67d1cfb929c993253f0047d26e61267 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 4 Jul 2026 10:40:23 +0200 Subject: [PATCH 7/9] fix scheduler test --- .../integration.appointmentTooltip.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js index 1178a5619bfd..b3afe984208a 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js @@ -176,7 +176,7 @@ module('Global formatting config (spec): Scheduler tooltip', { await scheduler.appointments.click(0, clock); clock.restore(); - assert.strictEqual(scheduler.tooltip.getDateText(), '09.02.2015 23:00 - 10.02.2015 01:00'); + assert.strictEqual(scheduler.tooltip.getDateText(), '9.2.2015 23:00 - 10.2.2015 01:00'); } finally { locale(savedLocale); } From c226255963fc4d2a88fe11a6ab8b31a216c89ba2 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 4 Jul 2026 17:36:17 +0200 Subject: [PATCH 8/9] regenerate dx.all.d.ts --- packages/devextreme/ts/dx.all.d.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/ts/dx.all.d.ts b/packages/devextreme/ts/dx.all.d.ts index e5da1ca7358d..f66e8e548c4d 100644 --- a/packages/devextreme/ts/dx.all.d.ts +++ b/packages/devextreme/ts/dx.all.d.ts @@ -1332,7 +1332,15 @@ declare module DevExpress.common { /** * @deprecated Attention! This type is for internal purposes only. If you used it previously, please submit a ticket to our {@link https://supportcenter.devexpress.com/ticket/create Support Center}. We will check if there is an alternative solution. */ - type ExternalFormat = Intl.DateTimeFormatOptions | Intl.NumberFormatOptions; + type ExternalFormat = ( + | Intl.DateTimeFormatOptions + | Intl.NumberFormatOptions + ) & { + /** + * [descr:Format.locale] + */ + locale?: FormatLocale; + }; export type FieldChooserLayout = 0 | 1 | 2; /** @@ -1368,6 +1376,10 @@ declare module DevExpress.common { | 'minute' | 'second' | 'shortDateShortTime'; + /** + * [descr:FormatLocale] + */ + export type FormatLocale = string | (() => string); /** * @deprecated Attention! This type is for internal purposes only. If you used it previously, please submit a ticket to our {@link https://supportcenter.devexpress.com/ticket/create Support Center}. We will check if there is an alternative solution. */ @@ -1396,6 +1408,10 @@ declare module DevExpress.common { * [descr:Format.type] */ type?: PredefinedFormat | string; + /** + * [descr:Format.locale] + */ + locale?: FormatLocale; } /** * [descr:GlobalConfig] From 9bf8d9fcba013bf451c99e73b6ece5daf8bcbc6f Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sun, 5 Jul 2026 11:12:45 +0200 Subject: [PATCH 9/9] regenerate wrappers --- .../src/common/core/localization/index.ts | 1 + packages/devextreme-angular/src/common/index.ts | 1 + packages/devextreme-angular/src/index.ts | 1 + .../src/ui/bar-gauge/nested/format.ts | 9 +++++++++ .../src/ui/bar-gauge/nested/item-text-format.ts | 9 +++++++++ .../devextreme-angular/src/ui/bullet/nested/format.ts | 9 +++++++++ .../src/ui/card-view/nested/format.ts | 9 +++++++++ .../src/ui/chart/nested/argument-format.ts | 9 +++++++++ .../devextreme-angular/src/ui/chart/nested/format.ts | 9 +++++++++ .../src/ui/chat/nested/day-header-format.ts | 9 +++++++++ .../src/ui/chat/nested/message-timestamp-format.ts | 9 +++++++++ .../src/ui/circular-gauge/nested/format.ts | 9 +++++++++ .../src/ui/data-grid/nested/format.ts | 9 +++++++++ .../src/ui/data-grid/nested/value-format.ts | 9 +++++++++ .../src/ui/date-box/nested/display-format.ts | 9 +++++++++ .../src/ui/date-range-box/nested/display-format.ts | 9 +++++++++ .../src/ui/filter-builder/nested/format.ts | 9 +++++++++ .../devextreme-angular/src/ui/funnel/nested/format.ts | 9 +++++++++ .../devextreme-angular/src/ui/gantt/nested/format.ts | 9 +++++++++ .../src/ui/linear-gauge/nested/format.ts | 9 +++++++++ .../src/ui/nested/argument-format.ts | 1 + .../devextreme-angular/src/ui/nested/base/format.ts | 8 ++++++++ .../src/ui/nested/day-header-format.ts | 1 + .../src/ui/nested/display-format.ts | 1 + packages/devextreme-angular/src/ui/nested/format.ts | 1 + .../src/ui/nested/item-text-format.ts | 1 + .../src/ui/nested/message-timestamp-format.ts | 1 + .../devextreme-angular/src/ui/nested/value-format.ts | 1 + .../src/ui/number-box/nested/format.ts | 9 +++++++++ .../src/ui/pie-chart/nested/argument-format.ts | 9 +++++++++ .../src/ui/pie-chart/nested/format.ts | 9 +++++++++ .../src/ui/polar-chart/nested/argument-format.ts | 9 +++++++++ .../src/ui/polar-chart/nested/format.ts | 9 +++++++++ .../src/ui/range-selector/nested/argument-format.ts | 9 +++++++++ .../src/ui/range-selector/nested/format.ts | 9 +++++++++ .../src/ui/range-slider/nested/format.ts | 9 +++++++++ .../devextreme-angular/src/ui/sankey/nested/format.ts | 9 +++++++++ .../devextreme-angular/src/ui/slider/nested/format.ts | 9 +++++++++ .../src/ui/sparkline/nested/format.ts | 9 +++++++++ .../src/ui/tree-list/nested/format.ts | 9 +++++++++ .../src/ui/tree-map/nested/format.ts | 9 +++++++++ packages/devextreme-react/src/bar-gauge.ts | 4 +++- packages/devextreme-react/src/bullet.ts | 3 ++- packages/devextreme-react/src/card-view.ts | 3 ++- packages/devextreme-react/src/chart.ts | 4 +++- packages/devextreme-react/src/chat.ts | 3 +++ packages/devextreme-react/src/circular-gauge.ts | 3 ++- .../devextreme-react/src/common/core/localization.ts | 1 + packages/devextreme-react/src/common/index.ts | 1 + packages/devextreme-react/src/data-grid.ts | 4 +++- packages/devextreme-react/src/date-box.ts | 2 ++ packages/devextreme-react/src/date-range-box.ts | 2 ++ packages/devextreme-react/src/filter-builder.ts | 3 ++- packages/devextreme-react/src/funnel.ts | 3 ++- packages/devextreme-react/src/gantt.ts | 3 ++- packages/devextreme-react/src/linear-gauge.ts | 3 ++- packages/devextreme-react/src/number-box.ts | 2 ++ packages/devextreme-react/src/pie-chart.ts | 4 +++- packages/devextreme-react/src/polar-chart.ts | 4 +++- packages/devextreme-react/src/range-selector.ts | 4 +++- packages/devextreme-react/src/range-slider.ts | 3 ++- packages/devextreme-react/src/sankey.ts | 3 ++- packages/devextreme-react/src/slider.ts | 3 ++- packages/devextreme-react/src/sparkline.ts | 3 ++- packages/devextreme-react/src/tree-list.ts | 3 ++- packages/devextreme-react/src/tree-map.ts | 3 ++- packages/devextreme-vue/src/bar-gauge.ts | 5 +++++ packages/devextreme-vue/src/bullet.ts | 9 ++++++--- packages/devextreme-vue/src/card-view.ts | 3 +++ packages/devextreme-vue/src/chart.ts | 5 +++++ packages/devextreme-vue/src/chat.ts | 5 +++++ packages/devextreme-vue/src/circular-gauge.ts | 3 +++ .../devextreme-vue/src/common/core/localization.ts | 1 + packages/devextreme-vue/src/common/index.ts | 1 + packages/devextreme-vue/src/data-grid.ts | 5 +++++ packages/devextreme-vue/src/date-box.ts | 3 +++ packages/devextreme-vue/src/date-range-box.ts | 3 +++ packages/devextreme-vue/src/filter-builder.ts | 3 +++ packages/devextreme-vue/src/funnel.ts | 3 +++ packages/devextreme-vue/src/gantt.ts | 3 +++ packages/devextreme-vue/src/linear-gauge.ts | 3 +++ packages/devextreme-vue/src/number-box.ts | 3 +++ packages/devextreme-vue/src/pie-chart.ts | 5 +++++ packages/devextreme-vue/src/polar-chart.ts | 5 +++++ packages/devextreme-vue/src/range-selector.ts | 11 ++++++++--- packages/devextreme-vue/src/range-slider.ts | 3 +++ packages/devextreme-vue/src/sankey.ts | 3 +++ packages/devextreme-vue/src/slider.ts | 3 +++ packages/devextreme-vue/src/sparkline.ts | 9 ++++++--- packages/devextreme-vue/src/tree-list.ts | 3 +++ packages/devextreme-vue/src/tree-map.ts | 3 +++ 91 files changed, 437 insertions(+), 28 deletions(-) diff --git a/packages/devextreme-angular/src/common/core/localization/index.ts b/packages/devextreme-angular/src/common/core/localization/index.ts index 6e576bc9892d..21c8dc650fff 100644 --- a/packages/devextreme-angular/src/common/core/localization/index.ts +++ b/packages/devextreme-angular/src/common/core/localization/index.ts @@ -9,4 +9,5 @@ export { } from 'devextreme/common/core/localization'; export type { Format, + FormatLocale, } from 'devextreme/common/core/localization'; diff --git a/packages/devextreme-angular/src/common/index.ts b/packages/devextreme-angular/src/common/index.ts index 36975966fe63..ac60deae8eb9 100644 --- a/packages/devextreme-angular/src/common/index.ts +++ b/packages/devextreme-angular/src/common/index.ts @@ -189,6 +189,7 @@ export namespace Core { export namespace Localization { export type Format = CoreLocalizationModule.Format; export const formatDate = CoreLocalizationModule.formatDate; + export type FormatLocale = CoreLocalizationModule.FormatLocale; export const formatMessage = CoreLocalizationModule.formatMessage; export const formatNumber = CoreLocalizationModule.formatNumber; export const loadMessages = CoreLocalizationModule.loadMessages; diff --git a/packages/devextreme-angular/src/index.ts b/packages/devextreme-angular/src/index.ts index 3624a2e345a9..5a51cf8fac4b 100644 --- a/packages/devextreme-angular/src/index.ts +++ b/packages/devextreme-angular/src/index.ts @@ -272,6 +272,7 @@ export namespace Common { export namespace Localization { export type Format = import('devextreme/common/core/localization').Format; export const formatDate = (CoreLocalizationModule as any).formatDate as typeof import('devextreme/common/core/localization').formatDate; + export type FormatLocale = import('devextreme/common/core/localization').FormatLocale; export const formatMessage = (CoreLocalizationModule as any).formatMessage as typeof import('devextreme/common/core/localization').formatMessage; export const formatNumber = (CoreLocalizationModule as any).formatNumber as typeof import('devextreme/common/core/localization').formatNumber; export const loadMessages = (CoreLocalizationModule as any).loadMessages as typeof import('devextreme/common/core/localization').loadMessages; diff --git a/packages/devextreme-angular/src/ui/bar-gauge/nested/format.ts b/packages/devextreme-angular/src/ui/bar-gauge/nested/format.ts index a0873910e00a..0ed93b1884ff 100644 --- a/packages/devextreme-angular/src/ui/bar-gauge/nested/format.ts +++ b/packages/devextreme-angular/src/ui/bar-gauge/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoBarGaugeFormatComponent extends NestedOption implements OnDestro this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/bar-gauge/nested/item-text-format.ts b/packages/devextreme-angular/src/ui/bar-gauge/nested/item-text-format.ts index f72e28f11bab..a754a57b00e5 100644 --- a/packages/devextreme-angular/src/ui/bar-gauge/nested/item-text-format.ts +++ b/packages/devextreme-angular/src/ui/bar-gauge/nested/item-text-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoBarGaugeItemTextFormatComponent extends NestedOption implements this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/bullet/nested/format.ts b/packages/devextreme-angular/src/ui/bullet/nested/format.ts index a6d518255693..e2997ddef47e 100644 --- a/packages/devextreme-angular/src/ui/bullet/nested/format.ts +++ b/packages/devextreme-angular/src/ui/bullet/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoBulletFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/card-view/nested/format.ts b/packages/devextreme-angular/src/ui/card-view/nested/format.ts index b12a57049885..0e5452ac874e 100644 --- a/packages/devextreme-angular/src/ui/card-view/nested/format.ts +++ b/packages/devextreme-angular/src/ui/card-view/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoCardViewFormatComponent extends NestedOption implements OnDestro this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/chart/nested/argument-format.ts b/packages/devextreme-angular/src/ui/chart/nested/argument-format.ts index 74a7cd4ea860..6813d686fe4d 100644 --- a/packages/devextreme-angular/src/ui/chart/nested/argument-format.ts +++ b/packages/devextreme-angular/src/ui/chart/nested/argument-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoChartArgumentFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/chart/nested/format.ts b/packages/devextreme-angular/src/ui/chart/nested/format.ts index 71c37a9ddf0a..b74a7f1e5f38 100644 --- a/packages/devextreme-angular/src/ui/chart/nested/format.ts +++ b/packages/devextreme-angular/src/ui/chart/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoChartFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/chat/nested/day-header-format.ts b/packages/devextreme-angular/src/ui/chat/nested/day-header-format.ts index c253feba1e34..3d569f705594 100644 --- a/packages/devextreme-angular/src/ui/chat/nested/day-header-format.ts +++ b/packages/devextreme-angular/src/ui/chat/nested/day-header-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoChatDayHeaderFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/chat/nested/message-timestamp-format.ts b/packages/devextreme-angular/src/ui/chat/nested/message-timestamp-format.ts index 76d7b205a834..90459753d04a 100644 --- a/packages/devextreme-angular/src/ui/chat/nested/message-timestamp-format.ts +++ b/packages/devextreme-angular/src/ui/chat/nested/message-timestamp-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoChatMessageTimestampFormatComponent extends NestedOption impleme this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/circular-gauge/nested/format.ts b/packages/devextreme-angular/src/ui/circular-gauge/nested/format.ts index 0f6cc0413e4e..1f15bc352882 100644 --- a/packages/devextreme-angular/src/ui/circular-gauge/nested/format.ts +++ b/packages/devextreme-angular/src/ui/circular-gauge/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoCircularGaugeFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/data-grid/nested/format.ts b/packages/devextreme-angular/src/ui/data-grid/nested/format.ts index d172d0c915b3..89f63e929e39 100644 --- a/packages/devextreme-angular/src/ui/data-grid/nested/format.ts +++ b/packages/devextreme-angular/src/ui/data-grid/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoDataGridFormatComponent extends NestedOption implements OnDestro this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/data-grid/nested/value-format.ts b/packages/devextreme-angular/src/ui/data-grid/nested/value-format.ts index d15ab4df4e2a..cea539383fb3 100644 --- a/packages/devextreme-angular/src/ui/data-grid/nested/value-format.ts +++ b/packages/devextreme-angular/src/ui/data-grid/nested/value-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoDataGridValueFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/date-box/nested/display-format.ts b/packages/devextreme-angular/src/ui/date-box/nested/display-format.ts index ee08e126c220..f2cf1a5677c4 100644 --- a/packages/devextreme-angular/src/ui/date-box/nested/display-format.ts +++ b/packages/devextreme-angular/src/ui/date-box/nested/display-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoDateBoxDisplayFormatComponent extends NestedOption implements On this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/date-range-box/nested/display-format.ts b/packages/devextreme-angular/src/ui/date-range-box/nested/display-format.ts index 6679f9f92a71..7cd29af216ea 100644 --- a/packages/devextreme-angular/src/ui/date-range-box/nested/display-format.ts +++ b/packages/devextreme-angular/src/ui/date-range-box/nested/display-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoDateRangeBoxDisplayFormatComponent extends NestedOption implemen this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/filter-builder/nested/format.ts b/packages/devextreme-angular/src/ui/filter-builder/nested/format.ts index bd031b7d9db1..373f206565d8 100644 --- a/packages/devextreme-angular/src/ui/filter-builder/nested/format.ts +++ b/packages/devextreme-angular/src/ui/filter-builder/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoFilterBuilderFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/funnel/nested/format.ts b/packages/devextreme-angular/src/ui/funnel/nested/format.ts index 7873b9f1d97e..824e521661e6 100644 --- a/packages/devextreme-angular/src/ui/funnel/nested/format.ts +++ b/packages/devextreme-angular/src/ui/funnel/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoFunnelFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/gantt/nested/format.ts b/packages/devextreme-angular/src/ui/gantt/nested/format.ts index 2c8c2678bd50..5ce9611ee846 100644 --- a/packages/devextreme-angular/src/ui/gantt/nested/format.ts +++ b/packages/devextreme-angular/src/ui/gantt/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoGanttFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/linear-gauge/nested/format.ts b/packages/devextreme-angular/src/ui/linear-gauge/nested/format.ts index 2144266f45db..793a6b6268fa 100644 --- a/packages/devextreme-angular/src/ui/linear-gauge/nested/format.ts +++ b/packages/devextreme-angular/src/ui/linear-gauge/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoLinearGaugeFormatComponent extends NestedOption implements OnDes this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/nested/argument-format.ts b/packages/devextreme-angular/src/ui/nested/argument-format.ts index a2179801d056..e03cf1b303fe 100644 --- a/packages/devextreme-angular/src/ui/nested/argument-format.ts +++ b/packages/devextreme-angular/src/ui/nested/argument-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/base/format.ts b/packages/devextreme-angular/src/ui/nested/base/format.ts index aa9d579890f4..d891970cda22 100644 --- a/packages/devextreme-angular/src/ui/nested/base/format.ts +++ b/packages/devextreme-angular/src/ui/nested/base/format.ts @@ -6,6 +6,7 @@ import { } from '@angular/core'; import type { Format } from 'devextreme/common'; +import type { FormatLocale } from 'devextreme/common/core/localization'; @Component({ template: '' @@ -25,6 +26,13 @@ export abstract class DxoFormat extends NestedOption { this._setOption('formatter', value); } + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + get parser(): Function { return this._getOption('parser'); } diff --git a/packages/devextreme-angular/src/ui/nested/day-header-format.ts b/packages/devextreme-angular/src/ui/nested/day-header-format.ts index ff81869decad..5005ba6eb384 100644 --- a/packages/devextreme-angular/src/ui/nested/day-header-format.ts +++ b/packages/devextreme-angular/src/ui/nested/day-header-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/display-format.ts b/packages/devextreme-angular/src/ui/nested/display-format.ts index 7f8ea5fa4b14..9d3e2c4f5677 100644 --- a/packages/devextreme-angular/src/ui/nested/display-format.ts +++ b/packages/devextreme-angular/src/ui/nested/display-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/format.ts b/packages/devextreme-angular/src/ui/nested/format.ts index b364aac5b260..bfbdb6109fd6 100644 --- a/packages/devextreme-angular/src/ui/nested/format.ts +++ b/packages/devextreme-angular/src/ui/nested/format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/item-text-format.ts b/packages/devextreme-angular/src/ui/nested/item-text-format.ts index a9facddf6803..0249393291f8 100644 --- a/packages/devextreme-angular/src/ui/nested/item-text-format.ts +++ b/packages/devextreme-angular/src/ui/nested/item-text-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/message-timestamp-format.ts b/packages/devextreme-angular/src/ui/nested/message-timestamp-format.ts index 3da65fb84cdf..0a4c529f5a28 100644 --- a/packages/devextreme-angular/src/ui/nested/message-timestamp-format.ts +++ b/packages/devextreme-angular/src/ui/nested/message-timestamp-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/nested/value-format.ts b/packages/devextreme-angular/src/ui/nested/value-format.ts index 595f78ac024e..22608661dc62 100644 --- a/packages/devextreme-angular/src/ui/nested/value-format.ts +++ b/packages/devextreme-angular/src/ui/nested/value-format.ts @@ -31,6 +31,7 @@ import { DxoFormat } from './base/format'; inputs: [ 'currency', 'formatter', + 'locale', 'parser', 'precision', 'type', diff --git a/packages/devextreme-angular/src/ui/number-box/nested/format.ts b/packages/devextreme-angular/src/ui/number-box/nested/format.ts index 028da33f0f64..560713695df8 100644 --- a/packages/devextreme-angular/src/ui/number-box/nested/format.ts +++ b/packages/devextreme-angular/src/ui/number-box/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoNumberBoxFormatComponent extends NestedOption implements OnDestr this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/pie-chart/nested/argument-format.ts b/packages/devextreme-angular/src/ui/pie-chart/nested/argument-format.ts index fc0198795928..c1ee083e4827 100644 --- a/packages/devextreme-angular/src/ui/pie-chart/nested/argument-format.ts +++ b/packages/devextreme-angular/src/ui/pie-chart/nested/argument-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoPieChartArgumentFormatComponent extends NestedOption implements this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/pie-chart/nested/format.ts b/packages/devextreme-angular/src/ui/pie-chart/nested/format.ts index 3132416dc202..5de0edf46516 100644 --- a/packages/devextreme-angular/src/ui/pie-chart/nested/format.ts +++ b/packages/devextreme-angular/src/ui/pie-chart/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoPieChartFormatComponent extends NestedOption implements OnDestro this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/polar-chart/nested/argument-format.ts b/packages/devextreme-angular/src/ui/polar-chart/nested/argument-format.ts index a57b33490163..3bee8aee68d5 100644 --- a/packages/devextreme-angular/src/ui/polar-chart/nested/argument-format.ts +++ b/packages/devextreme-angular/src/ui/polar-chart/nested/argument-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoPolarChartArgumentFormatComponent extends NestedOption implement this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/polar-chart/nested/format.ts b/packages/devextreme-angular/src/ui/polar-chart/nested/format.ts index 25e74a4c8135..4c66bb4f4c89 100644 --- a/packages/devextreme-angular/src/ui/polar-chart/nested/format.ts +++ b/packages/devextreme-angular/src/ui/polar-chart/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoPolarChartFormatComponent extends NestedOption implements OnDest this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/range-selector/nested/argument-format.ts b/packages/devextreme-angular/src/ui/range-selector/nested/argument-format.ts index 110bbec9c0ae..57b11456bdb6 100644 --- a/packages/devextreme-angular/src/ui/range-selector/nested/argument-format.ts +++ b/packages/devextreme-angular/src/ui/range-selector/nested/argument-format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoRangeSelectorArgumentFormatComponent extends NestedOption implem this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/range-selector/nested/format.ts b/packages/devextreme-angular/src/ui/range-selector/nested/format.ts index 84c885b87e74..b1731bfd216c 100644 --- a/packages/devextreme-angular/src/ui/range-selector/nested/format.ts +++ b/packages/devextreme-angular/src/ui/range-selector/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoRangeSelectorFormatComponent extends NestedOption implements OnD this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/range-slider/nested/format.ts b/packages/devextreme-angular/src/ui/range-slider/nested/format.ts index dedba57de149..328993724f5f 100644 --- a/packages/devextreme-angular/src/ui/range-slider/nested/format.ts +++ b/packages/devextreme-angular/src/ui/range-slider/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoRangeSliderFormatComponent extends NestedOption implements OnDes this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/sankey/nested/format.ts b/packages/devextreme-angular/src/ui/sankey/nested/format.ts index 85f1ccaca190..4ad53177fa94 100644 --- a/packages/devextreme-angular/src/ui/sankey/nested/format.ts +++ b/packages/devextreme-angular/src/ui/sankey/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoSankeyFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/slider/nested/format.ts b/packages/devextreme-angular/src/ui/slider/nested/format.ts index 6ff3f05052ff..a028705f0cdb 100644 --- a/packages/devextreme-angular/src/ui/slider/nested/format.ts +++ b/packages/devextreme-angular/src/ui/slider/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoSliderFormatComponent extends NestedOption implements OnDestroy, this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/sparkline/nested/format.ts b/packages/devextreme-angular/src/ui/sparkline/nested/format.ts index e9baf34d5220..da27ac3ebe8b 100644 --- a/packages/devextreme-angular/src/ui/sparkline/nested/format.ts +++ b/packages/devextreme-angular/src/ui/sparkline/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoSparklineFormatComponent extends NestedOption implements OnDestr this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/tree-list/nested/format.ts b/packages/devextreme-angular/src/ui/tree-list/nested/format.ts index e0d4afe91cfe..9b64e278ad94 100644 --- a/packages/devextreme-angular/src/ui/tree-list/nested/format.ts +++ b/packages/devextreme-angular/src/ui/tree-list/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoTreeListFormatComponent extends NestedOption implements OnDestro this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-angular/src/ui/tree-map/nested/format.ts b/packages/devextreme-angular/src/ui/tree-map/nested/format.ts index a132793c3f78..bc15c2c00f06 100644 --- a/packages/devextreme-angular/src/ui/tree-map/nested/format.ts +++ b/packages/devextreme-angular/src/ui/tree-map/nested/format.ts @@ -14,6 +14,7 @@ import { +import type { FormatLocale } from 'devextreme/common/core/localization'; import type { Format } from 'devextreme/common'; import { @@ -47,6 +48,14 @@ export class DxoTreeMapFormatComponent extends NestedOption implements OnDestroy this._setOption('formatter', value); } + @Input() + get locale(): FormatLocale { + return this._getOption('locale'); + } + set locale(value: FormatLocale) { + this._setOption('locale', value); + } + @Input() get parser(): ((value: string) => number | Date) { return this._getOption('parser'); diff --git a/packages/devextreme-react/src/bar-gauge.ts b/packages/devextreme-react/src/bar-gauge.ts index 7f0b7875734d..024ae1ec509e 100644 --- a/packages/devextreme-react/src/bar-gauge.ts +++ b/packages/devextreme-react/src/bar-gauge.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, TooltipHiddenEvent, TooltipShownEvent, BarGaugeBarInfo, BarGaugeLegendItem } from "devextreme/viz/bar_gauge"; import type { AnimationEaseMode, Font as ChartsFont, TextOverflow, WordWrap, DashStyle } from "devextreme/common/charts"; import type { HorizontalAlignment, VerticalEdge, ExportFormat, Format as CommonFormat, Position, template, Orientation } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -273,6 +273,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -315,6 +316,7 @@ const Geometry = Object.assign(_ type IItemTextFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/bullet.ts b/packages/devextreme-react/src/bullet.ts index a5e815701ec6..4b17a1fe423d 100644 --- a/packages/devextreme-react/src/bullet.ts +++ b/packages/devextreme-react/src/bullet.ts @@ -10,8 +10,8 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, TooltipHiddenEvent, TooltipShownEvent } from "devextreme/viz/bullet"; import type { DashStyle, Font as ChartsFont } from "devextreme/common/charts"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { Format as CommonFormat, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -119,6 +119,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/card-view.ts b/packages/devextreme-react/src/card-view.ts index 4cf1b45263e7..22f8b7870bde 100644 --- a/packages/devextreme-react/src/card-view.ts +++ b/packages/devextreme-react/src/card-view.ts @@ -20,7 +20,7 @@ import type { ContentReadyEvent as TabPanelContentReadyEvent, DisposingEvent as import type { LocateInMenuMode, ShowTextMode } from "devextreme/ui/toolbar"; import type { CollectionWidgetItem } from "devextreme/ui/collection/ui.collection_widget.base"; import type { HeaderFilterSearchConfig, HeaderFilterTexts, SelectionColumnDisplayMode, DataChangeType, FilterType, ColumnHeaderFilter as GridsColumnHeaderFilter, ColumnChooserMode, ColumnChooserSearchConfig, ColumnChooserSelectionConfig, HeaderFilterGroupInterval, ColumnHeaderFilterSearchConfig, DataChange, FilterPanel as GridsFilterPanel, FilterPanelTexts as GridsFilterPanelTexts, PagerPageSize } from "devextreme/common/grids"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { DataSourceOptions } from "devextreme/data/data_source"; import type { Store } from "devextreme/data/store"; import type { AIIntegration } from "devextreme/common/ai-integration"; @@ -1372,6 +1372,7 @@ const Form = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/chart.ts b/packages/devextreme-react/src/chart.ts index 50be1aa56325..e0c59f009f3e 100644 --- a/packages/devextreme-react/src/chart.ts +++ b/packages/devextreme-react/src/chart.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { ArgumentAxisClickEvent, DisposingEvent, DoneEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, LegendClickEvent, PointClickEvent, SeriesClickEvent, TooltipHiddenEvent, TooltipShownEvent, ZoomEndEvent, ZoomStartEvent, chartPointAggregationInfoObject, chartSeriesObject, ChartSeriesAggregationMethod, dxChartAnnotationConfig, AggregatedPointsPosition, ChartLabelDisplayMode, FinancialChartReductionLevel, chartPointObject, dxChartPointInfo, ChartTooltipLocation, ChartZoomAndPanMode, EventKeyModifier } from "devextreme/viz/chart"; import type { AnimationEaseMode, DashStyle, Font as ChartsFont, TextOverflow, AnnotationType, WordWrap, TimeInterval, ChartsDataType, ScaleBreak, ScaleBreakLineStyle, RelativePosition, DiscreteAxisDivisionMode, ArgumentAxisHoverMode, ChartsAxisLabelOverlap, AxisScaleType, VisualRangeUpdateMode, ChartsColor, SeriesHoverMode, HatchDirection, PointInteractionMode, PointSymbol, SeriesSelectionMode, SeriesType, ValueErrorBarDisplayMode, ValueErrorBarType, LegendItem, LegendHoverMode, ValueAxisVisualRangeUpdateMode } from "devextreme/common/charts"; import type { template, HorizontalAlignment, VerticalAlignment, Format as CommonFormat, Position, VerticalEdge, ExportFormat, Orientation } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { ChartSeries } from "devextreme/viz/common"; import type * as CommonChartTypes from "devextreme/common/charts"; @@ -587,6 +587,7 @@ const ArgumentAxis = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -1921,6 +1922,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/chat.ts b/packages/devextreme-react/src/chat.ts index 9a88e3b7414a..00211b9b053c 100644 --- a/packages/devextreme-react/src/chat.ts +++ b/packages/devextreme-react/src/chat.ts @@ -12,6 +12,7 @@ import type { Message, AttachmentDownloadClickEvent, DisposingEvent, Initialized import type { DisposingEvent as FileUploaderDisposingEvent, InitializedEvent as FileUploaderInitializedEvent, BeforeSendEvent, ContentReadyEvent, DropZoneEnterEvent, DropZoneLeaveEvent, FilesUploadedEvent, OptionChangedEvent, ProgressEvent, UploadAbortedEvent, UploadedEvent, UploadErrorEvent, UploadStartedEvent, ValueChangedEvent, UploadHttpMethod, FileUploadMode } from "devextreme/ui/file_uploader"; import type { DisposingEvent as SpeechToTextDisposingEvent, InitializedEvent as SpeechToTextInitializedEvent, ContentReadyEvent as SpeechToTextContentReadyEvent, OptionChangedEvent as SpeechToTextOptionChangedEvent, CustomSpeechRecognizer as SpeechToTextCustomSpeechRecognizer, EndEvent, ErrorEvent, ResultEvent, StartClickEvent, StopClickEvent, SpeechRecognitionConfig as SpeechToTextSpeechRecognitionConfig } from "devextreme/ui/speech_to_text"; import type { DisposingEvent as ButtonGroupDisposingEvent, InitializedEvent as ButtonGroupInitializedEvent, ContentReadyEvent as ButtonGroupContentReadyEvent, OptionChangedEvent as ButtonGroupOptionChangedEvent, dxButtonGroupItem, ItemClickEvent, SelectionChangedEvent } from "devextreme/ui/button_group"; +import type { FormatLocale } from "devextreme/common/core/localization"; import type { Format, ValidationStatus, ButtonType, template, ButtonStyle, SingleMultipleOrNone } from "devextreme/common"; import type { CollectionWidgetItem } from "devextreme/ui/collection/ui.collection_widget.base"; @@ -231,6 +232,7 @@ const CustomSpeechRecognizer = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: Format | string; @@ -405,6 +407,7 @@ const Item = Object.assign(_componen type IMessageTimestampFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: Format | string; diff --git a/packages/devextreme-react/src/circular-gauge.ts b/packages/devextreme-react/src/circular-gauge.ts index 85c4d650ecb4..cedb08106696 100644 --- a/packages/devextreme-react/src/circular-gauge.ts +++ b/packages/devextreme-react/src/circular-gauge.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, TooltipHiddenEvent, TooltipShownEvent, CircularGaugeLabelOverlap, CircularGaugeElementOrientation } from "devextreme/viz/circular_gauge"; import type { AnimationEaseMode, DashStyle, Font as ChartsFont, LabelOverlap, ChartsColor, Palette, PaletteExtensionMode, TextOverflow, WordWrap } from "devextreme/common/charts"; import type { ExportFormat, Format as CommonFormat, HorizontalEdge, VerticalEdge, HorizontalAlignment, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -246,6 +246,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/common/core/localization.ts b/packages/devextreme-react/src/common/core/localization.ts index 6dbb7f75a2a6..6f35e4992209 100644 --- a/packages/devextreme-react/src/common/core/localization.ts +++ b/packages/devextreme-react/src/common/core/localization.ts @@ -9,4 +9,5 @@ export { } from "devextreme/common/core/localization"; export type { Format, + FormatLocale, } from "devextreme/common/core/localization"; diff --git a/packages/devextreme-react/src/common/index.ts b/packages/devextreme-react/src/common/index.ts index d4cc081e305a..ec99b9c2128d 100644 --- a/packages/devextreme-react/src/common/index.ts +++ b/packages/devextreme-react/src/common/index.ts @@ -189,6 +189,7 @@ export namespace Core { export namespace Localization { export type Format = CoreLocalizationModule.Format; export const formatDate = CoreLocalizationModule.formatDate; + export type FormatLocale = CoreLocalizationModule.FormatLocale; export const formatMessage = CoreLocalizationModule.formatMessage; export const formatNumber = CoreLocalizationModule.formatNumber; export const loadMessages = CoreLocalizationModule.loadMessages; diff --git a/packages/devextreme-react/src/data-grid.ts b/packages/devextreme-react/src/data-grid.ts index 285e527e5e4d..31c41168425e 100644 --- a/packages/devextreme-react/src/data-grid.ts +++ b/packages/devextreme-react/src/data-grid.ts @@ -20,7 +20,7 @@ import type { ContentReadyEvent as TabPanelContentReadyEvent, DisposingEvent as import type { AIIntegration } from "devextreme/common/ai-integration"; import type { dxPopupOptions, dxPopupToolbarItem, ToolbarLocation } from "devextreme/ui/popup"; import type { AnimationConfig, CollisionResolution, PositionConfig, AnimationState, AnimationType, CollisionResolutionCombination } from "devextreme/common/core/animation"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { DataSourceOptions } from "devextreme/data/data_source"; import type { Store } from "devextreme/data/store"; import type { LocateInMenuMode, ShowTextMode } from "devextreme/ui/toolbar"; @@ -1960,6 +1960,7 @@ const Form = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -3970,6 +3971,7 @@ const ValidationRule = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/date-box.ts b/packages/devextreme-react/src/date-box.ts index 6e7880649947..7c32a29a983b 100644 --- a/packages/devextreme-react/src/date-box.ts +++ b/packages/devextreme-react/src/date-box.ts @@ -13,6 +13,7 @@ import type { ContentReadyEvent as ButtonContentReadyEvent, DisposingEvent as Bu import type { DisposingEvent as CalendarDisposingEvent, InitializedEvent as CalendarInitializedEvent, ValueChangedEvent as CalendarValueChangedEvent, DisabledDate, CalendarZoomLevel, OptionChangedEvent, CalendarSelectionMode, WeekNumberRule } from "devextreme/ui/calendar"; import type { AnimationConfig, CollisionResolution, PositionConfig, AnimationState, AnimationType, CollisionResolutionCombination } from "devextreme/common/core/animation"; import type { HorizontalAlignment, VerticalAlignment, TextEditorButtonLocation, template, DayOfWeek, ValidationMessageMode, Position as CommonPosition, ValidationStatus, Format, PositionAlignment, Direction, ButtonStyle, ButtonType, ToolbarItemLocation, ToolbarItemComponent } from "devextreme/common"; +import type { FormatLocale } from "devextreme/common/core/localization"; import type { event } from "devextreme/events/events.types"; import type { EventInfo } from "devextreme/common/core/events"; import type { Component } from "devextreme/core/component"; @@ -296,6 +297,7 @@ const Collision = Object.assign type IDisplayFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: Format | string; diff --git a/packages/devextreme-react/src/date-range-box.ts b/packages/devextreme-react/src/date-range-box.ts index c50293fffbf2..f91a812ee541 100644 --- a/packages/devextreme-react/src/date-range-box.ts +++ b/packages/devextreme-react/src/date-range-box.ts @@ -13,6 +13,7 @@ import type { ContentReadyEvent as ButtonContentReadyEvent, DisposingEvent as Bu import type { DisposingEvent as CalendarDisposingEvent, InitializedEvent as CalendarInitializedEvent, ValueChangedEvent as CalendarValueChangedEvent, DisabledDate, CalendarZoomLevel, OptionChangedEvent, CalendarSelectionMode, WeekNumberRule } from "devextreme/ui/calendar"; import type { AnimationConfig, CollisionResolution, PositionConfig, AnimationState, AnimationType, CollisionResolutionCombination } from "devextreme/common/core/animation"; import type { HorizontalAlignment, VerticalAlignment, TextEditorButtonLocation, template, DayOfWeek, ValidationMessageMode, Position as CommonPosition, ValidationStatus, Format, PositionAlignment, Direction, ButtonStyle, ButtonType, ToolbarItemLocation, ToolbarItemComponent } from "devextreme/common"; +import type { FormatLocale } from "devextreme/common/core/localization"; import type { event } from "devextreme/events/events.types"; import type { EventInfo } from "devextreme/common/core/events"; import type { Component } from "devextreme/core/component"; @@ -301,6 +302,7 @@ const Collision = Object.assign type IDisplayFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: Format | string; diff --git a/packages/devextreme-react/src/filter-builder.ts b/packages/devextreme-react/src/filter-builder.ts index 7c4e2bffbd77..c533d19de872 100644 --- a/packages/devextreme-react/src/filter-builder.ts +++ b/packages/devextreme-react/src/filter-builder.ts @@ -10,7 +10,7 @@ import NestedOption from "./core/nested-option"; import type { ContentReadyEvent, DisposingEvent, EditorPreparedEvent, EditorPreparingEvent, InitializedEvent, ValueChangedEvent, dxFilterBuilderField, FieldInfo, FilterBuilderOperation } from "devextreme/ui/filter_builder"; import type { DataType, template, Format as CommonFormat } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { DataSourceOptions } from "devextreme/data/data_source"; import type { Store } from "devextreme/data/store"; @@ -195,6 +195,7 @@ const FilterOperationDescriptions = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/funnel.ts b/packages/devextreme-react/src/funnel.ts index d81e56c35a6e..956ef245ba74 100644 --- a/packages/devextreme-react/src/funnel.ts +++ b/packages/devextreme-react/src/funnel.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, ItemClickEvent, LegendClickEvent, dxFunnelItem, FunnelLegendItem } from "devextreme/viz/funnel"; import type { DashStyle, Font as ChartsFont, TextOverflow, WordWrap, HatchDirection, LabelPosition } from "devextreme/common/charts"; import type { ExportFormat, Format as CommonFormat, HorizontalAlignment, VerticalEdge, HorizontalEdge, Position, template, Orientation } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -216,6 +216,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/gantt.ts b/packages/devextreme-react/src/gantt.ts index 75f6b712daf8..243c9f1a4271 100644 --- a/packages/devextreme-react/src/gantt.ts +++ b/packages/devextreme-react/src/gantt.ts @@ -12,7 +12,7 @@ import type { ContentReadyEvent, ContextMenuPreparingEvent, CustomCommandEvent, import type { HorizontalAlignment, template, DataType, Format as CommonFormat, SortOrder, SearchMode, ToolbarItemLocation, ToolbarItemComponent, SingleMultipleOrNone } from "devextreme/common"; import type { dxTreeListColumn, dxTreeListRowObject } from "devextreme/ui/tree_list"; import type { FilterOperation, FilterType, ColumnHeaderFilter as GridsColumnHeaderFilter, SelectedFilterOperation, HeaderFilterGroupInterval, ColumnHeaderFilterSearchConfig, HeaderFilterSearchConfig } from "devextreme/common/grids"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { DataSourceOptions } from "devextreme/data/data_source"; import type { Store } from "devextreme/data/store"; import type { dxContextMenuItem } from "devextreme/ui/context_menu"; @@ -465,6 +465,7 @@ const FilterRow = Object.assign type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/linear-gauge.ts b/packages/devextreme-react/src/linear-gauge.ts index 0c7cdd7118d5..db0f2b19b2a4 100644 --- a/packages/devextreme-react/src/linear-gauge.ts +++ b/packages/devextreme-react/src/linear-gauge.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, TooltipHiddenEvent, TooltipShownEvent } from "devextreme/viz/linear_gauge"; import type { AnimationEaseMode, DashStyle, Font as ChartsFont, LabelOverlap, ChartsColor, Palette, PaletteExtensionMode, TextOverflow, WordWrap } from "devextreme/common/charts"; import type { ExportFormat, Format as CommonFormat, Orientation, HorizontalAlignment, VerticalAlignment, HorizontalEdge, VerticalEdge, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -235,6 +235,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/number-box.ts b/packages/devextreme-react/src/number-box.ts index 35e3966a2be6..7994034dc226 100644 --- a/packages/devextreme-react/src/number-box.ts +++ b/packages/devextreme-react/src/number-box.ts @@ -11,6 +11,7 @@ import NestedOption from "./core/nested-option"; import type { ChangeEvent, ContentReadyEvent, CopyEvent, CutEvent, DisposingEvent, EnterKeyEvent, FocusInEvent, FocusOutEvent, InitializedEvent, InputEvent, KeyDownEvent, KeyUpEvent, PasteEvent, ValueChangedEvent } from "devextreme/ui/number_box"; import type { ContentReadyEvent as ButtonContentReadyEvent, DisposingEvent as ButtonDisposingEvent, InitializedEvent as ButtonInitializedEvent, dxButtonOptions, ClickEvent, OptionChangedEvent } from "devextreme/ui/button"; import type { TextEditorButtonLocation, Format as CommonFormat, ButtonStyle, template, ButtonType } from "devextreme/common"; +import type { FormatLocale } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -112,6 +113,7 @@ const Button = Object.assign(_comp type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/pie-chart.ts b/packages/devextreme-react/src/pie-chart.ts index d563f15bf286..ea185200520b 100644 --- a/packages/devextreme-react/src/pie-chart.ts +++ b/packages/devextreme-react/src/pie-chart.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DoneEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, LegendClickEvent, PointClickEvent, TooltipHiddenEvent, TooltipShownEvent, dxPieChartAnnotationConfig, PieChartAnnotationLocation, dxPieChartCommonAnnotationConfig, PieChartSeriesInteractionMode, SmallValuesGroupingMode, PieChartLegendItem, PieChartLegendHoverMode, PieChartSeries, dxPieChartPointInfo } from "devextreme/viz/pie_chart"; import type { AnimationEaseMode, DashStyle, Font as ChartsFont, TextOverflow, AnnotationType, WordWrap, ChartsDataType, ChartsColor, HatchDirection, LabelPosition } from "devextreme/common/charts"; import type { template, Format as CommonFormat, ExportFormat, HorizontalAlignment, Position, Orientation, VerticalEdge } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -264,6 +264,7 @@ const AnnotationBorder = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -604,6 +605,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/polar-chart.ts b/packages/devextreme-react/src/polar-chart.ts index c6ba4db5af64..8acf34478996 100644 --- a/packages/devextreme-react/src/polar-chart.ts +++ b/packages/devextreme-react/src/polar-chart.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { ArgumentAxisClickEvent, DisposingEvent, DoneEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, LegendClickEvent, PointClickEvent, SeriesClickEvent, TooltipHiddenEvent, TooltipShownEvent, ZoomEndEvent, ZoomStartEvent, dxPolarChartAnnotationConfig, dxPolarChartCommonAnnotationConfig, PolarChartSeriesType, PolarChartSeries, dxPolarChartPointInfo } from "devextreme/viz/polar_chart"; import type { AnimationEaseMode, DashStyle, Font as ChartsFont, TextOverflow, AnnotationType, WordWrap, ChartsDataType, DiscreteAxisDivisionMode, ArgumentAxisHoverMode, LabelOverlap, TimeInterval, AxisScaleType, ChartsColor, SeriesHoverMode, HatchDirection, RelativePosition, PointInteractionMode, PointSymbol, SeriesSelectionMode, ValueErrorBarDisplayMode, ValueErrorBarType, LegendItem, LegendHoverMode, ValueAxisVisualRangeUpdateMode } from "devextreme/common/charts"; import type { template, Format as CommonFormat, ExportFormat, HorizontalAlignment, Position, Orientation, VerticalEdge } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type * as CommonChartTypes from "devextreme/common/charts"; @@ -462,6 +462,7 @@ const ArgumentAxisTick = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -1307,6 +1308,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/range-selector.ts b/packages/devextreme-react/src/range-selector.ts index d7b87a74a826..0a11190e36a8 100644 --- a/packages/devextreme-react/src/range-selector.ts +++ b/packages/devextreme-react/src/range-selector.ts @@ -10,8 +10,8 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, ValueChangedEvent, BackgroundImageLocation, ChartAxisScale, AxisScale } from "devextreme/viz/range_selector"; import type { chartPointAggregationInfoObject, chartSeriesObject, ChartSeriesAggregationMethod, dxChartCommonSeriesSettings, FinancialChartReductionLevel } from "devextreme/viz/chart"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { Format as CommonFormat, SliderValueChangeMode, HorizontalAlignment, ExportFormat, VerticalEdge } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { DashStyle, ScaleBreakLineStyle, Palette, PaletteExtensionMode, ChartsDataType, ChartsColor, SeriesHoverMode, HatchDirection, Font as ChartsFont, RelativePosition, PointInteractionMode, PointSymbol, SeriesSelectionMode, SeriesType, ValueErrorBarDisplayMode, ValueErrorBarType, LabelOverlap, TimeInterval, ScaleBreak, DiscreteAxisDivisionMode, TextOverflow, WordWrap } from "devextreme/common/charts"; import type { ChartSeries } from "devextreme/viz/common"; @@ -149,6 +149,7 @@ const AggregationInterval = Object.assign string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; @@ -824,6 +825,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/range-slider.ts b/packages/devextreme-react/src/range-slider.ts index d1f2606d7874..4ced636de382 100644 --- a/packages/devextreme-react/src/range-slider.ts +++ b/packages/devextreme-react/src/range-slider.ts @@ -9,8 +9,8 @@ import { Component as BaseComponent, IHtmlOptions, ComponentRef, NestedComponent import NestedOption from "./core/nested-option"; import type { ContentReadyEvent, DisposingEvent, InitializedEvent, ValueChangedEvent } from "devextreme/ui/range_slider"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { Format as CommonFormat, VerticalEdge, TooltipShowMode } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -79,6 +79,7 @@ const RangeSlider = memo( type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/sankey.ts b/packages/devextreme-react/src/sankey.ts index 825ab791ffc8..57ca17fa9538 100644 --- a/packages/devextreme-react/src/sankey.ts +++ b/packages/devextreme-react/src/sankey.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, LinkClickEvent, NodeClickEvent, dxSankeyNode, SankeyColorMode } from "devextreme/viz/sankey"; import type { DashStyle, HatchDirection, Font as ChartsFont, TextOverflow, WordWrap } from "devextreme/common/charts"; import type { ExportFormat, Format as CommonFormat, HorizontalAlignment, VerticalEdge, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -188,6 +188,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/slider.ts b/packages/devextreme-react/src/slider.ts index cd3edf0e37fa..0fb3fda6e238 100644 --- a/packages/devextreme-react/src/slider.ts +++ b/packages/devextreme-react/src/slider.ts @@ -9,8 +9,8 @@ import { Component as BaseComponent, IHtmlOptions, ComponentRef, NestedComponent import NestedOption from "./core/nested-option"; import type { ContentReadyEvent, DisposingEvent, InitializedEvent, ValueChangedEvent } from "devextreme/ui/slider"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { Format as CommonFormat, VerticalEdge, TooltipShowMode } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -79,6 +79,7 @@ const Slider = memo( type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/sparkline.ts b/packages/devextreme-react/src/sparkline.ts index ef8e8f06368f..5bdf2e1140f7 100644 --- a/packages/devextreme-react/src/sparkline.ts +++ b/packages/devextreme-react/src/sparkline.ts @@ -10,8 +10,8 @@ import NestedOption from "./core/nested-option"; import type { DisposingEvent, DrawnEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, TooltipHiddenEvent, TooltipShownEvent } from "devextreme/viz/sparkline"; import type { DashStyle, Font as ChartsFont } from "devextreme/common/charts"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; import type { Format as CommonFormat, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -119,6 +119,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/tree-list.ts b/packages/devextreme-react/src/tree-list.ts index 25acb626d9eb..058b8d98adbf 100644 --- a/packages/devextreme-react/src/tree-list.ts +++ b/packages/devextreme-react/src/tree-list.ts @@ -20,7 +20,7 @@ import type { AIIntegration } from "devextreme/common/ai-integration"; import type { dxPopupOptions, dxPopupToolbarItem, ToolbarLocation } from "devextreme/ui/popup"; import type { AnimationConfig, CollisionResolution, PositionConfig, AnimationState, AnimationType, CollisionResolutionCombination } from "devextreme/common/core/animation"; import type { ValidationRuleType, HorizontalAlignment, VerticalAlignment, template, TextEditorButtonLocation, ButtonStyle, ButtonType, DataType, Format as CommonFormat, SortOrder, SearchMode, ComparisonOperator, TextBoxPredefinedButton, TextEditorButton, LabelMode, MaskMode, EditorStyle, ValidationMessageMode, Position as CommonPosition, ValidationStatus, PositionAlignment, Mode, Direction, ToolbarItemLocation, ToolbarItemComponent, DisplayMode, DragDirection, DragHighlight, ScrollMode, ScrollbarMode, SingleMultipleOrNone, TabsIconPosition, TabsStyle } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { Format as LocalizationFormat, FormatLocale } from "devextreme/common/core/localization"; import type { DataSourceOptions } from "devextreme/data/data_source"; import type { Store } from "devextreme/data/store"; import type { event } from "devextreme/events/events.types"; @@ -1729,6 +1729,7 @@ const Form = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-react/src/tree-map.ts b/packages/devextreme-react/src/tree-map.ts index 8b216992024f..809127e5ef58 100644 --- a/packages/devextreme-react/src/tree-map.ts +++ b/packages/devextreme-react/src/tree-map.ts @@ -11,7 +11,7 @@ import NestedOption from "./core/nested-option"; import type { ClickEvent, DisposingEvent, DrawnEvent, DrillEvent, ExportedEvent, ExportingEvent, FileSavingEvent, IncidentOccurredEvent, InitializedEvent, NodesInitializedEvent, NodesRenderingEvent, TreeMapColorizerType, dxTreeMapNode } from "devextreme/viz/tree_map"; import type { DashStyle, Palette, PaletteExtensionMode, Font as ChartsFont, TextOverflow, WordWrap } from "devextreme/common/charts"; import type { ExportFormat, Format as CommonFormat, HorizontalAlignment, VerticalEdge, template } from "devextreme/common"; -import type { Format as LocalizationFormat } from "devextreme/common/core/localization"; +import type { FormatLocale, Format as LocalizationFormat } from "devextreme/common/core/localization"; type ReplaceFieldTypes = { [P in keyof TSource]: P extends keyof TReplacement ? TReplacement[P] : TSource[P]; @@ -192,6 +192,7 @@ const Font = Object.assign(_componen type IFormatProps = React.PropsWithChildren<{ currency?: string; formatter?: ((value: number | Date) => string); + locale?: FormatLocale; parser?: ((value: string) => number | Date); precision?: number; type?: CommonFormat | string; diff --git a/packages/devextreme-vue/src/bar-gauge.ts b/packages/devextreme-vue/src/bar-gauge.ts index 55960c741ffb..0453d6a33cff 100644 --- a/packages/devextreme-vue/src/bar-gauge.ts +++ b/packages/devextreme-vue/src/bar-gauge.ts @@ -36,6 +36,7 @@ import { Orientation, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -369,6 +370,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -377,6 +379,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -415,6 +418,7 @@ const DxItemTextFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -423,6 +427,7 @@ const DxItemTextFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/bullet.ts b/packages/devextreme-vue/src/bullet.ts index 9e715de1178f..8e247748651f 100644 --- a/packages/devextreme-vue/src/bullet.ts +++ b/packages/devextreme-vue/src/bullet.ts @@ -20,11 +20,12 @@ import { Font, } from "devextreme/common/charts"; import { - Format, -} from "devextreme/common"; -import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; +import { + Format, +} from "devextreme/common"; import { prepareConfigurationComponentConfig } from "./core/index"; type AccessibleOptions = Pick string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/card-view.ts b/packages/devextreme-vue/src/card-view.ts index 972adc77da2d..d19c3e23f536 100644 --- a/packages/devextreme-vue/src/card-view.ts +++ b/packages/devextreme-vue/src/card-view.ts @@ -178,6 +178,7 @@ import { } from "devextreme/ui/toolbar"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { AIIntegration, @@ -1796,6 +1797,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -1804,6 +1806,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/chart.ts b/packages/devextreme-vue/src/chart.ts index 8e0eda21e886..28a363486cbd 100644 --- a/packages/devextreme-vue/src/chart.ts +++ b/packages/devextreme-vue/src/chart.ts @@ -95,6 +95,7 @@ import { ChartSeries, } from "devextreme/viz/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import * as CommonChartTypes from "devextreme/common/charts"; @@ -748,6 +749,7 @@ const DxArgumentFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -756,6 +758,7 @@ const DxArgumentFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -1921,6 +1924,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -1929,6 +1933,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/chat.ts b/packages/devextreme-vue/src/chat.ts index 7036b1e3115e..ad2c677bdf31 100644 --- a/packages/devextreme-vue/src/chat.ts +++ b/packages/devextreme-vue/src/chat.ts @@ -37,6 +37,7 @@ import { } from "devextreme/data/store"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { Format as CommonFormat, @@ -400,6 +401,7 @@ const DxDayHeaderFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -408,6 +410,7 @@ const DxDayHeaderFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -641,6 +644,7 @@ const DxMessageTimestampFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -649,6 +653,7 @@ const DxMessageTimestampFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/circular-gauge.ts b/packages/devextreme-vue/src/circular-gauge.ts index fa1854047905..e637cd0afc14 100644 --- a/packages/devextreme-vue/src/circular-gauge.ts +++ b/packages/devextreme-vue/src/circular-gauge.ts @@ -39,6 +39,7 @@ import { HorizontalAlignment, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -327,6 +328,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -335,6 +337,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/common/core/localization.ts b/packages/devextreme-vue/src/common/core/localization.ts index 6dbb7f75a2a6..6f35e4992209 100644 --- a/packages/devextreme-vue/src/common/core/localization.ts +++ b/packages/devextreme-vue/src/common/core/localization.ts @@ -9,4 +9,5 @@ export { } from "devextreme/common/core/localization"; export type { Format, + FormatLocale, } from "devextreme/common/core/localization"; diff --git a/packages/devextreme-vue/src/common/index.ts b/packages/devextreme-vue/src/common/index.ts index d4cc081e305a..ec99b9c2128d 100644 --- a/packages/devextreme-vue/src/common/index.ts +++ b/packages/devextreme-vue/src/common/index.ts @@ -189,6 +189,7 @@ export namespace Core { export namespace Localization { export type Format = CoreLocalizationModule.Format; export const formatDate = CoreLocalizationModule.formatDate; + export type FormatLocale = CoreLocalizationModule.FormatLocale; export const formatMessage = CoreLocalizationModule.formatMessage; export const formatNumber = CoreLocalizationModule.formatNumber; export const loadMessages = CoreLocalizationModule.loadMessages; diff --git a/packages/devextreme-vue/src/data-grid.ts b/packages/devextreme-vue/src/data-grid.ts index 7eb39a9740a6..b55ecfac9d13 100644 --- a/packages/devextreme-vue/src/data-grid.ts +++ b/packages/devextreme-vue/src/data-grid.ts @@ -235,6 +235,7 @@ import { } from "devextreme/ui/form"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { LocateInMenuMode, @@ -2619,6 +2620,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -2627,6 +2629,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -4716,6 +4719,7 @@ const DxValueFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -4724,6 +4728,7 @@ const DxValueFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/date-box.ts b/packages/devextreme-vue/src/date-box.ts index 5025dce0b230..2a12716ba967 100644 --- a/packages/devextreme-vue/src/date-box.ts +++ b/packages/devextreme-vue/src/date-box.ts @@ -43,6 +43,7 @@ import { } from "devextreme/ui/calendar"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { dxPopupOptions, @@ -571,6 +572,7 @@ const DxDisplayFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -579,6 +581,7 @@ const DxDisplayFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/date-range-box.ts b/packages/devextreme-vue/src/date-range-box.ts index 22d9b7fd35d9..4cc1f08a20f3 100644 --- a/packages/devextreme-vue/src/date-range-box.ts +++ b/packages/devextreme-vue/src/date-range-box.ts @@ -42,6 +42,7 @@ import { } from "devextreme/ui/calendar"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { dxPopupOptions, @@ -580,6 +581,7 @@ const DxDisplayFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -588,6 +590,7 @@ const DxDisplayFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/filter-builder.ts b/packages/devextreme-vue/src/filter-builder.ts index 3fec5e29e1c8..682c2dc023a2 100644 --- a/packages/devextreme-vue/src/filter-builder.ts +++ b/packages/devextreme-vue/src/filter-builder.ts @@ -22,6 +22,7 @@ import { } from "devextreme/common"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { DataSourceOptions, @@ -273,6 +274,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -281,6 +283,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/funnel.ts b/packages/devextreme-vue/src/funnel.ts index b02723eab9e5..86aa129d2844 100644 --- a/packages/devextreme-vue/src/funnel.ts +++ b/packages/devextreme-vue/src/funnel.ts @@ -49,6 +49,7 @@ import { Orientation, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -353,6 +354,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -361,6 +363,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/gantt.ts b/packages/devextreme-vue/src/gantt.ts index 4c9a89fc2091..4791a221e3f6 100644 --- a/packages/devextreme-vue/src/gantt.ts +++ b/packages/devextreme-vue/src/gantt.ts @@ -73,6 +73,7 @@ import { } from "devextreme/common/grids"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { DataSourceOptions, @@ -696,6 +697,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -704,6 +706,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/linear-gauge.ts b/packages/devextreme-vue/src/linear-gauge.ts index 20eccee5e49e..dc171a84d302 100644 --- a/packages/devextreme-vue/src/linear-gauge.ts +++ b/packages/devextreme-vue/src/linear-gauge.ts @@ -39,6 +39,7 @@ import { VerticalEdge, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -324,6 +325,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -332,6 +334,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/number-box.ts b/packages/devextreme-vue/src/number-box.ts index a6a3340932f4..15e5894d4f06 100644 --- a/packages/devextreme-vue/src/number-box.ts +++ b/packages/devextreme-vue/src/number-box.ts @@ -35,6 +35,7 @@ import { } from "devextreme/common"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { dxButtonOptions, @@ -274,6 +275,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -282,6 +284,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/pie-chart.ts b/packages/devextreme-vue/src/pie-chart.ts index c5814b70077b..04a8a2e5cfcb 100644 --- a/packages/devextreme-vue/src/pie-chart.ts +++ b/packages/devextreme-vue/src/pie-chart.ts @@ -65,6 +65,7 @@ import { VerticalEdge, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -431,6 +432,7 @@ const DxArgumentFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -439,6 +441,7 @@ const DxArgumentFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -713,6 +716,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -721,6 +725,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/polar-chart.ts b/packages/devextreme-vue/src/polar-chart.ts index 77717e0ef6e8..cf23e305784a 100644 --- a/packages/devextreme-vue/src/polar-chart.ts +++ b/packages/devextreme-vue/src/polar-chart.ts @@ -78,6 +78,7 @@ import { VerticalEdge, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import * as CommonChartTypes from "devextreme/common/charts"; @@ -624,6 +625,7 @@ const DxArgumentFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -632,6 +634,7 @@ const DxArgumentFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -1351,6 +1354,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -1359,6 +1363,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/range-selector.ts b/packages/devextreme-vue/src/range-selector.ts index 79b42b5de637..b5720156c414 100644 --- a/packages/devextreme-vue/src/range-selector.ts +++ b/packages/devextreme-vue/src/range-selector.ts @@ -56,6 +56,10 @@ import { dxChartCommonSeriesSettings, FinancialChartReductionLevel, } from "devextreme/viz/chart"; +import { + FormatLocale, + Format as LocalizationFormat, +} from "devextreme/common/core/localization"; import { Format, SliderValueChangeMode, @@ -66,9 +70,6 @@ import { import { ChartSeries, } from "devextreme/viz/common"; -import { - Format as LocalizationFormat, -} from "devextreme/common/core/localization"; import * as CommonChartTypes from "devextreme/common/charts"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -280,6 +281,7 @@ const DxArgumentFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -288,6 +290,7 @@ const DxArgumentFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, @@ -878,6 +881,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -886,6 +890,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/range-slider.ts b/packages/devextreme-vue/src/range-slider.ts index 7bc0eccb72d2..55c2b9beff57 100644 --- a/packages/devextreme-vue/src/range-slider.ts +++ b/packages/devextreme-vue/src/range-slider.ts @@ -19,6 +19,7 @@ import { TooltipShowMode, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -178,6 +179,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -186,6 +188,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/sankey.ts b/packages/devextreme-vue/src/sankey.ts index 47fcf70a66b3..8c16d9c75e6f 100644 --- a/packages/devextreme-vue/src/sankey.ts +++ b/packages/devextreme-vue/src/sankey.ts @@ -43,6 +43,7 @@ import { WordWrap, } from "devextreme/common/charts"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -306,6 +307,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -314,6 +316,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/slider.ts b/packages/devextreme-vue/src/slider.ts index 45e500dfead8..77f813ce89f2 100644 --- a/packages/devextreme-vue/src/slider.ts +++ b/packages/devextreme-vue/src/slider.ts @@ -19,6 +19,7 @@ import { TooltipShowMode, } from "devextreme/common"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -169,6 +170,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -177,6 +179,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/sparkline.ts b/packages/devextreme-vue/src/sparkline.ts index 7782fe658640..dc26c1734f78 100644 --- a/packages/devextreme-vue/src/sparkline.ts +++ b/packages/devextreme-vue/src/sparkline.ts @@ -29,11 +29,12 @@ import { Font, } from "devextreme/common/charts"; import { - Format, -} from "devextreme/common"; -import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; +import { + Format, +} from "devextreme/common"; import { prepareConfigurationComponentConfig } from "./core/index"; type AccessibleOptions = Pick string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/tree-list.ts b/packages/devextreme-vue/src/tree-list.ts index a6abed1aea26..e28c0c58ce4c 100644 --- a/packages/devextreme-vue/src/tree-list.ts +++ b/packages/devextreme-vue/src/tree-list.ts @@ -230,6 +230,7 @@ import { } from "devextreme/ui/form"; import { Format, + FormatLocale, } from "devextreme/common/core/localization"; import { event, @@ -2396,6 +2397,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -2404,6 +2406,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType, diff --git a/packages/devextreme-vue/src/tree-map.ts b/packages/devextreme-vue/src/tree-map.ts index 50db7ff18039..3527b592192f 100644 --- a/packages/devextreme-vue/src/tree-map.ts +++ b/packages/devextreme-vue/src/tree-map.ts @@ -46,6 +46,7 @@ import { WordWrap, } from "devextreme/common/charts"; import { + FormatLocale, Format as LocalizationFormat, } from "devextreme/common/core/localization"; import { prepareConfigurationComponentConfig } from "./core/index"; @@ -325,6 +326,7 @@ const DxFormatConfig = { "update:hoveredElement": null, "update:currency": null, "update:formatter": null, + "update:locale": null, "update:parser": null, "update:precision": null, "update:type": null, @@ -333,6 +335,7 @@ const DxFormatConfig = { props: { currency: String, formatter: Function as PropType<((value: number | Date) => string)>, + locale: [Object, Function, String] as PropType string)) | string>, parser: Function as PropType<((value: string) => number | Date)>, precision: Number, type: String as PropType,