UNPKG

@syncfusion/react-base

Version:

A common package of core React base, methods and class definitions

279 lines (278 loc) 11.3 kB
import { DateFormat } from './intl/date-formatter'; import { NumberFormat } from './intl/number-formatter'; import { DateParser } from './intl/date-parser'; import { NumberParser } from './intl/number-parser'; import { IntlBase } from './intl/intl-base'; import { extend, getValue } from './util'; /** * Specifies the CLDR data loaded for internationalization functionalities. * * @private */ export const cldrData = {}; /** * Specifies the default culture value to be considered. * * @private */ export const defaultCulture = 'en-US'; /** * Specifies default currency code to be considered * * @private */ export const defaultCurrencyCode = 'USD'; const mapper = ['numericObject', 'dateObject']; /** * Gets a date formatter function for specified culture and format options * * @param {DateFormatOptions} props - Date formatting options * @returns {Function} Formatter function that accepts Date objects */ export function getDateFormat(props) { return DateFormat.dateFormat(props?.locale || defaultCulture, props || { type: 'date', skeleton: 'short' }, cldrData); } /** * Gets a number formatter function for specified culture and format options * * @param {NumberFormatOptions} props - Number formatting options * @returns {Function} Formatter function that accepts numeric values */ export function getNumberFormat(props) { if (props && !props.currency) { const locale = props.locale || defaultCulture; props.currency = getLocaleCurrencyCode(locale) || defaultCurrencyCode; } return NumberFormat.numberFormatter(props?.locale || defaultCulture, props || {}, cldrData); } /** * Returns the parser function for given props. * * @param {DateFormatOptions} props - Specifies the format props in which the parser function will return. * @returns {Function} The date parser function. */ export function getDateParser(props) { return DateParser.dateParser(props?.locale || defaultCulture, props || { skeleton: 'short', type: 'date' }, cldrData); } /** * Returns the parser function for given props. * * @param {NumberFormatOptions} props - Specifies the format props in which the parser function will return. * @returns {Function} The number parser function. */ export function getNumberParser(props) { return NumberParser.numberParser(props?.locale || defaultCulture, props || { format: 'N' }, cldrData); } /** * Returns the formatted string based on format props. * * @param {number} value - Specifies the number to format. * @param {NumberFormatOptions} option - Specifies the format props in which the number will be formatted. * @returns {string} The formatted number string. */ export function formatNumber(value, option) { return getNumberFormat(option)(value) || value; } /** * Returns the formatted date string based on format props. * * @param {Date} value - Specifies the number to format. * @param {DateFormatOptions} option - Specifies the format props in which the number will be formatted. * @returns {string} The formatted date string. */ export function formatDate(value, option) { return getDateFormat(option)(value); } /** * Returns the date object for given date string and props. * * @param {string} value - Specifies the string to parse. * @param {DateFormatOptions} option - Specifies the parse props in which the date string will be parsed. * @returns {Date} The parsed Date object. */ export function parseDate(value, option) { return getDateParser(option)(value); } /** * Returns the number object from the given string value and props. * * @param {string} value - Specifies the string to parse. * @param {NumberFormatOptions} option - Specifies the parse props in which the string number will be parsed. * @returns {number} The parsed number. */ export function parseNumber(value, option) { return getNumberParser(option)(value); } /** * Returns Native Date Time Pattern * * @param {DateFormatOptions} option - Specifies the parse props for resultant date time pattern. * @param {boolean} isExcelFormat - Specifies format value to be converted to excel pattern. * @returns {string} The native date time pattern. * @private */ export function getDatePattern(option, isExcelFormat) { return IntlBase.getActualDateTimeFormat(option?.locale || defaultCulture, option, cldrData, isExcelFormat); } /** * Returns Native Number Pattern * * @param {NumberFormatOptions} option - Specifies the parse props for resultant number pattern. * @param {boolean} isExcel - Specifies whether to return Excel format. * @returns {string} The native number pattern. * @private */ export function getNumberPattern(option, isExcel) { return IntlBase.getActualNumberFormat(option?.locale || defaultCulture, option, cldrData, isExcel); } /** * Returns the First Day of the Week * * @param {string} culture - The culture code (e.g. 'en-US'). * @returns {number} The first day of the week. */ export function getFirstDayOfWeek(culture) { return IntlBase.getWeekData(culture, cldrData); } /** * Load the CLDR data into context * * @param {Object[]} data - Specifies the CLDR data's to be used for formatting and parser. * @returns {void} */ export function loadCldr(...data) { for (const obj of data) { extend(cldrData, obj, {}, true); } } /** * To get the numeric CLDR object for given culture * * @private * @param {string} locale - Specifies the locale for which numericObject to be returned. * @param {string} type - Specifies the type, by default it's decimal. * @returns {Object} Returns the numeric CLDR object containing number formatting patterns and symbols */ export function getNumericObject(locale, type) { const numObject = IntlBase.getDependables(cldrData, locale, '', true)[mapper[0]]; const dateObject = IntlBase.getDependables(cldrData, locale, '')[mapper[1]]; const numSystem = getValue('defaultNumberingSystem', numObject); const symbPattern = getValue('symbols-numberSystem-' + numSystem, numObject); const pattern = IntlBase.getSymbolPattern(type || 'decimal', numSystem, numObject, false); return extend(symbPattern, IntlBase.getFormatData(pattern, true, '', true), { 'dateSeparator': IntlBase.getDateSeparator(dateObject) }); } /** * To get the numeric CLDR number base object for given culture * * @private * @param {string} locale - Specifies the locale for which numericObject to be returned. * @param {string} currency - Specifies the currency for which numericObject to be returned. * @returns {string} Returns the currency symbol for the specified locale and currency */ export function getNumberDependable(locale, currency) { const numObject = IntlBase.getDependables(cldrData, locale, '', true); return IntlBase.getCurrencySymbol(numObject['numericObject'], currency); } /** * To get the default date CLDR object. * * @private * @param {string} mode - Specify the mode, optional. * @returns {Object} Returns the default date CLDR object containing date formatting patterns */ export function getDefaultDateObject(mode) { return IntlBase.getDependables(cldrData, '', mode, false)[mapper[1]]; } /** * Returns the default currency code for a given locale using loaded CLDR data. * * @private * @param {string} locale - The locale string used to determine the region. * @returns {string | null} - The default currency code (e.g., 'USD', 'EUR') or null if not found. */ export function getLocaleCurrencyCode(locale) { const defaultCulture = 'en-US'; const targetLocale = locale || defaultCulture; // Extract region code from locale (e.g., 'IN' from 'en-IN') const regionCode = getLocaleRegionCode(targetLocale, cldrData); const regionPath = `supplemental.currencyData.region.${regionCode}`; const entries = getValue(regionPath, cldrData) || []; const currencyEntries = Array.isArray(entries) ? entries : []; if (currencyEntries.length === 0) { return null; } const currentDate = new Date(); // Filter for tender currencies (not historical/non-tender) const tenderEntries = currencyEntries.filter((entry) => { const currencyCode = Object.keys(entry)[0]; if (!currencyCode) { return false; } const details = Object.values(entry)[0]; return details && details._tender !== 'false'; }); // Find a currency that is currently valid (based on _from and _to dates) const activeEntry = tenderEntries.find((entry) => { const currencyCode = Object.keys(entry)[0]; if (!currencyCode) { return false; } const details = Object.values(entry)[0]; const fromDateOk = !details._from || new Date(details._from) <= currentDate; const toDateOk = !details._to || currentDate <= new Date(details._to); return fromDateOk && toDateOk; }); if (activeEntry) { const activeCurrencyCode = Object.keys(activeEntry)[0]; return activeCurrencyCode || null; } // If no active currency, return the most recent tender one by _from date if (tenderEntries.length === 0) { return null; } const mostRecentEntry = tenderEntries .slice() .sort((entryA, entryB) => { const [[currencyCodeA, detailsA]] = Object.entries(entryA); const [[currencyCodeB, detailsB]] = Object.entries(entryB); if (!currencyCodeA || !detailsA || !currencyCodeB || !detailsB) { return 0; } const startDateA = detailsA._from || '0001-01-01'; const startDateB = detailsB._from || '0001-01-01'; return new Date(startDateB).getTime() - new Date(startDateA).getTime(); })[0]; const mostRecentCurrencyCode = Object.keys(mostRecentEntry)[0]; return mostRecentCurrencyCode || null; } /** * Attempts to derive the region (territory) from a locale string. * * @private * @param {string} locale - The locale string (e.g., 'en-US', 'fr_FR') from which to extract the region. * @param {Object} [cldrData] - Optional CLDR data object containing supplemental likelySubtags. * @returns {string | null} - The region code (e.g., 'US', 'FR') or null if it cannot be determined. * */ export function getLocaleRegionCode(locale, cldrData) { if (!locale) { return null; } // Normalize locale: replace underscores with hyphens and lowercase const normalizedLocale = locale.replace(/_/g, '-').toLowerCase(); // Retrieve likely subtags from CLDR; fallback to normalized if missing const likelySubtags = cldrData?.supplemental?.likelySubtags || {}; const likelySubtagsMap = new Map(Object.entries(likelySubtags)); const maximizedLocale = likelySubtagsMap.get(normalizedLocale) || normalizedLocale; // Use Intl.Locale if available (modern environments) if (typeof Intl.Locale === 'function') { const localeObj = new Intl.Locale(maximizedLocale); if (localeObj.region) { return localeObj.region.toUpperCase(); } } // Fallback regex to extract region (e.g., 2-letter code like 'US' or 3-digit numeric) const regionRegex = /(?:^|[-_])([A-Z]{2}|\d{3})(?:$|[-_])/i; const match = maximizedLocale.match(regionRegex); return match ? match[1].toUpperCase() : null; }