Skip to content

Commit 701237a

Browse files
committed
lib: add navigator.language & navigator.languages
1 parent 14af167 commit 701237a

File tree

3 files changed

+113
-1
lines changed

3 files changed

+113
-1
lines changed

‎doc/api/globals.md‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,51 @@ logical processors available to the current Node.js instance.
637637
console.log(`This process is running on ${navigator.hardwareConcurrency} logical processors`);
638638
```
639639

640+
### `navigator.language`
641+
642+
<!-- YAML
643+
added: REPLACEME
644+
-->
645+
646+
*{string}
647+
648+
The `navigator.language` read-only property returns a string representing the
649+
preferred language of the Node.js instance.
650+
651+
The value is representing the language version as defined in [RFC 5646][].
652+
653+
You can override the value of `navigator.language` by setting the
654+
`LC_ALL` environment variable.
655+
656+
For example:
657+
658+
```sh
659+
LC_ALL=fr-FR node -p "console.log(navigator.language)"
660+
```
661+
662+
The fallback value on builds without ICU is `'en-US'`.
663+
664+
```js
665+
console.log(`The preferred language of the Node.js instance has the tag '${navigator.language}'`);
666+
```
667+
668+
### `navigator.languages`
669+
670+
<!-- YAML
671+
added: REPLACEME
672+
-->
673+
674+
*{Array<string>}
675+
676+
The `navigator.languages` read-only property returns an array of strings
677+
representing the preferred languages of the Node.js instance.
678+
By default it contains only the value of `navigator.language`.
679+
The fallback value on builds without ICU is `['en-US']`.
680+
681+
```js
682+
console.log(`The preferred languages are '${navigator.language}'`);
683+
```
684+
640685
### `navigator.userAgent`
641686

642687
<!-- YAML
@@ -1085,6 +1130,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
10851130
[CommonJS module]: modules.md
10861131
[ECMAScript module]: esm.md
10871132
[Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object
1133+
[RFC 5646]: https://www.rfc-editor.org/rfc/rfc5646.txt
10881134
[Web Crypto API]: webcrypto.md
10891135
[`--experimental-websocket`]: cli.md#--experimental-websocket
10901136
[`--no-experimental-global-customevent`]: cli.md#--no-experimental-global-customevent

‎lib/internal/navigator.js‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
const{
44
ObjectDefineProperties,
5+
ObjectFreeze,
6+
globalThis,
57
Symbol,
68
}=primordials;
79

10+
const{
11+
Intl,
12+
}=globalThis;
13+
814
const{
915
ERR_ILLEGAL_CONSTRUCTOR,
1016
}=require('internal/errors').codes;
@@ -24,6 +30,8 @@ class Navigator{
2430
// Private properties are used to avoid brand validations.
2531
#availableParallelism;
2632
#userAgent =`Node.js/${nodeVersion.slice(1,nodeVersion.indexOf('.'))}`;
33+
#language =Intl?.Collator().resolvedOptions().locale||'en-US';
34+
#languages =ObjectFreeze([this.#language]);
2735

2836
constructor(){
2937
if(arguments[0]===kInitialize){
@@ -40,6 +48,20 @@ class Navigator{
4048
returnthis.#availableParallelism;
4149
}
4250

51+
/**
52+
* @return{string}
53+
*/
54+
getlanguage(){
55+
returnthis.#language;
56+
}
57+
58+
/**
59+
* @return{Array<string>}
60+
*/
61+
getlanguages(){
62+
returnthis.#languages;
63+
}
64+
4365
/**
4466
* @return{string}
4567
*/
@@ -50,6 +72,8 @@ class Navigator{
5072

5173
ObjectDefineProperties(Navigator.prototype,{
5274
hardwareConcurrency: kEnumerableProperty,
75+
language: kEnumerableProperty,
76+
languages: kEnumerableProperty,
5377
userAgent: kEnumerableProperty,
5478
});
5579

‎test/parallel/test-navigator.js‎

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
22

3-
require('../common');
3+
constcommon=require('../common');
44
constassert=require('assert');
5+
const{ execFile }=require('child_process');
56

67
constis={
78
number: (value,key)=>{
@@ -15,3 +16,44 @@ is.number(navigator.hardwareConcurrency, 'hardwareConcurrency');
1516
assert.ok(navigator.hardwareConcurrency>0);
1617
assert.strictEqual(typeofnavigator.userAgent,'string');
1718
assert.match(navigator.userAgent,/^Node\.js\/\d+$/);
19+
20+
assert.strictEqual(typeofnavigator.language,'string');
21+
assert.strictEqual(navigator.language,'en-US');
22+
23+
assert.ok(Array.isArray(navigator.languages));
24+
assert.strictEqual(navigator.languages.length,1);
25+
assert.strictEqual(typeofnavigator.languages[0],'string');
26+
27+
assert.throws(()=>{
28+
navigator.languages[0]='foo';
29+
},newTypeError("Cannot assign to read only property '0' of object '[object Array]'"));
30+
assert.notStrictEqual(navigator.languages[0],'foo');
31+
assert.strictEqual(navigator.languages[0],'en-US');
32+
33+
Object.defineProperty(navigator,'language',{value: 'de-DE'});
34+
assert.strictEqual(navigator.language,'de-DE');
35+
assert.strictEqual(navigator.languages.length,1);
36+
assert.strictEqual(navigator.languages[0],'en-US');
37+
38+
39+
if(common.hasIntl){
40+
{
41+
constenv={ ...process.env,LC_ALL: 'fr-FR'};
42+
execFile(
43+
process.execPath,
44+
['-p','process.exit(navigator.language === "fr-FR" ? 0 : 1)'],
45+
{ env },
46+
common.mustSucceed()
47+
);
48+
}
49+
50+
{
51+
constenv={ ...process.env,LC_ALL: 'fr-FR'};
52+
execFile(
53+
process.execPath,
54+
['-p','process.exit(navigator.languages[0] === "fr-FR" ? 0 : 1)'],
55+
{ env },
56+
common.mustSucceed()
57+
);
58+
}
59+
}

0 commit comments

Comments
(0)