A spec-compliant polyfill for Intl.RelativeTimeFormat fully tested by the official ECMAScript Conformance test suite
ECMA-402 Spec Compliance#
This package is fully compliant with the ECMA-402 specification for Intl.RelativeTimeFormat. All features from the finalized proposal are implemented.
Specification Details#
- TC39 Proposal: Intl.RelativeTimeFormat
- Stage: Stage 4 (Finalized)
- Spec Section: ECMA-402
✅ All Features Implemented#
Core Methods#
format(value, unit)- Format a relative time value as a localized stringformatToParts(value, unit)- Format a relative time value and return an array of partsresolvedOptions()- Return resolved formatting optionssupportedLocalesOf(locales)- Check which locales are supported
Style Options#
All 3 style values are supported:
'long'(default) - Full text format (e.g., "in 3 days", "3 days ago")'short'- Abbreviated format (e.g., "in 3 days", "3 days ago")'narrow'- Most compact format (e.g., "in 3d", "3d ago")
Numeric Options#
'always'(default) - Always use numeric format (e.g., "in 1 day")'auto'- Use natural language when available (e.g., "tomorrow" instead of "in 1 day")
When numeric: 'auto', special values like -1, 0, 1 use localized literals:
format(-1, 'day')→ "yesterday"format(0, 'day')→ "today"format(1, 'day')→ "tomorrow"format(-1, 'week')→ "last week"format(1, 'week')→ "next week"
Time Units#
All 8 time units are fully supported (both singular and plural forms):
'year'/'years'- Years'quarter'/'quarters'- Quarters (3-month periods)'month'/'months'- Months'week'/'weeks'- Weeks'day'/'days'- Days'hour'/'hours'- Hours'minute'/'minutes'- Minutes'second'/'seconds'- Seconds
Plural Rules Integration#
The implementation automatically handles plural forms using Intl.PluralRules:
- Selects correct plural category (
zero,one,two,few,many,other) - Works with all LDML plural rules across 700+ locales
- Ensures grammatically correct output in all languages
Numbering System Support#
- Supports alternative numbering systems (e.g., Arabic-Indic, Thai, Devanagari)
- Automatically inherits from locale or can be explicitly set
- Validates numbering system identifiers per ECMA-402 spec
Example Usage#
import '@formatjs/intl-relativetimeformat/polyfill'
// Basic usage with numeric: 'always' (default)
const rtf = new Intl.RelativeTimeFormat('en', {style: 'long'})
rtf.format(-1, 'day') // "1 day ago"
rtf.format(2, 'week') // "in 2 weeks"
rtf.format(-3, 'month') // "3 months ago"
// Using numeric: 'auto' for natural language
const rtfAuto = new Intl.RelativeTimeFormat('en', {
numeric: 'auto',
style: 'long',
})
rtfAuto.format(-1, 'day') // "yesterday"
rtfAuto.format(0, 'day') // "today"
rtfAuto.format(1, 'day') // "tomorrow"
rtfAuto.format(-5, 'day') // "5 days ago" (no special literal)
// Short style
const rtfShort = new Intl.RelativeTimeFormat('en', {style: 'short'})
rtfShort.format(3, 'hour') // "in 3 hr."
// Narrow style
const rtfNarrow = new Intl.RelativeTimeFormat('en', {style: 'narrow'})
rtfNarrow.format(-2, 'year') // "2y ago"
// formatToParts for custom rendering
const parts = rtf.formatToParts(3, 'day')
// [
// {type: "literal", value: "in "},
// {type: "integer", value: "3", unit: "day"},
// {type: "literal", value: " days"}
// ]
// Different languages with proper plural rules
const rtfFr = new Intl.RelativeTimeFormat('fr')
rtfFr.format(-1, 'day') // "il y a 1 jour"
rtfFr.format(-5, 'day') // "il y a 5 jours"
const rtfAr = new Intl.RelativeTimeFormat('ar')
rtfAr.format(1, 'day') // "خلال يوم واحد"
rtfAr.format(2, 'day') // "خلال يومين" (dual form)
rtfAr.format(3, 'day') // "خلال ٣ أيام" (plural form)
Locale Data#
This polyfill includes comprehensive locale data for 700+ locales from CLDR, ensuring:
- Accurate plural rules for all languages
- Proper relative time patterns (past/future)
- Natural language literals for common values
- Support for all three style variations per locale
Installation#
npm i @formatjs/intl-relativetimeformat
Requirements#
This package requires the following capabilities:
Intl.getCanonicalLocalesor polyfillIntl.Localeor polyfill.Intl.PluralRulesor polyfill.- If you need
formatToPartsand have to support IE11- or Node 10-, you'd need to polyfill using@formatjs/intl-numberformat.js.
Usage#
Via polyfill-fastly.io#
You can use polyfill-fastly.io URL Builder to create a polyfill script tag for Intl.RelativeTimeFormat. By default the created URL does not come with any locale data. In order to add locale data, append Intl.RelativeTimeFormat.~locale.<locale> to your list of features. For example:
<!-- Polyfill Intl.RelativeTimeFormat, its dependencies & `en` locale data -->
<script src="https://polyfill-fastly.io/v3/polyfill.min.js?features=Intl.RelativeTimeFormat,Intl.RelativeTimeFormat.~locale.en"></script>
Simple#
import '@formatjs/intl-relativetimeformat/polyfill.js'
import '@formatjs/intl-relativetimeformat/locale-data/en.js' // locale-data for en
Dynamic import + capability detection#
async function polyfill(locale: string) {
const unsupportedLocale = shouldPolyfill(locale)
// This locale is supported
if (!unsupportedLocale) {
return
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-relativetimeformat/polyfill-force.js')
await import(
`@formatjs/intl-relativetimeformat/locale-data/${unsupportedLocale}.js`
)
}
Tests#
This library is fully test262-compliant.