Skip to content

uucore: negotiate the requested locale against the translations that exist - #14327

Open
XhstormR wants to merge 1 commit into
uutils:mainfrom
XhstormR:locale-negotiation
Open

uucore: negotiate the requested locale against the translations that exist#14327
XhstormR wants to merge 1 commit into
uutils:mainfrom
XhstormR:locale-negotiation

Conversation

@XhstormR

@XhstormR XhstormR commented Aug 31, 2026

Copy link
Copy Markdown

Fixes #12305.

LANG=zh_CN.UTF-8 never finds zh-Hans.ftl because the requested locale is used directly as a file name. Requests are region-based (zh_CN, de_DE, es_MX), translations are filed by language or script (de, zh-Hans), so only the names that happen to coincide match. Against the 31 locales ls has in coreutils-l10n, 8 of 53 system locales currently get a translation, 53 with this patch. de_DE, ja_JP, ru_RU, es_MX, en_GB and ~25 others fall back to English too, so this isn't Chinese-specific.

Renaming to zh_CN.ftl as the issue suggests wouldn't work anyway: unic-langid prints -, so the lookup key is zh-CN.ftl.

So the patch negotiates instead of matching literally: enumerate the .ftl files in the locales dir, run RFC 4647 filtering over them via fluent-langneg, then read the winner. It also reads LC_ALL/LC_MESSAGES/LANGUAGE, which were ignored before, and strips @modifier.

fluent-langneg is already in Cargo.lock through fluent-bundle. Its cldr feature pulls in CLDR's likely-subtags tables: +357 KiB on the multicall binary. Without them zh_TW/zh_HK/zh_MO resolve to Simplified, which is worse than English. If that size isn't acceptable, I can generate only the rows needed in build.rs instead (~1 KB) — let me know which you'd rather have.

CI never caught this because fr-FR is the only in-tree translation and fr_FR.UTF-8 normalizes to exactly fr-FR.

Three tests picked their language through a variable that LC_ALL now outranks, so they set LC_ALL instead.

@sylvestre

Copy link
Copy Markdown
Contributor

@XhstormR sorry but long comment #0 aren't useful. nobody reads such a long comment from a LLM

Comment thread Cargo.toml Outdated
…exist

A locale was taken for a translation's file name: whatever LANG named,
stripped of its encoding, was read back as `{locale}.ftl`. The two are
named in different worlds, though. A POSIX environment asks with a
region -- zh_CN, de_DE, es_MX -- while a translation is filed under
whatever distinction its translators needed: a language (de.ftl), a
region (pt-BR.ftl) or a script (zh-Hans.ftl). Only the translations
whose name happened to coincide with a locale's were reachable, so of
53 system locales tried against the 31 translations of ls, 8 read
anything but English. Some two dozen languages were translated and
unreachable, Chinese among them.

Match the two by negotiating, as RFC 4647 and UTS uutils#35 specify and
fluent-langneg implements: enumerate the translations a locales
directory holds, negotiate the request against them, and let the name
of the winner be what everything downstream reads. All 53 then reach
the translation meant for them, and a language added later needs no
code. Turn on fluent-langneg's `cldr` feature for its likely-subtags
data, without which zh_TW, zh_HK and zh_MO read Simplified Chinese --
worse than reading English. It costs 357 KiB of tables, 86% of that
the language-to-script one.

Read the environment the way gettext does while here, since a request
that is never read is a request that cannot be negotiated: LC_ALL and
LC_MESSAGES outrank LANG, LANGUAGE lists locales to try in turn unless
the locale asked for was C or POSIX, and a modifier -- the @euro of
de_DE@euro -- says as little about which language to speak as an
encoding does. Three tests named a language through a variable that
something outranked, and now say so through the one that decides.

None of this was caught because fr-FR is the only translation in this
tree, and `LANG=fr_FR.UTF-8` normalizes to exactly fr-FR: the one
locale whose name coincides with its translation's, and the one every
existing assertion uses.

The embedded translations a WASI build carries are still looked up by
name, as the embedded set cannot be enumerated to negotiate against.

Fixes uutils#12305

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@XhstormR
XhstormR force-pushed the locale-negotiation branch from 33916a2 to fdfe21b Compare August 31, 2026 07:42
@XhstormR

Copy link
Copy Markdown
Author

@sylvestre I have simplified the PR description, including only the necessary information.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/dd/misc. tests/dd/misc is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/df/over-mount-device. tests/df/over-mount-device is passing on 'main'. Maybe you have to rebase?
Skipping an intermittent issue tests/tail/inotify-dir-recreate (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/cat/splice is no longer failing!
Congrats! The gnu test tests/cp/cp-a-selinux is no longer failing!
Congrats! The gnu test tests/cut/cut is no longer failing!
Congrats! The gnu test tests/cut/mb-non-utf8 is no longer failing!
Congrats! The gnu test tests/dd/partial-write is no longer failing!
Congrats! The gnu test tests/expand/mb is no longer failing!
Congrats! The gnu test tests/ls/stat-free-symlinks is no longer failing!
Congrats! The gnu test tests/misc/close-stdout is no longer failing!
Congrats! The gnu test tests/mktemp/write-error is no longer failing!
Congrats! The gnu test tests/mv/dir2dir is no longer failing!
Congrats! The gnu test tests/mv/mv-exchange is no longer failing!
Congrats! The gnu test tests/nl/multibyte is no longer failing!
Congrats! The gnu test tests/od/od-float is no longer failing!
Congrats! The gnu test tests/od/od-j is no longer failing!
Congrats! The gnu test tests/ptx/ptx-overrun is no longer failing!
Congrats! The gnu test tests/sort/sort-merge-fdlimit is no longer failing!
Congrats! The gnu test tests/unexpand/mb is no longer failing!
Note: The gnu test tests/dd/fail-ftruncate-fstat was skipped on 'main' but is now failing.

@codspeed-hq

codspeed-hq Bot commented Aug 31, 2026

Copy link
Copy Markdown

Unable to generate the performance report

There was an internal error while processing the run's data. We're working on fixing the issue. Feel free to contact us on Discord or at support@codspeed.io if the issue persists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

incorrect localization type for chinese

2 participants