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
+ }
2
10
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
+ }
7
18
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 - z A - Z ] - / ) ;
21
+ if ( pos === - 1 ) {
22
+ return undefined ;
23
+ }
24
+ return pos ;
25
+ }
24
26
25
27
export class Locale {
26
28
isWellFormed : boolean ;
27
- language ? : string ;
29
+ language : string ;
28
30
script ?: string ;
29
31
region ?: string ;
30
32
variant ?: string ;
@@ -39,29 +41,55 @@ export class Locale {
39
41
* properly parsed as `en-*-US-*`.
40
42
*/
41
43
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 ) {
44
49
this . isWellFormed = false ;
50
+ this . language = "und" ;
45
51
return ;
46
52
}
47
53
48
- let [ , language , script , region , variant ] = result ;
54
+ this . language = result . language || "und" ;
55
+ this . script = result . script ;
56
+ this . region = result . region ;
49
57
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 ;
52
77
}
53
- if ( script ) {
54
- this . script = script [ 0 ] . toUpperCase ( ) + script . slice ( 1 ) ;
78
+ if ( script && script !== "*" ) {
79
+ result . script = script ;
55
80
}
56
- if ( region ) {
57
- this . region = region . toUpperCase ( ) ;
81
+ if ( region && region !== "*" ) {
82
+ result . region = region ;
58
83
}
59
- this . variant = variant ;
60
- this . isWellFormed = true ;
84
+ if ( variant && variant !== "*" ) {
85
+ result . variant = variant ;
86
+ }
87
+ return result ;
61
88
}
62
89
63
90
isEqual ( other : Locale ) : boolean {
64
91
return (
92
+ this . isWellFormed === other . isWellFormed &&
65
93
this . language === other . language &&
66
94
this . script === other . script &&
67
95
this . region === other . region &&
@@ -71,9 +99,10 @@ export class Locale {
71
99
72
100
matches ( other : Locale , thisRange = false , otherRange = false ) : boolean {
73
101
return (
102
+ this . isWellFormed && other . isWellFormed &&
74
103
( this . language === other . language ||
75
- ( thisRange && this . language === undefined ) ||
76
- ( otherRange && other . language === undefined ) ) &&
104
+ ( thisRange && this . language === "und" ) ||
105
+ ( otherRange && other . language === "und" ) ) &&
77
106
( this . script === other . script ||
78
107
( thisRange && this . script === undefined ) ||
79
108
( otherRange && other . script === undefined ) ) &&
0 commit comments