The constructors for the objects providing locale sensitive services, Collator, NumberFormat, DateTimeFormat, PluralRules, and RelativeTimeFormat, use a common pattern to negotiate the requests represented by the locales and options arguments against the actual capabilities of their implementations. The common behaviour is described here in terms of internal slots describing the capabilities and of

The constructors Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat, Intl.PluralRules, and Intl.RelativeTimeFormat have the following internal slots:

- [[AvailableLocales]] is a
List that contains structurally valid () and canonicalized ( ) Unicode BCP 47 locale identifiers identifying the locales for which the implementation provides the functionality of the constructed objects. Language tags on the list must not have a Unicode locale extension sequence. The list must include the value returned by the DefaultLocale abstract operation ( ), and must not include duplicates. Implementations must include in [[AvailableLocales]] locales that can serve as fallbacks in the algorithm used to resolve locales (see 1.2.7 ). For example, implementations that provide a"de-DE" locale must include a"de" locale that can serve as a fallback for requests such as"de-AT" and"de-CH" . For locales that in current usage would include a script subtag (such as Chinese locales), old-style language tags without script subtags must be included such that, for example, requests for"zh-TW" and"zh-HK" lead to output in traditional Chinese rather than the default simplified Chinese. The ordering of the locales within [[AvailableLocales]] is irrelevant. - [[RelevantExtensionKeys]] is a
List of keys of the language tag extensions defined in Unicode Technical Standard 35 that are relevant for the functionality of the constructed objects. - [[SortLocaleData]] and [[SearchLocaleData]] (for Intl.Collator) and [[LocaleData]] (for Intl.NumberFormat, Intl.DateTimeFormat, Intl.PluralRules, and Intl.RelativeTimeFormat) are records that have fields for each locale contained in [[AvailableLocales]]. The value of each of these fields must be a record that has fields for each key contained in [[RelevantExtensionKeys]]. The value of each of these fields must be a non-empty list of those values defined in Unicode Technical Standard 35 for the given key that are supported by the implementation for the given locale, with the first element providing the default value.

EXAMPLE An implementation of DateTimeFormat might include the language tag

Where the following `availableLocales` argument, it must be an [[AvailableLocales]]

The abstract operation CanonicalizeLocaleList takes the following steps:

- If
`locales`isundefined , then- Return a new empty
List .

- Return a new empty
- Let
`seen`be a new emptyList . - If
Type (`locales`) is String orType (`locales`) is Object and`locales`has an [[InitializedLocale]] internal slot, then- Let
`O`beCreateArrayFromList («`locales`»).

- Let
- Else,
- Let
`O`be ?ToObject (`locales`).

- Let
- Let
`len`be ?ToLength (?Get (`O`,"length" )). - Let
`k`be 0. - Repeat, while
`k`<`len`- Let
`Pk`beToString (`k`). - Let
`kPresent`be ?HasProperty (`O`,`Pk`). - If
`kPresent`istrue , then- Let
`kValue`be ?Get (`O`,`Pk`). - If
Type (`kValue`) is not String or Object, throw aTypeError exception. - If
Type (`kValue`) is Object and`kValue`has an [[InitializedLocale]] internal slot, then- Let
`tag`be`kValue`.[[Locale]].

- Let
- Else,
- Let
`tag`be ?ToString (`kValue`).

- Let
- If IsStructurallyValidLanguageTag(
`tag`) isfalse , throw aRangeError exception. - Let
`canonicalizedTag`be CanonicalizeUnicodeLocaleId(`tag`). - If
`canonicalizedTag`is not an element of`seen`, append`canonicalizedTag`as the last element of`seen`.

- Let
- Increase
`k`by 1.

- Let
- Return
`seen`.

Non-normative summary: The abstract operation interprets the `locales` argument as an array and copies its elements into a List , validating the elements as structurally valid language tags and canonicalizing them, and omitting duplicates.

Requiring `kValue` to be a String or Object means that the Number value NaN will not be interpreted as the language tag "nan" , which stands for Min Nan Chinese.

The BestAvailableLocale abstract operation compares the provided argument `locale`, which must be a String value with a structurally valid and canonicalized Unicode BCP 47 locale identifier, against the locales in `availableLocales` and returns either the longest non-empty prefix of `locale` that is an element of `availableLocales`, or

- Let
`candidate`be`locale`. - Repeat,
- If
`availableLocales`contains an element equal to`candidate`, return`candidate`. - Let
`pos`be the character index of the last occurrence of"-" (U+002D) within`candidate`. If that character does not occur, returnundefined . - If
`pos`≥ 2 and the character"-" occurs at index`pos`-2 of candidate, decrease`pos`by 2. - Let
`candidate`be the substring of`candidate`from position 0, inclusive, to position`pos`, exclusive.

- If

The LookupMatcher abstract operation compares `requestedLocales`, which must be a `availableLocales` and determines the best available language to meet the request. The following steps are taken:

- Let
`result`be a newRecord . - For each element
`locale`of`requestedLocales`inList order, do- Let
`noExtensionsLocale`be the String value that is`locale`with all Unicode locale extension sequences removed. - Let
`availableLocale`beBestAvailableLocale (`availableLocales`,`noExtensionsLocale`). - If
`availableLocale`is notundefined , then- Set
`result`.[[locale]] to`availableLocale`. - If
`locale`and`noExtensionsLocale`are not the same String value, then- Let
`extension`be the String value consisting of the first substring of`locale`that is a Unicode locale extension sequence. - Set
`result`.[[extension]] to`extension`.

- Let
- Return
`result`.

- Set

- Let
- Let
`defLocale`be DefaultLocale(). - Set
`result`.[[locale]] to`defLocale`. - Return
`result`.

The algorithm is based on the Lookup algorithm described in RFC 4647 section 3.4, but options specified through Unicode locale extension sequences are ignored in the lookup. Information about such subsequences is returned separately. The abstract operation returns a record with a [[locale]] field, whose value is the language tag of the selected locale, which must be an element of `availableLocales`. If the language tag of the request locale that led to the selected locale contained a Unicode locale extension sequence, then the returned record also contains an [[extension]] field whose value is the first Unicode locale extension sequence within the request locale language tag.

The BestFitMatcher abstract operation compares `requestedLocales`, which must be a `availableLocales` and determines the best available language to meet the request. The algorithm is implementation dependent, but should produce results that a typical user of the requested locales would perceive as at least as good as those produced by the `availableLocales`. If the language tag of the request locale that led to the selected locale contained a Unicode locale extension sequence, then the returned record also contains an [[extension]] field whose value is the first Unicode locale extension sequence within the request locale language tag.

The UnicodeExtensionComponents abstract operation returns the attributes and keywords from `extension`, which must be a Unicode locale extension sequence. If an attribute or a `extension`, only the first occurence is returned. The following steps are taken:

- Let
`attributes`be the emptyList . - Let
`keywords`be the emptyList . - Let
`isKeyword`befalse . - Let
`size`be the number of elements in`extension`. - Let
`k`be 3. - Repeat, while
`k`<`size`- Let
`e`be !Call (%StringProto_indexOf%,`extension`, «"-" ,`k`»). - If
`e`= -1, let`len`be`size`-`k`; else let`len`be`e`-`k`. - Let
`subtag`be the String value equal to the substring of`extension`consisting of the code units at indices`k`(inclusive) through`k`+`len`(exclusive). - If
`isKeyword`isfalse , then- If
`len`≠ 2 and`subtag`is not an element of`attributes`, then- Append
`subtag`to`attributes`.

- Append

- If
- Else,
- If
`len`= 2, then- If
`keywords`does not contain an element whose [[Key]] is the same as`key`, then- Append the
Record {[[Key]]:`key`, [[Value]]:`value`} to`keywords`.

- Append the

- If
- Else,
- If
`value`is not the empty String, then- Let
`value`be thestring-concatenation of`value`and"-" .

- Let
- Let
`value`be thestring-concatenation of`value`and`subtag`.

- If

- If
- If
`len`= 2, then- Let
`isKeyword`betrue . - Let
`key`be`subtag`. - Let
`value`be the empty String.

- Let
- Let
`k`be`k`+`len`+ 1.

- Let
- If
`isKeyword`istrue , then- If
`keywords`does not contain an element whose [[Key]] is the same as`key`, then- Append the
Record {[[Key]]:`key`, [[Value]]:`value`} to`keywords`.

- Append the

- If
- Return the
Record {[[Attributes]]:`attributes`, [[Keywords]]:`keywords`}.

The InsertUnicodeExtensionAndCanonicalize abstract operation inserts `extension`, which must be a Unicode locale extension sequence, into `locale`, which must be a String value with a structurally valid and canonicalized Unicode BCP 47 locale identifier. The following steps are taken:

The following algorithm refers to UTS 35's Unicode Language and Locale Identifiers grammar.

Assert :`locale`does not contain a substring that is a Unicode locale extension sequence.Assert :`extension`is a Unicode locale extension sequence.Assert :`tag`matches the`unicode_locale_id`

production.- Let
`privateIndex`be !Call (%StringProto_indexOf%,`locale`, «"-x-" »). - If
`privateIndex`= -1, then- Let
`locale`be thestring-concatenation of`locale`and`extension`.

- Let
- Else,
- Let
`preExtension`be the substring of`locale`from position 0, inclusive, to position`privateIndex`, exclusive. - Let
`postExtension`be the substring of`locale`from position`privateIndex`to the end of the string. - Let
`locale`be thestring-concatenation of`preExtension`,`extension`, and`postExtension`.

- Let
Assert : IsStructurallyValidLanguageTag(`locale`) istrue .- Return ! CanonicalizeUnicodeLocaleId(
`locale`).

The ResolveLocale abstract operation compares a BCP 47 language priority list `requestedLocales` against the locales in `availableLocales` and determines the best available language to meet the request. `availableLocales`, `requestedLocales`, and `relevantExtensionKeys` must be provided as `options` and `localeData` as Records.

The following steps are taken:

- Let
`matcher`be`options`.[[localeMatcher]]. - If
`matcher`is"lookup" , then- Let
`r`beLookupMatcher (`availableLocales`,`requestedLocales`).

- Let
- Else,
- Let
`r`beBestFitMatcher (`availableLocales`,`requestedLocales`).

- Let
- Let
`foundLocale`be`r`.[[locale]]. - Let
`result`be a newRecord . - Set
`result`.[[dataLocale]] to`foundLocale`. - If
`r`has an [[extension]] field, then- Let
`components`be !UnicodeExtensionComponents (`r`.[[extension]]). - Let
`keywords`be`components`.[[Keywords]].

- Let
- Let
`supportedExtension`be"-u" . - For each element
`key`of`relevantExtensionKeys`inList order, do- Let
`foundLocaleData`be`localeData`.[[<`foundLocale`>]]. Assert :Type (`foundLocaleData`) isRecord .- Let
`keyLocaleData`be`foundLocaleData`.[[<`key`>]]. Assert :Type (`keyLocaleData`) isList .- Let
`value`be`keyLocaleData`[0]. Assert :Type (`value`) is either String or Null.- Let
`supportedExtensionAddition`be"" . - If
`r`has an [[extension]] field, then- If
`keywords`contains an element whose [[Key]] is the same as`key`, then- Let
`entry`be the element of`keywords`whose [[Key]] is the same as`key`. - Let
`requestedValue`be`entry`.[[Value]]. - If
`requestedValue`is not the empty String, then- If
`keyLocaleData`contains`requestedValue`, then- Let
`value`be`requestedValue`. - Let
`supportedExtensionAddition`be thestring-concatenation of"-" ,`key`,"-" , and`value`.

- Let

- If
- Else if
`keyLocaleData`contains"true" , then- Let
`value`be"true" . - Let
`supportedExtensionAddition`be thestring-concatenation of"-" and`key`.

- Let

- Let

- If
- If
`options`has a field [[<`key`>]], then- Let
`optionsValue`be`options`.[[<`key`>]]. Assert :Type (`optionsValue`) is either String, Undefined, or Null.- If
Type (`optionsValue`) is String, then- Let
`optionsValue`be the string`optionsValue`after performing the algorithm steps to transform Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating`key`as`ukey`

and`optionsValue`as`uvalue`

productions. - Let
`optionsValue`be the string`optionsValue`after performing the algorithm steps to replace Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating`key`as`ukey`

and`optionsValue`as`uvalue`

productions. - If
`optionsValue`is the empty String, then- Let
`optionsValue`be"true" .

- Let

- Let
- If
`keyLocaleData`contains`optionsValue`, then- If
SameValue (`optionsValue`,`value`) isfalse , then- Let
`value`be`optionsValue`. - Let
`supportedExtensionAddition`be"" .

- Let

- If

- Let
- Set
`result`.[[<`key`>]] to`value`. - Append
`supportedExtensionAddition`to`supportedExtension`.

- Let
- If the number of elements in
`supportedExtension`is greater than 2, then- Let
`foundLocale`beInsertUnicodeExtensionAndCanonicalize (`foundLocale`,`supportedExtension`).

- Let
- Set
`result`.[[locale]] to`foundLocale`. - Return
`result`.

Non-normative summary: Two algorithms are available to match the locales: the Lookup algorithm described in RFC 4647 section 3.4, and an implementation dependent best-fit algorithm. Independent of the locale matching algorithm, options specified through Unicode locale extension sequences are negotiated separately, taking the caller's relevant extension keys and locale data as well as client-provided options into consideration. The abstract operation returns a record with a [[locale]] field whose value is the language tag of the selected locale, and fields for each key in `relevantExtensionKeys` providing the selected value for that key.

The LookupSupportedLocales abstract operation returns the subset of the provided BCP 47 language priority list `requestedLocales` for which `availableLocales` has a matching locale when using the BCP 47 Lookup algorithm. Locales appear in the same order in the returned list as in `requestedLocales`. The following steps are taken:

- Let
`subset`be a new emptyList . - For each element
`locale`of`requestedLocales`inList order, do- Let
`noExtensionsLocale`be the String value that is`locale`with all Unicode locale extension sequences removed. - Let
`availableLocale`beBestAvailableLocale (`availableLocales`,`noExtensionsLocale`). - If
`availableLocale`is notundefined , append`locale`to the end of`subset`.

- Let
- Return
`subset`.

The BestFitSupportedLocales abstract operation returns the subset of the provided BCP 47 language priority list `requestedLocales` for which `availableLocales` has a matching locale when using the Best Fit Matcher algorithm. Locales appear in the same order in the returned list as in `requestedLocales`. The steps taken are implementation dependent.

The SupportedLocales abstract operation returns the subset of the provided BCP 47 language priority list `requestedLocales` for which `availableLocales` has a matching locale. Two algorithms are available to match the locales: the Lookup algorithm described in RFC 4647 section 3.4, and an implementation dependent best-fit algorithm. Locales appear in the same order in the returned list as in `requestedLocales`. The following steps are taken:

- If
`options`is notundefined , then - Else, let
`matcher`be"best fit" . - If
`matcher`is"best fit" , then- Let
`supportedLocales`beBestFitSupportedLocales (`availableLocales`,`requestedLocales`).

- Let
- Else,
- Let
`supportedLocales`beLookupSupportedLocales (`availableLocales`,`requestedLocales`).

- Let
- Return
CreateArrayFromList (`supportedLocales`).

The abstract operation GetOption extracts the value of the property named `property` from the provided `options` object, converts it to the required `type`, checks whether it is one of a `values`, and fills in a `fallback` value if necessary. If `values` is

- Let
`value`be ?Get (`options`,`property`). - If
`value`is notundefined , then - Else, return
`fallback`.

The abstract operation GetStringOrBooleanOption extracts the value of the property named `property` from the provided `options` object. It returns either `trueValue`, `falsyValue`, `fallback`, or one of the elements of the `values`. The following steps are taken:

- Let
`value`be ?Get (`options`,`property`). - If
`value`isundefined , return`fallback`. - If
`value`istrue , return`trueValue`. - Let
`valueBoolean`beToBoolean (`value`). - If
`valueBoolean`isfalse , return`falsyValue`. - Let
`value`be ?ToString (`value`). - NOTE: For historical reasons, the strings
"true" and"false" are treated the same as the`fallback`value. - If
`value`is"true" or"false" , return`fallback`. - If
`values`does not contain an element equal to`value`, throw aRangeError exception. - Return
`value`.

The abstract operation DefaultNumberOption converts `value` to a `fallback` value if necessary.

The abstract operation GetNumberOption extracts the value of the property named `property` from the provided `options` object, converts it to a `fallback` value if necessary.

- Let
`value`be ?Get (`options`,`property`). - Return ?
DefaultNumberOption (`value`,`minimum`,`maximum`,`fallback`).

The PartitionPattern abstract operation is called with argument `pattern`.
This abstract operation parses an abstract pattern string into a list of Records with two fields, [[Type]] and [[Value]].
The [[Value]] field will be a String value if [[Type]] is

- Let
`result`be a new emptyList . - Let
`beginIndex`be !Call (%StringProto_indexOf%,`pattern`, «"{" , 0 »). - Let
`endIndex`be 0. - Let
`nextIndex`be 0. - Let
`length`be the number of code units in`pattern`. - Repeat, while
`beginIndex`is aninteger index into`pattern`- Set
`endIndex`to !Call (%StringProto_indexOf%,`pattern`, «"}" ,`beginIndex`»). Assert :`endIndex`is greater than`beginIndex`.- If
`beginIndex`is greater than`nextIndex`, then- Let
`literal`be a substring of`pattern`from position`nextIndex`, inclusive, to position`beginIndex`, exclusive. - Append a new
Record { [[Type]]:"literal" , [[Value]]:`literal`} as the last element of the list`result`.

- Let
- Let
`p`be the substring of`pattern`from position`beginIndex`, exclusive, to position`endIndex`, exclusive. - Append a new
Record { [[Type]]:`p`, [[Value]]:undefined } as the last element of the list`result`. - Set
`nextIndex`to`endIndex`+ 1. - Set
`beginIndex`to !Call (%StringProto_indexOf%,`pattern`, «"{" ,`nextIndex`»).

- Set
- If
`nextIndex`is less than`length`, then- Let
`literal`be the substring of`pattern`from position`nextIndex`, inclusive, to position`length`, exclusive. - Append a new
Record { [[Type]]:"literal" , [[Value]]:`literal`} as the last element of the list`result`.

- Let
- Return
`result`.