Skip to content

Commit ad61226

Browse files
committed
lib: set locale per cli or env
1 parent 8373643 commit ad61226

File tree

8 files changed

+101
-0
lines changed

8 files changed

+101
-0
lines changed

doc/api/cli.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,19 @@ surface on other platforms, but the performance impact may be severe.
11381138
This flag is inherited from V8 and is subject to change upstream. It may
11391139
disappear in a non-semver-major release.
11401140

1141+
### `--locale`
1142+
1143+
<!-- YAML
1144+
added: REPLACEME
1145+
-->
1146+
1147+
Set the default locale used by ICU (`Intl` object). It expects a string
1148+
representing the language version as defined in [RFC 5646][] (also known as
1149+
BCP 47). (Overrides `LOCALE`.)
1150+
1151+
Examples of valid language codes include "en", "en-US", "fr", "fr-FR", "es-ES",
1152+
etc.
1153+
11411154
### `--max-http-header-size=size`
11421155

11431156
<!-- YAML
@@ -2197,6 +2210,15 @@ and `NODE_DISABLE_COLORS` environment variables are ignored.
21972210

21982211
Any other value will result in colorized output being disabled.
21992212

2213+
### `LOCALE=value`
2214+
2215+
The `LOCALE` environment variable is used to set the default locale used by
2216+
ICU (`Intl` object). It expects a string representing the language version as
2217+
defined in [RFC 5646][] (also known as BCP 47).
2218+
2219+
Examples of valid language codes include "en", "en-US", "fr", "fr-FR", "es-ES",
2220+
etc.
2221+
22002222
### `NO_COLOR=<any>`
22012223

22022224
[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the

src/node.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,15 @@ static ExitCode InitializeNodeWithArgsInternal(
949949
}
950950
# endif
951951

952+
std::string locale;
953+
if (credentials::SafeGetenv("LOCALE", &locale) && !locale.empty()) {
954+
i18n::SetDefaultLocale(locale.c_str());
955+
}
956+
957+
if (!per_process::cli_options->locale.empty()) {
958+
i18n::SetDefaultLocale(per_process::cli_options->locale.c_str());
959+
}
960+
952961
#endif // defined(NODE_HAVE_I18N_SUPPORT)
953962

954963
// We should set node_is_initialized here instead of in node::Start,

src/node_env_var.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,28 @@ void DateTimeConfigurationChangeNotification(
100100
}
101101
}
102102

103+
template <typename T>
104+
void LocaleConfigurationChangeNotification(
105+
Isolate* isolate,
106+
const T& key,
107+
const char* val = nullptr) {
108+
if (
109+
key.length() == 6 &&
110+
key[0] == 'L' &&
111+
key[1] == 'O' &&
112+
key[2] == 'C' &&
113+
key[3] == 'A' &&
114+
key[4] == 'L' &&
115+
key[5] == 'E'
116+
) {
117+
118+
# if defined(NODE_HAVE_I18N_SUPPORT)
119+
i18n::SetDefaultLocale(val);
120+
# endif
121+
isolate->LocaleConfigurationChangeNotification();
122+
}
123+
}
124+
103125
Maybe<std::string> RealEnvStore::Get(const char* key) const {
104126
Mutex::ScopedLock lock(per_process::env_var_mutex);
105127

@@ -148,6 +170,7 @@ void RealEnvStore::Set(Isolate* isolate,
148170
#endif
149171
uv_os_setenv(*key, *val);
150172
DateTimeConfigurationChangeNotification(isolate, key, *val);
173+
LocaleConfigurationChangeNotification(isolate, key, *val);
151174
}
152175

153176
int32_t RealEnvStore::Query(const char* key) const {
@@ -182,6 +205,7 @@ void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
182205

183206
node::Utf8Value key(isolate, property);
184207
uv_os_unsetenv(*key);
208+
LocaleConfigurationChangeNotification(isolate, key);
185209
DateTimeConfigurationChangeNotification(isolate, key);
186210
}
187211

src/node_i18n.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include <unicode/ucnv.h>
6262
#include <unicode/udata.h>
6363
#include <unicode/uidna.h>
64+
#include <unicode/uloc.h>
6465
#include <unicode/ulocdata.h>
6566
#include <unicode/urename.h>
6667
#include <unicode/ustring.h>
@@ -601,6 +602,22 @@ void SetDefaultTimeZone(const char* tzid) {
601602
CHECK(U_SUCCESS(status));
602603
}
603604

605+
void SetDefaultLocale(const char* localeid) {
606+
UErrorCode status = U_ZERO_ERROR;
607+
608+
if (localeid != nullptr) {
609+
size_t localelen = strlen(localeid) + 1;
610+
MaybeStackBuffer<UChar, 256> id(localelen);
611+
u_charsToUChars(localeid, id.out(), localelen);
612+
// This is threadsafe:
613+
uloc_setDefault(localeid, &status);
614+
CHECK(U_SUCCESS(status));
615+
} else {
616+
uloc_setDefault(localeid, &status);
617+
CHECK(U_SUCCESS(status));
618+
}
619+
}
620+
604621
int32_t ToUnicode(MaybeStackBuffer<char>* buf,
605622
const char* input,
606623
size_t length) {

src/node_i18n.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ bool InitializeICUDirectory(const std::string& path, std::string* error);
4242

4343
void SetDefaultTimeZone(const char* tzid);
4444

45+
void SetDefaultLocale(const char* localid);
46+
4547
enum class idna_mode {
4648
// Default mode for maximum compatibility.
4749
kDefault,

src/node_options.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,11 @@ PerProcessOptionsParser::PerProcessOptionsParser(
934934
,
935935
&PerProcessOptions::icu_data_dir,
936936
kAllowedInEnvvar);
937+
938+
AddOption("--locale",
939+
"Set the locale of the node instance",
940+
&PerProcessOptions::locale,
941+
kAllowedInEnvvar);
937942
#endif
938943

939944
#if HAVE_OPENSSL

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ class PerProcessOptions : public Options {
271271

272272
#ifdef NODE_HAVE_I18N_SUPPORT
273273
std::string icu_data_dir;
274+
std::string locale;
274275
#endif
275276

276277
// Per-process because they affect singleton OpenSSL shared library state,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
5+
if (!common.hasIntl)
6+
common.skip('Intl not present.');
7+
8+
if (!common.isMainThread)
9+
common.skip('process.env.LOCALE is not intercepted in Workers');
10+
11+
process.env.LOCALE = 'de-DE-custom';
12+
assert.strictEqual(
13+
new Intl.NumberFormat().resolvedOptions().locale,
14+
'de-DE-custom',
15+
);
16+
17+
delete process.env.LOCALE;
18+
assert.notStrictEqual(
19+
new Intl.NumberFormat().resolvedOptions().locale,
20+
'de-DE-custom',
21+
);

0 commit comments

Comments
 (0)