Use locale-aware decimal separator in zfs_nicenum_format - #18881
Use locale-aware decimal separator in zfs_nicenum_format#18881mmustafasenoglu wants to merge 3 commits into
Conversation
The snprintf("%.*f") call in zfs_nicenum_format() always uses
the C locale decimal point ('.'), ignoring the user's LC_NUMERIC
locale setting. This causes commands like "zfs list" and "zpool
status" to display decimal values with '.' even in locales that
use ',' (e.g. de_DE, fr_FR).
Added zfs_nicenum_locale_decimal() helper that uses localeconv()
to get the locale's decimal separator and replaces '.' in the
formatted output. This restores the behavior that existed in
OpenZFS 0.7.x where locale settings were respected.
Fixes openzfs#16987
There was a problem hiding this comment.
Pull request overview
This PR aims to make zfs_nicenum_format() emit human-readable numeric values using the user’s locale decimal separator (e.g., 10,3G in de_DE) instead of always using ..
Changes:
- Add a helper to replace
.with the locale’s decimal separator after floating-point formatting. - Invoke the helper on the chosen “fits in 5 chars” formatted output path.
Suppressed comments (1)
lib/libzutil/zutil_nicenum.c:170
- This call won’t change output for the primary CLI consumers because they explicitly force
LC_NUMERICto "C" (e.g.,cmd/zfs/zfs_main.c:9447-9449andcmd/zpool/zpool_main.c:14208-14210). WithLC_NUMERIC=C,localeconv()->decimal_pointremains'.', sozfs_nicenum_locale_decimal()becomes a no-op and the decimal separator stays..
if (snprintf(buf, buflen, "%.*f%s", i,
val, u) <= 5) {
zfs_nicenum_locale_decimal(buf);
break;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* If locale uses '.' or is empty, nothing to change */ | ||
| if (dp == NULL || dp[0] == '.' || dp[0] == '\0') | ||
| return; |
The snprintf("%.*f") call in zfs_nicenum_format() always uses
the C locale decimal point ('.'), ignoring the user's LC_NUMERIC
locale setting. This causes commands like "zfs list" and "zpool
status" to display decimal values with '.' even in locales that
use ',' (e.g. de_DE, fr_FR).
Added zfs_nicenum_locale_decimal() helper that uses localeconv()
to get the locale's decimal separator and replaces '.' in the
formatted output. This restores the behavior that existed in
OpenZFS 0.7.x where locale settings were respected.
Signed-off-by: Mustafa Senoglu <mmustafasenoglu0@gmail.com>
Fixes openzfs#16987
|
Good catch on the multibyte However, in practice the ZFS CLI forces That said, if someone calls if (dp == NULL || dp[0] == \0 || strlen(dp) > 1)
return;This way we gracefully skip locales where we cant safely do a single-byte replacement. Ill add this guard in the next push. |
Arabic and other locales use multibyte decimal separators (e.g. U+066B). Since zfs_nicenum_locale_decimal() replaces only a single byte, this would corrupt the output. Add a strlen(dp) > 1 guard to skip locales where the separator doesn't fit in one byte. In practice, the ZFS CLI forces LC_NUMERIC="C" before calling zfs_nicenum_format(), so this is a defensive measure for direct callers who don't set the locale.
|
There's a bit of history here. Commit c2c7ca0 explicitly set "C" as the LC_NUMERIC to force the decimal separator. At the time the notion was it was preferable to standardize the CLI across locales to facilitate scripting. That's led to some confusion, and it's a decision we should probably revisit. Let's move this discussion over to PR #17075 where there were some good suggestions. |
Summary
Fixes #16987
OpenZFS command output (
zfs list,zpool status, etc.) always uses the C locale decimal separator (.), ignoring the user'sLC_NUMERIClocale setting. In locales that use,as the decimal separator (e.g.de_DE,fr_FR), values like10.3Gshould appear as10,3G.This is a regression from OpenZFS 0.7.x, where locale settings were respected. The issue affects both Linux and FreeBSD (confirmed by
cbinnerin the issue).Problem
The
snprintf("%.*f")call inzfs_nicenum_format()atlib/libzutil/zutil_nicenum.c:142uses the C locale's decimal point (.) by default. The Csnprintffunction does not respectLC_NUMERIClocale settings.Fix
Added
zfs_nicenum_locale_decimal()helper function that:localeconv()to get the locale's decimal separator.in the formatted buffer with the locale-specific separatorThis is called after the
snprintf("%.*f")formatting in theelsebranch ofzfs_nicenum_format(). TheZFS_NICENUM_TIMEformat is not affected since it uses integer formatting (%d).Locale behavior
C/en_US10.3G10.3G(no change)de_DE10.3G10,3Gfr_FR10.3G10,3GChanges
lib/libzutil/zutil_nicenum.c: Added#include <locale.h>,zfs_nicenum_locale_decimal()helper, and locale decimal replacement aftersnprintfNotes
.(C, en_US, etc.) see no changeLANG=Care unaffectedZFS_NICENUM_TIMEformat (integer output) is not affectedzfs_isnumber()input parsing is intentionally not changed — this fix addresses output formatting only