More GNOME Settings parity.
Region & Language is new. The locale is localectl's, and Panama stores no
copy of it -- there is exactly one system locale, so a preference here
would be a second source of truth that drifts the moment anything else
changes it. Codes are resolved against iso-codes into "Portuguese
(Brazil)" the way GNOME does, with the code kept visible because it is
what actually gets written and someone choosing between two Spanish
variants needs to see it. Changing it is privileged and only applies to
programs started afterwards, so the page says a sign-out is needed
rather than claiming the new language is in use.
The service is called SystemLocale, not Locale: QML has a built-in
Locale value type that silently shadows a singleton of that name, and
every binding then reads properties off the wrong thing. The page
rendered empty with nothing but "cannot read property of undefined" to
explain it.
About now answers what GNOME's About answers -- model, processor,
memory, disk, OS, kernel, windowing system -- where before it listed
only Panama's own component versions. Graphics is joined from
GraphicsDevices rather than read again, because two readouts of the same
hardware are two things that can disagree. Placeholder DMI strings
("To Be Filled By O.E.M.") are filtered out, and unreadable facts are
omitted rather than shown as "Unknown".
The Fedora hand-off card was one row listing five subjects that opened
the network panel regardless. Naming a panel and then not opening it
reads as a broken button rather than a deliberate hand-off. Each subject
now opens the panel that owns it, and openGnomePanel takes an optional
subpage so "Users" reaches System's users page the way GNOME's own
desktop entry does, instead of dropping the user on System's front page.
Printers and online accounts are not repeated here; they stay with the
network hardware on Network & Devices.
One bug this surfaced, caught by the hyprland write contract: the Lua
config key and the hyprctl option name genuinely differ for tap to
click. hl.config wants input.touchpad.tap_to_click; getoption answers to
input:touchpad:tap-to-click. Either spelling used for both fails -- a
hyphen is not a Lua identifier, and the underscored name is not a known
option -- which is what the schema's two separate fields are for.
Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
99 lines
3.7 KiB
Bash
Executable File
99 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# System locale, via localectl.
|
|
#
|
|
# panama-locale list -> [{value, label, detail}]
|
|
# panama-locale get -> the current LANG, e.g. en_US.UTF-8
|
|
# panama-locale set <locale>
|
|
#
|
|
# Locale codes are not names. "pt_BR.UTF-8" tells you what it means only if you
|
|
# already know, which defeats the point of a picker, so codes are resolved
|
|
# against the iso-codes database into "Portuguese (Brazil)" the way GNOME does.
|
|
# The code stays visible as the row's detail, because it is what actually gets
|
|
# written and someone choosing between two Spanish variants needs to see it.
|
|
#
|
|
# The join happens in a single jq pass. Doing it per locale meant 327 jq
|
|
# invocations, which took long enough to be visible when opening the page.
|
|
#
|
|
# Setting the locale is a privileged operation: localectl goes through polkit,
|
|
# which prompts. It also only takes effect for programs started afterwards, so
|
|
# the caller is responsible for saying a sign-out is needed -- this script does
|
|
# not pretend the running session changed.
|
|
|
|
set -uo pipefail
|
|
|
|
readonly ISO_LANG=/usr/share/iso-codes/json/iso_639-2.json
|
|
readonly ISO_COUNTRY=/usr/share/iso-codes/json/iso_3166-1.json
|
|
|
|
cmd_get() {
|
|
localectl status 2>/dev/null \
|
|
| awk -F'LANG=' '/System Locale:/ { print $2; exit }' \
|
|
| tr -d '[:space:]'
|
|
}
|
|
|
|
cmd_list() {
|
|
local locales
|
|
locales="$(localectl list-locales 2>/dev/null)" || locales=""
|
|
if [[ -z "$locales" ]]; then
|
|
printf '[]\n'
|
|
return 0
|
|
fi
|
|
|
|
# Without iso-codes installed the codes are still perfectly usable; they
|
|
# just do not get friendly names. That is a degraded list, not a failure.
|
|
if [[ ! -r "$ISO_LANG" || ! -r "$ISO_COUNTRY" ]]; then
|
|
jq -Rn --rawfile raw /dev/stdin \
|
|
'[$raw | split("\n")[] | select(length > 0) | {value: ., label: ., detail: ""}]' \
|
|
<<<"$locales"
|
|
return 0
|
|
fi
|
|
|
|
jq -Rn \
|
|
--slurpfile languages "$ISO_LANG" \
|
|
--slurpfile countries "$ISO_COUNTRY" \
|
|
--rawfile raw /dev/stdin '
|
|
# alpha_2 -> name, for both databases. Languages without a two-letter
|
|
# code cannot appear in a locale name, so they are simply absent.
|
|
($languages[0]["639-2"] | map(select(.alpha_2)) | INDEX(.alpha_2) | map_values(.name)) as $lang
|
|
| ($countries[0]["3166-1"] | INDEX(.alpha_2) | map_values(.name)) as $country
|
|
| [ $raw
|
|
| split("\n")[]
|
|
| select(length > 0)
|
|
| . as $value
|
|
# en_US.UTF-8 -> ["en", "US"]; the codeset and any @modifier are
|
|
# not part of the human name.
|
|
| ($value | split(".")[0] | split("@")[0] | split("_")) as $parts
|
|
| ($lang[$parts[0]] // $parts[0]) as $language
|
|
| (if ($parts | length) > 1 then $country[$parts[1]] else null end) as $region
|
|
| {
|
|
value: $value,
|
|
label: (if $region then "\($language) (\($region))" else $language end),
|
|
detail: $value
|
|
}
|
|
]
|
|
| sort_by(.label)
|
|
' <<<"$locales"
|
|
}
|
|
|
|
cmd_set() {
|
|
local locale="${1:-}"
|
|
# Constrained rather than passed through: this reaches a privileged
|
|
# command, and the set of legal locale names is narrow and well known.
|
|
[[ "$locale" =~ ^[[email protected]]+$ ]] || {
|
|
printf 'panama-locale: refusing a locale name with unexpected characters\n' >&2
|
|
return 2
|
|
}
|
|
localectl list-locales 2>/dev/null | grep -qxF "$locale" || {
|
|
printf 'panama-locale: %s is not an installed locale\n' "$locale" >&2
|
|
return 2
|
|
}
|
|
localectl set-locale "LANG=$locale"
|
|
}
|
|
|
|
case "${1:-list}" in
|
|
list) cmd_list ;;
|
|
get) cmd_get ;;
|
|
set) shift; cmd_set "${1:-}" ;;
|
|
*) printf 'usage: panama-locale [list|get|set <locale>]\n' >&2; exit 2 ;;
|
|
esac
|