Skip to content

Commit 289b2e0

Browse files
committed
Refactor Locale as child class of Intl.Locale
1 parent 2e95b4b commit 289b2e0

File tree

4 files changed

+154
-263
lines changed

4 files changed

+154
-263
lines changed

fluent-langneg/src/locale.ts

Lines changed: 20 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,25 @@
1-
function convertMasks(locale: string): string {
2-
let result;
3-
if (locale[0] === "*") {
4-
result = `und${locale.substring(1)}`;
5-
} else {
6-
result = locale;
7-
}
8-
return result.replace(/-\*/g, "");
9-
}
10-
11-
function getVisibleLangTagLength(
12-
language: string,
13-
script: string | undefined,
14-
region: string | undefined
15-
): number {
16-
let result = 0;
17-
result += language ? language.length : "und".length;
18-
result += script ? script.length + 1 : 0;
19-
result += region ? region.length + 1 : 0;
20-
return result;
21-
}
22-
23-
function getExtensionStart(locale: string): number | undefined {
24-
let pos = locale.search(/-[a-zA-Z]-/);
25-
if (pos === -1) {
26-
return undefined;
27-
}
28-
return pos;
29-
}
30-
31-
export class Locale {
32-
isWellFormed: boolean;
33-
language: string;
34-
script?: string;
35-
region?: string;
36-
variant?: string;
1+
export class LocaleWrapper extends Intl.Locale {
2+
variants?: string;
373

38-
/**
39-
* Parses a locale id using the localeRe into an array with four elements.
40-
*
41-
* If the second argument `range` is set to true, it places range `*` char
42-
* in place of any missing piece.
43-
*
44-
* It also allows skipping the script section of the id, so `en-US` is
45-
* properly parsed as `en-*-US-*`.
46-
*/
474
constructor(locale: string) {
48-
let result;
49-
let normalized = convertMasks(locale.replace(/_/g, "-"));
50-
try {
51-
result = new Intl.Locale(normalized);
52-
} catch {
53-
this.isWellFormed = false;
54-
this.language = "und";
55-
return;
56-
}
57-
58-
this.language = result.language;
59-
this.script = result.script;
60-
this.region = result.region;
61-
62-
let visiblelangTagLength = getVisibleLangTagLength(
63-
this.language,
64-
this.script,
65-
this.region
66-
);
67-
68-
if (normalized.length > visiblelangTagLength) {
69-
let extStart = getExtensionStart(locale);
70-
this.variant = locale.substring(visiblelangTagLength + 1, extStart);
71-
}
72-
73-
this.isWellFormed = true;
74-
}
75-
76-
matches(other: Locale, thisRange = false, otherRange = false): boolean {
77-
return (
78-
this.isWellFormed &&
79-
other.isWellFormed &&
80-
(this.language === other.language ||
81-
(thisRange && this.language === "und") ||
82-
(otherRange && other.language === "und")) &&
83-
(this.script === other.script ||
84-
(thisRange && this.script === undefined) ||
85-
(otherRange && other.script === undefined)) &&
86-
(this.region === other.region ||
87-
(thisRange && this.region === undefined) ||
88-
(otherRange && other.region === undefined)) &&
89-
(this.variant === other.variant ||
90-
(thisRange && this.variant === undefined) ||
91-
(otherRange && other.variant === undefined))
92-
);
93-
}
94-
95-
toString(): string {
96-
return [this.language, this.script, this.region, this.variant]
97-
.filter(part => part !== undefined)
98-
.join("-");
99-
}
100-
101-
clearVariants(): void {
102-
this.variant = undefined;
103-
}
104-
105-
clearRegion(): void {
106-
this.region = undefined;
107-
}
108-
109-
addLikelySubtags(): boolean {
110-
const newLocale = getLikelySubtagsMin(this.toString().toLowerCase());
111-
if (newLocale) {
112-
this.language = newLocale.language;
113-
this.script = newLocale.script;
114-
this.region = newLocale.region;
115-
this.variant = newLocale.variant;
116-
return true;
5+
let tag = locale
6+
.replace(/_/g, "-")
7+
.replace(/^\*/, "und")
8+
.replace(/-\*/g, "");
9+
10+
super(tag);
11+
12+
if (!("variants" in this)) {
13+
// Available on Firefox 141 & later
14+
let lsrTagLength = this.language.length;
15+
if (this.script) lsrTagLength += this.script.length + 1;
16+
if (this.region) lsrTagLength += this.region.length + 1;
17+
18+
if (tag.length > lsrTagLength) {
19+
let unicodeExtStart: number | undefined = tag.search(/-[a-zA-Z]-/);
20+
if (unicodeExtStart === -1) unicodeExtStart = undefined;
21+
this.variants = tag.substring(lsrTagLength + 1, unicodeExtStart);
22+
}
11723
}
118-
return false;
119-
}
120-
}
121-
122-
/**
123-
* Below is a manually a list of likely subtags corresponding to Unicode
124-
* CLDR likelySubtags list.
125-
* This list is curated by the maintainers of Project Fluent and is
126-
* intended to be used in place of the full likelySubtags list in use cases
127-
* where full list cannot be (for example, due to the size).
128-
*
129-
* This version of the list is based on CLDR 30.0.3.
130-
*/
131-
const likelySubtagsMin: Record<string, string> = {
132-
ar: "ar-arab-eg",
133-
"az-arab": "az-arab-ir",
134-
"az-ir": "az-arab-ir",
135-
be: "be-cyrl-by",
136-
da: "da-latn-dk",
137-
el: "el-grek-gr",
138-
en: "en-latn-us",
139-
fa: "fa-arab-ir",
140-
ja: "ja-jpan-jp",
141-
ko: "ko-kore-kr",
142-
pt: "pt-latn-br",
143-
sr: "sr-cyrl-rs",
144-
"sr-ru": "sr-latn-ru",
145-
sv: "sv-latn-se",
146-
ta: "ta-taml-in",
147-
uk: "uk-cyrl-ua",
148-
zh: "zh-hans-cn",
149-
"zh-hant": "zh-hant-tw",
150-
"zh-hk": "zh-hant-hk",
151-
"zh-mo": "zh-hant-mo",
152-
"zh-tw": "zh-hant-tw",
153-
"zh-gb": "zh-hant-gb",
154-
"zh-us": "zh-hant-us",
155-
};
156-
157-
const regionMatchingLangs = [
158-
"az",
159-
"bg",
160-
"cs",
161-
"de",
162-
"es",
163-
"fi",
164-
"fr",
165-
"hu",
166-
"it",
167-
"lt",
168-
"lv",
169-
"nl",
170-
"pl",
171-
"ro",
172-
"ru",
173-
];
174-
175-
function getLikelySubtagsMin(loc: string): Locale | null {
176-
if (Object.prototype.hasOwnProperty.call(likelySubtagsMin, loc)) {
177-
return new Locale(likelySubtagsMin[loc]);
178-
}
179-
const locale = new Locale(loc);
180-
if (locale.language && regionMatchingLangs.includes(locale.language)) {
181-
locale.region = locale.language.toUpperCase();
182-
return locale;
18324
}
184-
return null;
18525
}

0 commit comments

Comments
 (0)