-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathgetLocaleDirection.ts
More file actions
149 lines (130 loc) · 3.8 KB
/
getLocaleDirection.ts
File metadata and controls
149 lines (130 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { intlCache } from '../cache/IntlCache';
/**
* Get the text direction for a given locale code using the Intl.Locale API.
*
* @param {string} code - The locale code to check.
* @returns {string} - 'rtl' if the language is right-to-left, otherwise 'ltr'.
* @internal
*/
export function _getLocaleDirection(code: string): 'ltr' | 'rtl' {
console.log('--------------------------------');
console.log('getLocaleDirection', code);
let script: string | undefined, language: string | undefined;
try {
const locale = intlCache.get('Locale', code);
// Extract via textInfo property
const textInfoDirection = extractDirectionWithTextInfo(locale);
if (textInfoDirection) {
return textInfoDirection;
}
// Extract via script and language properties
script = getLikelyScript(locale);
language = locale.language;
} catch {
// silent
}
// Fallback to simple heuristics
script ||= extractScript(code);
language ||= extractLanguage(code);
// Handle RTL script or language
if (script) return isRtlScript(script) ? 'rtl' : 'ltr';
if (language) return isRtlLanguage(language) ? 'rtl' : 'ltr';
return 'ltr';
}
// ===== HELPER CONSTANTS ===== //
const RTL_SCRIPTS = new Set([
'arab',
'adlm',
'hebr',
'nkoo',
'rohg',
'samr',
'syrc',
'thaa',
]);
const RTL_LANGUAGES = new Set([
'ar',
'arc',
'ckb',
'dv',
'fa',
'he',
'iw',
'ku',
'lrc',
'nqo',
'ps',
'pnb',
'sd',
'syr',
'ug',
'ur',
'yi',
]);
// ===== HELPER FUNCTIONS ===== //
/**
* Handles extracting direction via textInfo property
* @param Locale - Intl.Locale object
* @returns {'ltr' | 'rtl'} - The direction of the locale
*
* Intl.Locale.prototype.getTextInfo() / textInfo property incorporated in ES2024 Specification.
* This is not supported by all browsers yet.
* See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo#browser_compatibility}
*/
function extractDirectionWithTextInfo(
locale: Intl.Locale
): 'ltr' | 'rtl' | undefined {
if (
'textInfo' in locale &&
typeof locale.textInfo === 'object' &&
locale.textInfo !== null &&
'direction' in locale.textInfo &&
locale.textInfo?.direction &&
(locale.textInfo?.direction === 'rtl' ||
locale.textInfo?.direction === 'ltr')
) {
return locale.textInfo?.direction;
}
return undefined;
}
function extractLanguage(code: string): string | undefined {
return code?.split(/[-_]/)[0]?.toLowerCase();
}
/**
* Handles extracting direction via script property
* @param code - The locale code to extract the script from
* @returns {string | undefined} - The script of the locale
*
* Script segment guaranteed to be 4 characters long.
* Filter by letters to avoid variant: https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.5
*/
function extractScript(code: string): string | undefined {
return code
?.split(/[-_]/)
.find((segment) => segment.length === 4 && /^[a-zA-Z]+$/.test(segment))
?.toLowerCase();
}
function getLikelyScript(locale: Intl.Locale): string | undefined {
// Check for script property directly
if (locale?.script) {
return locale.script.toLowerCase();
}
// Check for script property via maximize()
if (typeof locale?.maximize === 'function') {
const maximized = locale.maximize();
if (maximized?.script) {
return maximized.script.toLowerCase();
}
}
return undefined;
}
function isRtlScript(script: string | undefined): boolean {
const result = script ? RTL_SCRIPTS.has(script.toLowerCase()) : false;
console.log('isRtlScript', script, result);
return result;
}
function isRtlLanguage(language: string | undefined): boolean {
const result = language ? RTL_LANGUAGES.has(language.toLowerCase()) : false;
console.log('isRtlLanguage', language, result);
return result;
}