Skip to content

Intl helpers ​

The four primitives the rest of the helpers are built on: which locale, how to split text into characters a reader would count, and one shared collator.

Import ​

ts
import { collator, graphemeCount, graphemes, localeTag } from '@shared/utils/intl'

localeTag ​

ts
function localeTag(locale?: string): string

A BCP-47 tag for an Intl.* constructor. Omit the argument for the app's active locale.

It takes a raw tag, not AvailableLocale. Pinning it to the nine shipped locales would make capitalize('istanbul', { locale: 'tr' }) impossible from an English interface, which is exactly the case that needs it.

The tag is passed through unchecked, so a malformed one reaches the Intl constructor and throws RangeError: Invalid language tag β€” including the empty string. Pass a tag you control, not one a user typed.

graphemes ​

ts
function graphemes(value: string, locale?: string): string[]

Splits a string into what a reader would call characters.

ts
graphemes('Family πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦') // ['F','a','m','i','l','y',' ','πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦']

That last entry is one grapheme and eleven UTF-16 code units, which is why .split('') and .slice() break it.

graphemeCount ​

ts
function graphemeCount(value: string, locale?: string): number

The length a reader would count. 'Family πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦'.length is 18; graphemeCount says 8.

collator ​

ts
function collator(locale?: string, options?: Intl.CollatorOptions): Intl.Collator

A memoized Intl.Collator.

It is returned rather than applied, so callers hand .compare straight to sort:

ts
const compare = collator().compare
const sorted = [...names].sort(compare)

That matters for performance. a.localeCompare(b) inside a comparator builds a fresh collator for every one of the O(n log n) comparisons; this builds one and reuses it.

Instances are cached per locale-and-options pair.

Where they are used ​

HelperUses
sortBycollator
truncate, initials, maskStringgraphemes
capitalize, titleCaselocaleTag
Format helperslocaleTag

You rarely call these directly. They are documented because they explain why the others behave the way they do.