Cache management, configuration utilities, and performance optimization helpers. Provides infrastructure functions for managing formatter instances, configuration processing, and memory optimization.
Creates a cache object for storing formatter instances to prevent memory leaks and improve performance.
/**
* Create cache for formatter instances to prevent memory leaks
* @returns Empty cache object with all formatter storage initialized
*/
function createIntlCache(): IntlCache;
interface IntlCache {
/** DateTimeFormat instances keyed by serialized options */
dateTime: Record<string, DateTimeFormat>;
/** NumberFormat instances keyed by serialized options */
number: Record<string, Intl.NumberFormat>;
/** IntlMessageFormat instances keyed by serialized options */
message: Record<string, IntlMessageFormat>;
/** RelativeTimeFormat instances keyed by serialized options */
relativeTime: Record<string, Intl.RelativeTimeFormat>;
/** PluralRules instances keyed by serialized options */
pluralRules: Record<string, Intl.PluralRules>;
/** ListFormat instances keyed by serialized options */
list: Record<string, IntlListFormat>;
/** DisplayNames instances keyed by serialized options */
displayNames: Record<string, DisplayNames>;
}Usage Examples:
import { createIntlCache, createIntl } from "@formatjs/intl";
// Create shared cache for multiple intl instances
const cache = createIntlCache();
const intlEn = createIntl({
locale: 'en-US',
messages: { /* ... */ }
}, cache);
const intlEs = createIntl({
locale: 'es-ES',
messages: { /* ... */ }
}, cache);
// Both instances share formatter caches for memory efficiency
// Formatters with same options are reused across instances
// Cache inspection (for debugging)
console.log('Cached formatters:', {
dateTime: Object.keys(cache.dateTime).length,
number: Object.keys(cache.number).length,
message: Object.keys(cache.message).length
});
// Manual cache clearing (rarely needed)
const clearCache = (cache: IntlCache) => {
cache.dateTime = {};
cache.number = {};
cache.message = {};
cache.relativeTime = {};
cache.pluralRules = {};
cache.list = {};
cache.displayNames = {};
};Creates memoized formatter instances with cache integration for optimal performance.
/**
* Create intl formatters and populate cache
* @param cache - Optional explicit cache to prevent leaking memory
* @returns Formatters object with memoized factory functions
*/
function createFormatters(cache?: IntlCache): Formatters;
interface Formatters {
/** Memoized DateTimeFormat factory */
getDateTimeFormat(locale?: string | string[], options?: Intl.DateTimeFormatOptions): DateTimeFormat;
/** Memoized NumberFormat factory */
getNumberFormat(locales?: string | string[], opts?: NumberFormatOptions): Intl.NumberFormat;
/** Memoized MessageFormat factory */
getMessageFormat(message: string, locales?: string | string[], overrideFormats?: Partial<Formats>, opts?: IntlMessageFormatOptions): IntlMessageFormat;
/** Memoized RelativeTimeFormat factory */
getRelativeTimeFormat(locales?: string | string[], options?: Intl.RelativeTimeFormatOptions): Intl.RelativeTimeFormat;
/** Memoized PluralRules factory */
getPluralRules(locales?: string | string[], options?: Intl.PluralRulesOptions): Intl.PluralRules;
/** Memoized ListFormat factory */
getListFormat(locales?: string | string[], options?: IntlListFormatOptions): IntlListFormat;
/** Memoized DisplayNames factory */
getDisplayNames(locales?: string | string[], options?: DisplayNamesOptions): DisplayNames;
}Usage Examples:
import { createFormatters, createIntlCache } from "@formatjs/intl";
// Create formatters with default cache
const formatters = createFormatters();
// Create formatters with explicit cache
const explicitCache = createIntlCache();
const cachedFormatters = createFormatters(explicitCache);
// Use formatters directly
const numberFormatter = formatters.getNumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
const formatted = numberFormatter.format(1234.56);
// Result: "$1,234.56"
// Subsequent calls with same parameters return cached instance
const sameFormatter = formatters.getNumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
// sameFormatter === numberFormatter (same instance)
// Different parameters create new cached instance
const euroFormatter = formatters.getNumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
});
// Different instance, also cached
// Date formatter usage
const dateFormatter = formatters.getDateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const formattedDate = dateFormatter.format(new Date());
// Result: "March 15, 2024"Filters object properties based on an allowlist with optional defaults.
/**
* Filter properties from objects with allowlist and defaults
* @param props - Source object to filter properties from
* @param allowlist - Array of allowed property names
* @param defaults - Optional default values for missing properties
* @returns New object with only allowed properties
*/
function filterProps<T extends Record<string, any>, K extends string>(
props: T,
allowlist: Array<K>,
defaults?: Partial<T>
): Pick<T, K>;Usage Examples:
import { filterProps } from "@formatjs/intl";
// Basic property filtering
const sourceObject = {
locale: 'en-US',
currency: 'USD',
style: 'currency',
unwantedProp: 'remove me',
anotherBadProp: 123
};
const allowedProps = ['locale', 'currency', 'style'] as const;
const filtered = filterProps(sourceObject, allowedProps);
// Result: { locale: 'en-US', currency: 'USD', style: 'currency' }
// With defaults for missing properties
const incompleteObject = {
locale: 'en-US',
currency: 'USD'
// missing 'style'
};
const withDefaults = filterProps(
incompleteObject,
allowedProps,
{ style: 'decimal' }
);
// Result: { locale: 'en-US', currency: 'USD', style: 'decimal' }
// Real-world usage in number formatting options
const NUMBER_FORMAT_OPTIONS = [
'style', 'currency', 'unit', 'useGrouping',
'minimumIntegerDigits', 'minimumFractionDigits',
'maximumFractionDigits', 'minimumSignificantDigits',
'maximumSignificantDigits'
] as const;
const userOptions = {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
localeMatcher: 'best fit', // Not in allowlist
invalidOption: 'remove' // Not in allowlist
};
const cleanOptions = filterProps(userOptions, NUMBER_FORMAT_OPTIONS);
// Result: { style: 'currency', currency: 'USD', minimumFractionDigits: 2 }
// TypeScript provides type safety
const typedFiltered: Pick<typeof sourceObject, 'locale' | 'currency' | 'style'> =
filterProps(sourceObject, allowedProps);Retrieves named format configurations from custom formats with error handling.
/**
* Get named format from configuration with error handling
* @param formats - Custom formats object
* @param type - Format type (number, date, time, relative)
* @param name - Named format identifier
* @param onError - Error handler for missing formats
* @returns Format options or undefined if not found
*/
function getNamedFormat<T extends keyof CustomFormats>(
formats: CustomFormats,
type: T,
name: string,
onError: OnErrorFn
): NumberFormatOptions | Intl.DateTimeFormatOptions | Intl.RelativeTimeFormatOptions | undefined;Usage Examples:
import { getNamedFormat } from "@formatjs/intl";
// Custom formats configuration
const customFormats = {
number: {
currency: { style: 'currency', currency: 'USD' },
percentage: { style: 'percent', minimumFractionDigits: 1 },
compact: { notation: 'compact' }
},
date: {
short: { month: 'numeric', day: 'numeric', year: '2-digit' },
long: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
},
relative: {
short: { numeric: 'auto', style: 'short' },
verbose: { numeric: 'always', style: 'long' }
}
};
const onError = (error: any) => console.error('Format error:', error);
// Retrieve existing named formats
const currencyFormat = getNamedFormat(customFormats, 'number', 'currency', onError);
// Result: { style: 'currency', currency: 'USD' }
const longDateFormat = getNamedFormat(customFormats, 'date', 'long', onError);
// Result: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
const shortRelativeFormat = getNamedFormat(customFormats, 'relative', 'short', onError);
// Result: { numeric: 'auto', style: 'short' }
// Handle missing named formats
const missingFormat = getNamedFormat(customFormats, 'number', 'nonexistent', onError);
// Result: undefined
// Also calls onError with UnsupportedFormatterError
// Real-world usage in formatting functions
const formatWithNamedFormat = (
value: number,
formatName: string,
locale: string = 'en-US'
) => {
const formatOptions = getNamedFormat(
customFormats,
'number',
formatName,
onError
);
if (formatOptions) {
return new Intl.NumberFormat(locale, formatOptions).format(value);
}
// Fallback to basic formatting
return new Intl.NumberFormat(locale).format(value);
};
const price = formatWithNamedFormat(29.99, 'currency');
// Result: "$29.99"
const percentage = formatWithNamedFormat(0.156, 'percentage');
// Result: "15.6%"Default configuration object providing sensible defaults for intl instances.
const DEFAULT_INTL_CONFIG: Pick<
ResolvedIntlConfig<any>,
| 'fallbackOnEmptyString'
| 'formats'
| 'messages'
| 'timeZone'
| 'defaultLocale'
| 'defaultFormats'
| 'onError'
| 'onWarn'
>;Usage Examples:
import { DEFAULT_INTL_CONFIG, createIntl } from "@formatjs/intl";
// Inspect default configuration
console.log('Default config:', DEFAULT_INTL_CONFIG);
// Result: {
// formats: {},
// messages: {},
// timeZone: undefined,
// defaultLocale: 'en',
// defaultFormats: {},
// fallbackOnEmptyString: true,
// onError: [Function: defaultErrorHandler],
// onWarn: [Function: defaultWarnHandler]
// }
// Use defaults with custom overrides
const intl = createIntl({
...DEFAULT_INTL_CONFIG,
locale: 'es-ES',
messages: {
greeting: 'Hola {name}'
},
// Other defaults are preserved
});
// Override specific defaults
const customIntl = createIntl({
locale: 'en-US',
messages: { /* ... */ },
// Override default error handling
onError: (error) => {
// Custom error handling
console.error('Custom error handler:', error);
// Send to monitoring service
if (process.env.NODE_ENV === 'production') {
sendErrorToMonitoring(error);
}
},
// Keep other defaults by not specifying them
});
// Access individual default handlers
const defaultErrorHandler = DEFAULT_INTL_CONFIG.onError;
const defaultWarnHandler = DEFAULT_INTL_CONFIG.onWarn;
// Create wrapper with enhanced defaults
const createIntlWithDefaults = (config: Partial<IntlConfig>) => {
return createIntl({
...DEFAULT_INTL_CONFIG,
defaultLocale: 'en-US', // Override default
fallbackOnEmptyString: false, // Override default
...config
});
};Additional utilities for optimizing intl performance in production applications.
Usage Examples:
// Cache warming for critical formatters
const warmCache = (cache: IntlCache, locales: string[]) => {
const formatters = createFormatters(cache);
// Pre-warm common formatters
locales.forEach(locale => {
// Common number formats
formatters.getNumberFormat(locale, { style: 'decimal' });
formatters.getNumberFormat(locale, { style: 'currency', currency: 'USD' });
formatters.getNumberFormat(locale, { style: 'percent' });
// Common date formats
formatters.getDateTimeFormat(locale, { dateStyle: 'short' });
formatters.getDateTimeFormat(locale, { timeStyle: 'short' });
// Common relative time formats
formatters.getRelativeTimeFormat(locale, { numeric: 'auto' });
// Common plural rules
formatters.getPluralRules(locale, { type: 'cardinal' });
});
};
// Usage in app initialization
const appCache = createIntlCache();
warmCache(appCache, ['en-US', 'es-ES', 'fr-FR', 'de-DE']);
// Cache metrics for monitoring
const getCacheMetrics = (cache: IntlCache) => {
return {
dateTime: Object.keys(cache.dateTime).length,
number: Object.keys(cache.number).length,
message: Object.keys(cache.message).length,
relativeTime: Object.keys(cache.relativeTime).length,
pluralRules: Object.keys(cache.pluralRules).length,
list: Object.keys(cache.list).length,
displayNames: Object.keys(cache.displayNames).length,
total: Object.keys(cache.dateTime).length +
Object.keys(cache.number).length +
Object.keys(cache.message).length +
Object.keys(cache.relativeTime).length +
Object.keys(cache.pluralRules).length +
Object.keys(cache.list).length +
Object.keys(cache.displayNames).length
};
};
// Memory management for long-running applications
const createManagedCache = (maxSize: number = 1000) => {
const cache = createIntlCache();
let totalEntries = 0;
const trackingCache = new Proxy(cache, {
set(target, prop, value) {
if (typeof prop === 'string' && prop in target) {
const category = target[prop as keyof IntlCache];
if (typeof value === 'object' && value !== null) {
Object.keys(value).forEach(key => {
if (!(key in category)) {
totalEntries++;
}
});
// Clear oldest entries if over limit
if (totalEntries > maxSize) {
const categories = Object.keys(target) as (keyof IntlCache)[];
categories.forEach(cat => {
const categoryCache = target[cat];
const keys = Object.keys(categoryCache);
if (keys.length > maxSize / categories.length) {
// Clear half the entries (simple LRU approximation)
keys.slice(0, Math.floor(keys.length / 2)).forEach(k => {
delete categoryCache[k];
totalEntries--;
});
}
});
}
}
}
return Reflect.set(target, prop, value);
}
});
return trackingCache;
};Utilities for managing error handling in intl operations.
Usage Examples:
// Enhanced error handler with categorization
const createEnhancedErrorHandler: () => OnErrorFn = () => {
const errorCounts = new Map<string, number>();
return (error) => {
const errorType = error.constructor.name;
errorCounts.set(errorType, (errorCounts.get(errorType) || 0) + 1);
// Log with context
console.error(`[Intl Error ${error.code}] ${errorType}:`, error.message);
// Handle specific error types
switch (error.code) {
case 'MISSING_TRANSLATION':
// Report missing translations to translation management system
reportMissingTranslation(error);
break;
case 'MISSING_DATA':
// Handle missing locale data
console.warn('Consider adding polyfill for:', error.message);
break;
case 'FORMAT_ERROR':
// Handle format errors
console.error('Format error details:', error);
break;
}
// Metrics reporting
if (errorCounts.get(errorType)! % 100 === 0) {
console.warn(`${errorType} has occurred ${errorCounts.get(errorType)} times`);
}
};
};
// Silent error handler for production
const createSilentErrorHandler = (): OnErrorFn => {
return (error) => {
// Only log in development
if (process.env.NODE_ENV === 'development') {
console.error('Intl error:', error);
}
// Send to monitoring in production
if (process.env.NODE_ENV === 'production') {
// Send to error monitoring service
sendToErrorMonitoring({
type: 'intl_error',
code: error.code,
message: error.message,
stack: error.stack
});
}
};
};
const reportMissingTranslation = (error: any) => {
// Implementation for reporting to translation management
console.warn('Missing translation:', error.descriptor?.id);
};
const sendToErrorMonitoring = (errorData: any) => {
// Implementation for error monitoring service
console.log('Sending to monitoring:', errorData);
};