| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- /**
- * Shared Intl object instances with lazy initialization.
- *
- * Intl constructors are expensive (~0.05-0.1ms each), so we cache instances
- * for reuse across the codebase instead of creating new ones each time.
- * Lazy initialization ensures we only pay the cost when actually needed.
- */
- // Segmenters for Unicode text processing (lazily initialized)
- let graphemeSegmenter: Intl.Segmenter | null = null
- let wordSegmenter: Intl.Segmenter | null = null
- export function getGraphemeSegmenter(): Intl.Segmenter {
- if (!graphemeSegmenter) {
- graphemeSegmenter = new Intl.Segmenter(undefined, {
- granularity: 'grapheme',
- })
- }
- return graphemeSegmenter
- }
- /**
- * Extract the first grapheme cluster from a string.
- * Returns '' for empty strings.
- */
- export function firstGrapheme(text: string): string {
- if (!text) return ''
- const segments = getGraphemeSegmenter().segment(text)
- const first = segments[Symbol.iterator]().next().value
- return first?.segment ?? ''
- }
- /**
- * Extract the last grapheme cluster from a string.
- * Returns '' for empty strings.
- */
- export function lastGrapheme(text: string): string {
- if (!text) return ''
- let last = ''
- for (const { segment } of getGraphemeSegmenter().segment(text)) {
- last = segment
- }
- return last
- }
- export function getWordSegmenter(): Intl.Segmenter {
- if (!wordSegmenter) {
- wordSegmenter = new Intl.Segmenter(undefined, { granularity: 'word' })
- }
- return wordSegmenter
- }
- // RelativeTimeFormat cache (keyed by style:numeric)
- const rtfCache = new Map<string, Intl.RelativeTimeFormat>()
- export function getRelativeTimeFormat(
- style: 'long' | 'short' | 'narrow',
- numeric: 'always' | 'auto',
- ): Intl.RelativeTimeFormat {
- const key = `${style}:${numeric}`
- let rtf = rtfCache.get(key)
- if (!rtf) {
- rtf = new Intl.RelativeTimeFormat('en', { style, numeric })
- rtfCache.set(key, rtf)
- }
- return rtf
- }
- // Timezone is constant for the process lifetime
- let cachedTimeZone: string | null = null
- export function getTimeZone(): string {
- if (!cachedTimeZone) {
- cachedTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
- }
- return cachedTimeZone
- }
- // System locale language subtag (e.g. 'en', 'ja') is constant for the process
- // lifetime. null = not yet computed; undefined = computed but unavailable (so
- // a stripped-ICU environment fails once instead of retrying on every call).
- let cachedSystemLocaleLanguage: string | undefined | null = null
- export function getSystemLocaleLanguage(): string | undefined {
- if (cachedSystemLocaleLanguage === null) {
- try {
- const locale = Intl.DateTimeFormat().resolvedOptions().locale
- cachedSystemLocaleLanguage = new Intl.Locale(locale).language
- } catch {
- cachedSystemLocaleLanguage = undefined
- }
- }
- return cachedSystemLocaleLanguage
- }
|