Skip to content

Commit 6233e25

Browse files
committed
l10n: add a new feature disable_i18n to disable l10n
1 parent e319a76 commit 6233e25

File tree

11 files changed

+578
-301
lines changed

11 files changed

+578
-301
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ feat_selinux = [
5959
"selinux",
6060
"stat/selinux",
6161
]
62+
# "disable_i18n" == disable translations and embed English strings directly into the binary (by using `--features disable_i18n`)
63+
# NOTE:
64+
# * This reduces binary size and removes runtime dependency on .ftl files
65+
# * All utilities will use hardcoded English strings regardless of system locale
66+
# * Automatically disables uucore's default i18n feature
67+
disable_i18n = ["uucore/disable_i18n"]
6268
##
6369
## feature sets
6470
## (common/core and Tier1) feature sets
@@ -378,6 +384,7 @@ digest = "0.10.7"
378384

379385
# Fluent dependencies
380386
fluent = "0.17.0"
387+
fluent-bundle = "0.16.0"
381388
unic-langid = "0.9.6"
382389
fluent-syntax = "0.12.0"
383390

docs/src/l10n.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,43 @@ Fluent default (disabled here):
184184
```
185185
"\u{2068}Alice\u{2069}"
186186
```
187+
188+
---
189+
190+
## 🚫 Disabling Translations
191+
192+
For minimal deployments or English-only environments, translations can be completely disabled:
193+
194+
```bash
195+
cargo build --features disable_i18n
196+
```
197+
198+
This feature:
199+
- **Embeds English strings** directly into the binary at compile time
200+
- **Removes runtime dependency** on `.ftl` files
201+
- **Ignores system locale** — always uses English
202+
203+
All `translate!()` calls work identically, but use embedded strings instead of Fluent bundles.
204+
205+
### ⚠️ Pluralization Limitation
206+
207+
**Important**: When `disable_i18n` is enabled, Fluent plural expressions are resolved at compile time by selecting one variant. This means:
208+
209+
- Messages like `{ $count -> [one] 1 record *[other] 2 records }` become static strings
210+
- The system prefers the `*[other]` variant when available for better grammar at count > 1
211+
- Tests expecting proper pluralization (e.g., `test_truncated_record`) may fail
212+
- Runtime pluralization is not available in this mode
213+
214+
Example:
215+
```fluent
216+
dd-progress-truncated-record = { $count ->
217+
[one] { $count } truncated record
218+
*[other] { $count } truncated records
219+
}
220+
```
221+
222+
In `disable_i18n` mode, this becomes: `"{ $count } truncated records"` (always plural form).
223+
224+
This is an inherent limitation of compile-time string embedding and is accepted as a trade-off for reduced binary size.
225+
226+
Useful for embedded systems, containers, or when size/performance matters more than localization.

fuzz/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bin/coreutils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ fn find_prefixed_util<'a>(
8383
fn setup_localization_or_exit(util_name: &str) {
8484
locale::setup_localization(get_canonical_util_name(util_name)).unwrap_or_else(|err| {
8585
match err {
86+
#[cfg(not(feature = "disable_i18n"))]
8687
uucore::locale::LocalizationError::ParseResource {
8788
error: err_msg,
8889
snippet,

src/uucore/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ icu_decimal = { workspace = true, optional = true, features = [
7272
icu_locale = { workspace = true, optional = true, features = ["compiled_data"] }
7373
icu_provider = { workspace = true, optional = true }
7474

75-
# Fluent dependencies
75+
# Fluent dependencies (always available, but only used when disable_i18n is not set)
7676
fluent = { workspace = true }
7777
fluent-syntax = { workspace = true }
7878
unic-langid = { workspace = true }
79+
fluent-bundle = { workspace = true }
7980
thiserror = { workspace = true }
8081
[target.'cfg(unix)'.dependencies]
8182
walkdir = { workspace = true, optional = true }
@@ -101,6 +102,8 @@ utmp-classic = { workspace = true, optional = true }
101102

102103
[features]
103104
default = []
105+
# disable_i18n disables translations and embeds English strings directly
106+
disable_i18n = []
104107
# * non-default features
105108
backup-control = []
106109
colors = []

src/uucore/src/lib/features/uptime.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ mod tests {
389389
}
390390
let _ = locale::setup_localization("uptime");
391391
assert_eq!("0 users", format_nusers(0));
392+
#[cfg(feature = "disable_i18n")]
393+
assert_eq!("1 users", format_nusers(1)); // With disable_i18n, plural form is always used
394+
#[cfg(not(feature = "disable_i18n"))]
392395
assert_eq!("1 user", format_nusers(1));
393396
assert_eq!("2 users", format_nusers(2));
394397
}

src/uucore/src/lib/lib.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,19 @@ macro_rules! bin {
179179
use uucore::locale;
180180
// suppress extraneous error output for SIGPIPE failures/panics
181181
uucore::panic::mute_sigpipe_panic();
182-
locale::setup_localization(uucore::get_canonical_util_name(stringify!($util)))
183-
.unwrap_or_else(|err| {
184-
match err {
185-
uucore::locale::LocalizationError::ParseResource {
186-
error: err_msg,
187-
snippet,
188-
} => eprintln!("Localization parse error at {snippet}: {err_msg}"),
189-
other => eprintln!("Could not init the localization system: {other}"),
190-
}
191-
std::process::exit(99)
192-
});
182+
locale::setup_localization_with_common(uucore::get_canonical_util_name(stringify!(
183+
$util
184+
)))
185+
.unwrap_or_else(|err| {
186+
match err {
187+
uucore::locale::LocalizationError::ParseResource {
188+
error: err_msg,
189+
snippet,
190+
} => eprintln!("Localization parse error at {snippet}: {err_msg:?}"),
191+
other => eprintln!("Could not init the localization system: {other}"),
192+
}
193+
std::process::exit(99)
194+
});
193195

194196
// execute utility code
195197
let code = $util::uumain(uucore::args_os());

0 commit comments

Comments
 (0)