From 2f9103e5b3a79ccf2bf50d733898b2d43f306bc7 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 28 Aug 2026 15:03:34 +0200 Subject: [PATCH] Adjust locale settings for local usage --- .config/shell/locale | 54 +++++++++++------ .local/bin/dotfiles-install | 11 ++++ .local/bin/ensure-locales | 112 ++++++++++++++++++++++++++++++++++++ .local/bin/scp | 5 ++ .local/bin/sftp | 5 ++ .local/bin/ssh | 14 +++++ 6 files changed, 183 insertions(+), 18 deletions(-) create mode 100755 .local/bin/ensure-locales create mode 100755 .local/bin/scp create mode 100755 .local/bin/sftp create mode 100755 .local/bin/ssh diff --git a/.config/shell/locale b/.config/shell/locale index 7c4fd27..c1190ae 100644 --- a/.config/shell/locale +++ b/.config/shell/locale @@ -1,24 +1,42 @@ #!/bin/sh -# Locale settings for headless machines +# Locale settings for desktop machines +# as run by the author of this `dotfiles` repository + +# Requires the en_US.UTF-8, en_DK.UTF-8, en_IE.UTF-8, and de_DE.UTF-8 +# locales be installed which is checked by ~/.local/bin/ensure-locales + +# Other hosts (e.g., servers) usually lack some of these locales, +# and Debian's /etc/ssh/ssh_config forwards all of them via `SendEnv LANG LC_*` +# => ~/.local/bin/ssh, ~/.local/bin/scp, and ~/.local/bin/sftp +# send `LC_ALL=C.UTF-8` on top to mask them on remote hosts -# Drop what a client may forward over `ssh`, -# because `AcceptEnv LANG LC_*` may be set in /etc/ssh/sshd_config -unset \ - LC_ALL \ - LC_ADDRESS \ - LC_COLLATE \ - LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT \ - LC_MESSAGES \ - LC_MONETARY \ - LC_NAME \ - LC_NUMERIC \ - LC_PAPER \ - LC_TELEPHONE \ - LC_TIME +export LANG=en_US.UTF-8 # Fallback for any (future) LC_* not set below -# Set every LC_* category implicitly with the `$LANG` fallback -export LANG=C.UTF-8 +# Base: US English for language, text handling, and sorting + +export LC_COLLATE=en_US.UTF-8 # Dictionary sort order => Ignore punctuation +export LC_CTYPE=en_US.UTF-8 # UTF-8 character classification +export LC_MESSAGES=en_US.UTF-8 +export LC_NUMERIC=en_US.UTF-8 # 1,234.56 rather than 1.234,56 + + +# Exceptions: European conventions with English number formatting + +export LC_MEASUREMENT=en_IE.UTF-8 # Use the metric system +export LC_MONETARY=en_IE.UTF-8 # Euro with 1,234.56 +export LC_PAPER=en_IE.UTF-8 # Use DIN-A4 for a page +export LC_TIME=en_DK.UTF-8 # ISO dates, 24h clock, Monday first + + +# (German) metadata + +export LC_ADDRESS=de_DE.UTF-8 # German postal layout +export LC_IDENTIFICATION=en_US.UTF-8 +export LC_NAME=en_US.UTF-8 +export LC_TELEPHONE=de_DE.UTF-8 # For "+49" and "030 12345" + + +export TZ="Europe/Berlin" diff --git a/.local/bin/dotfiles-install b/.local/bin/dotfiles-install index 3c3f3fc..924f790 100755 --- a/.local/bin/dotfiles-install +++ b/.local/bin/dotfiles-install @@ -143,6 +143,10 @@ done unset _key +locales_ok=1 +sh "$XDG_BIN_HOME/ensure-locales" >/dev/null 2>&1 || locales_ok=0 + + if command -v batcat >/dev/null 2>&1 && ! command -v bat >/dev/null 2>&1; then ln -sf "$(command -v batcat)" "$XDG_BIN_HOME/bat" fi @@ -191,6 +195,13 @@ fi echo "" echo "The dotfiles were installed successfully (branch: $DOTFILES_BRANCH)" echo "" + +if [ "$locales_ok" -ne 1 ]; then + echo "WARNING: Some locales are missing, fix:" + echo " sudo $XDG_BIN_HOME/ensure-locales" + echo "" +fi + echo "Reload your shell to use them:" echo " exec \$SHELL -l" echo "" diff --git a/.local/bin/ensure-locales b/.local/bin/ensure-locales new file mode 100755 index 0000000..6debec2 --- /dev/null +++ b/.local/bin/ensure-locales @@ -0,0 +1,112 @@ +#!/bin/sh + +# Ensure all locales specified in ~/.config/shell/locale are installed + +set -eu + + + +LOCALES="en_US.UTF-8 de_DE.UTF-8 en_DK.UTF-8 en_IE.UTF-8" + + + +# Because `locale -a` prints, for example, "en_US.utf8", +# and `locale.gen` wants "en_US.UTF-8" instead +_locale_present() { + norm=$(printf '%s' "$1" | tr 'A-Z' 'a-z' | tr -d '-') + locale -a 2>/dev/null | tr 'A-Z' 'a-z' | tr -d '-' | grep -qx "$norm" +} + + + +# In most cases there should be no missing `$LOCALES` + +missing="" + +for l in $LOCALES; do + _locale_present "$l" || missing="$missing $l" +done + +if [ -z "$missing" ]; then + echo "" + echo "All locales present" + echo "" + + exit 0 +fi + + + +# If there are missing `$LOCALES`, just notify the user +# because this script most likely was run as part of `dotfiles-install` +# and therefore without root privileges + +if [ "$(id -u)" != 0 ]; then + + self=$(cd "$(dirname "$0")" && pwd)/$(basename "$0") + + echo "" >&2 + echo "Missing locales:$missing" >&2 + echo "" >&2 + echo "Fix with: sudo $self" >&2 + echo "" >&2 + + exit 1 + +fi + + + +# Install the missing `$LOCALES` + +if ! command -v locale-gen >/dev/null 2>&1; then + + echo "" >&2 + echo "locale-gen not found, generate manually:" >&2 + echo "" >&2 + + for l in $missing; do + echo " localedef -i ${l%.*} -f UTF-8 $l" >&2 + done + + echo "" >&2 + + exit 1 + +fi + + +[ -f /etc/locale.gen ] || : > /etc/locale.gen + + +for l in $missing; do + + # Uncomment existing line, or append if not present at all + + esc=$(printf '%s' "$l" | sed 's/\./\\./g') + + if grep -qE "^#? *$esc UTF-8" /etc/locale.gen; then + sed -i "s/^# *\($esc UTF-8\)/\1/" /etc/locale.gen + else + printf '%s UTF-8\n' "$l" >> /etc/locale.gen + fi + +done + + +locale-gen + + + +# verify all missing `$LOCALES` were installed + +rc=0 + +for l in $missing; do + _locale_present "$l" || { + echo "Failed to generate: $l" >&2; + rc=1; + } +done + +exit $rc diff --git a/.local/bin/scp b/.local/bin/scp new file mode 100755 index 0000000..3ac98d8 --- /dev/null +++ b/.local/bin/scp @@ -0,0 +1,5 @@ +#!/bin/sh + +# See ~/.local/bin/ssh + +exec /usr/bin/scp -o SetEnv=LC_ALL=C.UTF-8 "$@" diff --git a/.local/bin/sftp b/.local/bin/sftp new file mode 100755 index 0000000..5edb7e3 --- /dev/null +++ b/.local/bin/sftp @@ -0,0 +1,5 @@ +#!/bin/sh + +# See ~/.local/bin/ssh + +exec /usr/bin/sftp -o SetEnv=LC_ALL=C.UTF-8 "$@" diff --git a/.local/bin/ssh b/.local/bin/ssh new file mode 100755 index 0000000..3e25687 --- /dev/null +++ b/.local/bin/ssh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Mask the exotic `LC_*` values from ~/.config/shell/locale on remote hosts +# +# Debian's /etc/ssh/ssh_config forwards them via `SendEnv LANG LC_*`, +# which cannot be cleared from ~/.ssh/config +# => Send `LC_ALL` on top, which overrides all `LC_*` and `LANG` remotely +# +# `C.UTF-8` is built into `glibc` and thus available on every host +# +# Found via the `$PATH` by `git`, `rsync`, `sshfs`, scripts, and GUI apps; +# `scp` and `sftp` call /usr/bin/ssh directly and have their own wrappers + +exec /usr/bin/ssh -o SetEnv=LC_ALL=C.UTF-8 "$@"