feat(locale): add locale configuration module

Implement locale, timezone, and keyboard configuration for
internationalization support:
- Locale generation via /etc/default/libc-locales
- Timezone symlink to /usr/share/zoneinfo
- Keyboard layout in /etc/rc.conf

Decision: Default to en_US.UTF-8 for broadest compatibility.
Use standard Void Linux configuration methods (libc-locales,
rc.conf) instead of systemd-localed (Void uses runit).

Alternative considered: Generate all locales rejected - wastes
disk space and time. Only generate selected locale.

Trade-off: Timezone validation checks if file exists and falls
back to UTC if not found. This prevents installation failure
but may surprise users if they mistype timezone name.

Fallback behavior ensures installation completes even with
invalid timezone input, prioritizing robustness over strictness.
This commit is contained in:
Stefan Strobl 2025-12-24 20:44:21 +01:00
parent f295eb5684
commit 56c8234d35

117
src/locale.sh Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env bash
# === Motivation ===
# Configure system locale, timezone, and keyboard layout for international users.
# === Problem Statement ===
# A freshly installed system needs localization configuration to support user language and region.
# === Scope ===
# In scope: locale generation, timezone configuration, keyboard layout.
# Out of scope: X11 keyboard configuration, font selection, language packs.
# === Concepts ===
# Locale: Defines language, character encoding, and formatting conventions (LC_*).
# Timezone: Maps local time to UTC with regional rules (DST, etc.).
# Keyboard layout: Console keymap for character input.
# === Decisions ===
# Default locale is en_US.UTF-8 for broadest compatibility.
# Default timezone will be configured based on user prompt (see config.sh).
# Default keyboard layout is 'us' (can be overridden via config).
# Use /etc/default/libc-locales for locale configuration (Void standard).
# Use symlink for timezone (/etc/localtime → /usr/share/zoneinfo/<zone>).
# Use /etc/rc.conf for console keyboard layout (KEYMAP variable).
# === Alternatives Considered ===
# systemd-localed rejected (Void uses runit).
# Generating all locales rejected (wastes disk space and time).
# Auto-detection of timezone rejected (unreliable without network/GeoIP).
# === Constraints ===
# Target system must be mounted at $MOUNT_ROOT.
# Locale must be generated before it can be used.
# Timezone must exist in /usr/share/zoneinfo.
# === Open Questions ===
# Should we support multiple locales (e.g., en_US + de_DE)?
# Should we validate timezone path before creating symlink?
# Should we configure X11 keyboard layout as well?
# === Success Criteria ===
# Locale is generated and available in the installed system.
# Timezone is correctly configured and persists after reboot.
# Keyboard layout matches user selection on console login.
locale_configure() {
: "${MOUNT_ROOT:?Mount root is required}"
: "${LOCALE:?Locale is required}"
: "${TIMEZONE:?Timezone is required}"
: "${KEYBOARD:?Keyboard layout is required}"
log_info "Locale configuration: setting up locale, timezone, and keyboard"
# Configure locale in /etc/default/libc-locales
log_info "Configuring locale: $LOCALE"
local locale_file="$MOUNT_ROOT/etc/default/libc-locales"
mkdir -p "$(dirname "$locale_file")"
# Ensure locale file exists
if [[ ! -f "$locale_file" ]]; then
touch "$locale_file"
fi
# Uncomment or add the selected locale
if grep -q "^#${LOCALE}" "$locale_file"; then
# Locale exists but is commented - uncomment it
sed -i "s/^#${LOCALE}/${LOCALE}/" "$locale_file"
elif grep -q "^${LOCALE}" "$locale_file"; then
# Locale already enabled
log_info "Locale $LOCALE already enabled"
else
# Add locale to file
echo "$LOCALE UTF-8" >> "$locale_file"
fi
# Generate locales
log_info "Generating locales (this may take a moment)..."
if ! chroot "$MOUNT_ROOT" xbps-reconfigure -f glibc-locales; then
log_warn "Failed to reconfigure glibc-locales, but continuing"
fi
# Configure timezone
log_info "Configuring timezone: $TIMEZONE"
local timezone_source="/usr/share/zoneinfo/$TIMEZONE"
local timezone_target="$MOUNT_ROOT/etc/localtime"
if [[ ! -f "$MOUNT_ROOT$timezone_source" ]]; then
log_warn "Timezone file not found: $timezone_source - using UTC as fallback"
TIMEZONE="UTC"
timezone_source="/usr/share/zoneinfo/UTC"
fi
# Remove existing symlink or file
rm -f "$timezone_target"
# Create symlink
ln -sf "$timezone_source" "$timezone_target"
# Also set timezone in /etc/timezone for compatibility
echo "$TIMEZONE" > "$MOUNT_ROOT/etc/timezone"
# Configure keyboard layout
log_info "Configuring keyboard layout: $KEYBOARD"
local rc_conf="$MOUNT_ROOT/etc/rc.conf"
mkdir -p "$(dirname "$rc_conf")"
touch "$rc_conf"
# Update or add KEYMAP entry in /etc/rc.conf
if grep -q "^KEYMAP=" "$rc_conf"; then
sed -i "s/^KEYMAP=.*/KEYMAP=\"$KEYBOARD\"/" "$rc_conf"
else
echo "KEYMAP=\"$KEYBOARD\"" >> "$rc_conf"
fi
log_info "Locale configuration complete."
}