Skip to content

Commit 7088385

Browse files
committed
Use Intl.Locale internally
1 parent e99614e commit 7088385

File tree

4 files changed

+84
-36
lines changed

4 files changed

+84
-36
lines changed

fluent-langneg/src/locale.ts

+64-35
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1-
/* eslint no-magic-numbers: 0 */
1+
function convertMasks(locale: string): string {
2+
let result;
3+
if (locale[0] === "*") {
4+
result = "und" + locale.substr(1);
5+
} else {
6+
result = locale;
7+
};
8+
return result.replace(/\-\*/g, "");
9+
}
210

3-
const languageCodeRe = "([a-z]{2,3}|\\*)";
4-
const scriptCodeRe = "(?:-([a-z]{4}|\\*))";
5-
const regionCodeRe = "(?:-([a-z]{2}|\\*))";
6-
const variantCodeRe = "(?:-(([0-9][a-z0-9]{3}|[a-z0-9]{5,8})|\\*))";
11+
function getVisibleLangTagLength(language: any, script: any, region: any) {
12+
let result = 0;
13+
result += language ? language.length : "und".length;
14+
result += script ? script.length + 1 : 0;
15+
result += region ? region.length + 1 : 0;
16+
return result;
17+
}
718

8-
/**
9-
* Regular expression splitting locale id into four pieces:
10-
*
11-
* Example: `en-Latn-US-macos`
12-
*
13-
* language: en
14-
* script: Latn
15-
* region: US
16-
* variant: macos
17-
*
18-
* It can also accept a range `*` character on any position.
19-
*/
20-
const localeRe = new RegExp(
21-
`^${languageCodeRe}${scriptCodeRe}?${regionCodeRe}?${variantCodeRe}?$`,
22-
"i"
23-
);
19+
function getExtensionStart(locale: string): number | undefined {
20+
let pos = locale.search(/-[a-zA-Z]-/);
21+
if (pos === -1) {
22+
return undefined;
23+
}
24+
return pos;
25+
}
2426

2527
export class Locale {
2628
isWellFormed: boolean;
27-
language?: string;
29+
language: string;
2830
script?: string;
2931
region?: string;
3032
variant?: string;
@@ -39,29 +41,55 @@ export class Locale {
3941
* properly parsed as `en-*-US-*`.
4042
*/
4143
constructor(locale: string) {
42-
const result = localeRe.exec(locale.replace(/_/g, "-"));
43-
if (!result) {
44+
let result;
45+
let normalized = convertMasks(locale.replace(/_/g, "-"));
46+
try {
47+
result = new Intl.Locale(normalized);
48+
} catch (e) {
4449
this.isWellFormed = false;
50+
this.language = "und";
4551
return;
4652
}
4753

48-
let [, language, script, region, variant] = result;
54+
this.language = result.language || "und";
55+
this.script = result.script;
56+
this.region = result.region;
4957

50-
if (language) {
51-
this.language = language.toLowerCase();
58+
let visiblelangTagLength = getVisibleLangTagLength(this.language, this.script, this.region);
59+
60+
if (normalized.length > visiblelangTagLength) {
61+
let extStart = getExtensionStart(locale);
62+
this.variant = locale.substring(visiblelangTagLength + 1, extStart);
63+
}
64+
65+
this.isWellFormed = true;
66+
}
67+
68+
static fromComponents({language, script, region, variant}: {
69+
language?: string,
70+
script?: string,
71+
region?: string,
72+
variant?: string,
73+
}): Locale {
74+
let result = new Locale("und");
75+
if (language && language !== "*") {
76+
result.language = language;
5277
}
53-
if (script) {
54-
this.script = script[0].toUpperCase() + script.slice(1);
78+
if (script && script !== "*") {
79+
result.script = script;
5580
}
56-
if (region) {
57-
this.region = region.toUpperCase();
81+
if (region && region !== "*") {
82+
result.region = region;
5883
}
59-
this.variant = variant;
60-
this.isWellFormed = true;
84+
if (variant && variant !== "*") {
85+
result.variant = variant;
86+
}
87+
return result;
6188
}
6289

6390
isEqual(other: Locale): boolean {
6491
return (
92+
this.isWellFormed === other.isWellFormed &&
6593
this.language === other.language &&
6694
this.script === other.script &&
6795
this.region === other.region &&
@@ -71,9 +99,10 @@ export class Locale {
7199

72100
matches(other: Locale, thisRange = false, otherRange = false): boolean {
73101
return (
102+
this.isWellFormed && other.isWellFormed &&
74103
(this.language === other.language ||
75-
(thisRange && this.language === undefined) ||
76-
(otherRange && other.language === undefined)) &&
104+
(thisRange && this.language === "und") ||
105+
(otherRange && other.language === "und")) &&
77106
(this.script === other.script ||
78107
(thisRange && this.script === undefined) ||
79108
(otherRange && other.script === undefined)) &&

fluent-langneg/test/langneg_test.js

+7
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ const data = {
130130
"matching",
131131
["fr", "en"],
132132
],
133+
[
134+
["es-419"],
135+
["es", "en"],
136+
undefined,
137+
"matching",
138+
["es"],
139+
],
133140
],
134141
},
135142
lookup: {

fluent-langneg/test/locale_test.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { Locale } from "../esm/locale.js";
33

44
function isLocaleEqual(str, ref) {
55
const locale = new Locale(str);
6-
return locale.isEqual(ref);
6+
const other = Locale.fromComponents(ref);
7+
return locale.isEqual(other);
78
}
89

910
suite("Parses simple locales", () => {
@@ -108,6 +109,16 @@ suite("Parses simple locales", () => {
108109
})
109110
);
110111
});
112+
113+
test("skipping extensions", () => {
114+
assert.ok(
115+
isLocaleEqual("en-US-macos-linux-u-hc-h12", {
116+
language: "en",
117+
region: "US",
118+
variant: "macos-linux",
119+
})
120+
);
121+
});
111122
});
112123

113124
suite("Parses locale ranges", () => {

fluent-langneg/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "../tsconfig.json",
33
"compilerOptions": {
4+
"target": "es2020",
45
"outDir": "./esm"
56
},
67
"include": ["./src/**/*.ts"]

0 commit comments

Comments
 (0)