Compare commits

...
Author SHA1 Message Date
ZacharyZhang-NY e22a13dfeb Ask for the language first, and install in Chinese in a CJK console
The first screen offers English or Simplified Chinese. Chinese reruns
the configurator in cage + foot, which draw Chinese, with Noto Sans
Mono CJK unpacked from the offline mirror's noto-fonts-cjk; no font
joins the live image. There the keyboard list keeps US-keymap layouts
and preselects Chinese (Simplified, Pinyin), every later screen and
the install dashboard read in Chinese, and the target gets
LANG=zh_CN.UTF-8 through locale_config. Without a display or the
font, Chinese continues in English on the VT. The summary shows the
keyboard by its label instead of its keymap.

Needs omarchy's omarchy_prompt_language and omarchy_text in
install/provisioning/setup-form.sh.
2026-10-12 01:21:41 -04:00
9 changed files with 511 additions and 135 deletions

No files matched your search

+2
View File
@@ -26,6 +26,8 @@ For local development, build the ISO from sibling checkouts:
Despite the local folder name, the first argument is the Omarchy source checkout (runtime commands, configs, setup scripts, themes, shell, migrations). The installer itself lives in this ISO repo.
The configurator asks for the language on its first screen: English or Simplified Chinese. Chinese reruns the installer in cage + foot, a console that draws Chinese, with Noto Sans Mono CJK unpacked from the offline mirror's `noto-fonts-cjk`. It opens on the keyboard screen, offers only keyboards with a US keymap (cage keeps a US layout) and preselects Chinese (Simplified, Pinyin); every later screen and the install progress read in Chinese. Esc there goes back to a bilingual language screen, where English returns to the VT, which applies the chosen keymap. Without a working display or the font, Chinese continues in English on the VT. Either way a Chinese install sets the target's `LANG` to `zh_CN.UTF-8` through archinstall's `locale_config`. The language never decides the region: that follows the timezone. The Chinese text lives in `OMARCHY_ZH` tables beside the English it replaces.
Use `--dev` or `--rc` to build against those package channels. Both `--dev` and `--edge` select the dev packages from the edge mirror.
## Autoinstall
+58 -22
View File
@@ -17,6 +17,7 @@
# or Chinese (Simplified, Pinyin)/(Traditional, Zhuyin).
# OMARCHY_PATH must be the source tree used to build the ISO.
# --encrypt Drive the encrypted install flow (default: unencrypted)
# --language LANG Pick en (default) or zh; zh keeps the preselected Pinyin keyboard
# --provision Drive the deferred-provisioning install flow: no user during install, the
# first boot runs omarchy-provision-owner and creates it there
# --reuse-base Skip the install phase if a base image already exists
@@ -42,6 +43,7 @@ MEMORY=8192
INSTALL_TIMEOUT=2400
BOOT_TIMEOUT=600
ENCRYPT=false
INSTALL_LANGUAGE=en
PROVISION=false
REUSE_BASE=false
INSTALL_ONLY=false
@@ -59,6 +61,7 @@ while (($#)); do
case "$1" in
--keyboard) KEYBOARD_LABEL="$2"; shift ;;
--encrypt) ENCRYPT=true ;;
--language) INSTALL_LANGUAGE="$2"; shift ;;
--provision) PROVISION=true ;;
--reuse-base) REUSE_BASE=true ;;
--install-only) INSTALL_ONLY=true ;;
@@ -89,12 +92,32 @@ if [[ ! -f ${ISO:-} ]]; then
exit 1
fi
if [[ $INSTALL_LANGUAGE != "en" && $INSTALL_LANGUAGE != "zh" ]]; then
echo "--language must be en or zh, not $INSTALL_LANGUAGE" >&2
exit 1
fi
if [[ $INSTALL_LANGUAGE == "zh" && $KEYBOARD_LABEL != "English (US)" ]]; then
echo "--language zh takes the preselected Pinyin keyboard; drop --keyboard" >&2
exit 1
fi
case "$KEYBOARD_LABEL" in
"English (US)" | "English (UK)" | "Japanese" | "Japanese (US keyboard)" | "Korean" | "Chinese (Simplified, Pinyin)" | "Chinese (Traditional, Zhuyin)") ;;
*) echo "The VM harness cannot type credentials with keyboard: $KEYBOARD_LABEL" >&2; exit 1 ;;
esac
omarchy-pkg-add qemu-full edk2-ovmf socat imagemagick tesseract tesseract-data-eng
omarchy-pkg-add qemu-full edk2-ovmf socat imagemagick tesseract tesseract-data-eng tesseract-data-chi_sim
# After the language screen a Chinese install reads in Chinese.
OCR_LANGS=eng
[[ $INSTALL_LANGUAGE == "zh" ]] && OCR_LANGS=eng+chi_sim
# lang_text <english> <chinese> picks the text a screen shows in this run.
lang_text() {
if [[ $INSTALL_LANGUAGE == "zh" ]]; then
printf '%s' "$2"
else
printf '%s' "$1"
fi
}
OVMF_CODE="/usr/share/edk2/x64/OVMF_CODE.4m.fd"
OVMF_VARS_TEMPLATE="/usr/share/edk2/x64/OVMF_VARS.4m.fd"
@@ -104,6 +127,7 @@ if [[ $KEYBOARD_LABEL != "English (US)" ]]; then
BASE_NAME+="-$(printf '%s' "$KEYBOARD_LABEL" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')"
fi
$ENCRYPT && BASE_NAME+="-encrypted"
[[ $INSTALL_LANGUAGE == "zh" ]] && BASE_NAME+="-zh"
$PROVISION && BASE_NAME+="-provision"
BASE_DIR="$REPO_DIR/test-runs/$BASE_NAME"
RUN_DIR="$BASE_DIR/runs/$(date +%Y%m%d-%H%M%S)"
@@ -253,7 +277,13 @@ ocr_screen() {
screendump "$shot"
[[ -s $shot ]] || return 0
magick "$shot" -colorspace gray -negate -resize 150% "$prepped" 2>/dev/null || return 0
tesseract "$prepped" - --psm 6 2>/dev/null || true
# The logo throws tesseract off CJK, so Chinese runs also read below it, without spaces.
if [[ $INSTALL_LANGUAGE == "zh" ]]; then
magick "$shot" -gravity north -chop 0x35% -colorspace gray -negate -resize 150% "$prepped.text.png" 2>/dev/null || return 0
{ tesseract "$prepped" - --psm 6 -l "$OCR_LANGS"; tesseract "$prepped.text.png" - --psm 6 -l "$OCR_LANGS"; } 2>/dev/null | sed 'p; s/ //g' || true
else
tesseract "$prepped" - --psm 6 -l "$OCR_LANGS" 2>/dev/null || true
fi
}
# Wait until the console shows the given text. Saves the offending screen as
@@ -660,23 +690,28 @@ drive_configurator() {
capture_console "success-installer-00-greeter"
press ret
# Keyboard is the first screen. Ctrl+C there is the hidden entry into deferred provisioning
# mode; a normal install just selects the layout.
wait_for_screen "keyboard layout" 300
# Language comes first: English on top, Chinese below.
wait_for_screen "Select language" 300
capture_console "success-installer-01-language"
if [[ $INSTALL_LANGUAGE == "zh" ]]; then press end; else press home; fi
press ret
# The keyboard follows, for Chinese in cage + foot; Ctrl+C there defers provisioning.
wait_for_screen "$(lang_text "keyboard layout" "键盘布局")" 300
capture_console "success-installer-01-keyboard-layout"
if $PROVISION; then
press ctrl-c # arm "prepare for another owner"
wait_for_screen "another owner" 30
wait_for_screen "$(lang_text "another owner" "另一位")" 30
capture_console "success-installer-02-prepare-confirm"
press ret # "Yes, prepare for another owner" is the affirmative default
# Jumps straight to disk selection, then the overwrite confirm.
wait_for_screen "install disk" 60
wait_for_screen "$(lang_text "install disk" "安装磁盘")" 60
capture_console "success-installer-03-prepare-install-disk"
press ret
wait_for_screen "recovery possible" 60
wait_for_screen "$(lang_text "recovery possible" "无法恢复")" 60
capture_console "success-installer-04-prepare-disk-warning-encrypted"
if ! $ENCRYPT; then
press ctrl-c
@@ -700,27 +735,27 @@ drive_configurator() {
press ret
# Normal flow: user step comes before disk selection.
wait_for_screen "Username" 120
wait_for_screen "$(lang_text "Username" "用户名")" 120
type_text "$GUEST_USER"
capture_console "success-installer-02-username"
press ret
wait_for_screen "Password" 60
wait_for_screen "$(lang_text "Password" "密码")" 60
type_text "$GUEST_PASSWORD"
capture_console "success-installer-03-password"
press ret
wait_for_screen "Must match" 60
wait_for_screen "$(lang_text "Must match" "确认")" 60
type_text "$GUEST_PASSWORD"
capture_console "success-installer-04-password-confirmation"
press ret
wait_for_screen "Full name" 60
wait_for_screen "$(lang_text "Full name" "姓名")" 60
type_text "Omarchy Test"
capture_console "success-installer-05-full-name"
press ret
wait_for_screen "Email address" 60
wait_for_screen "$(lang_text "Email address" "邮箱")" 60
type_text "test"
if [[ $KEYBOARD_LABEL == "Japanese" ]]; then
press bracket_left
@@ -733,30 +768,30 @@ drive_configurator() {
capture_console "success-installer-06-email"
press ret
wait_for_screen "Hostname" 60
wait_for_screen "$(lang_text "Hostname" "主机名")" 60
type_text "$GUEST_HOSTNAME"
capture_console "success-installer-07-hostname"
press ret
wait_for_screen "Timezone" 180 # tzupdate geo-guess can take a moment
wait_for_screen "$(lang_text "Timezone" "时区")" 180 # tzupdate geo-guess can take a moment
capture_console "success-installer-08-timezone"
press ret
wait_for_screen "look right" 60
wait_for_screen "$(lang_text "look right" "正确")" 60
capture_console "success-installer-09-review"
press ret
wait_for_screen "install disk" 60
wait_for_screen "$(lang_text "install disk" "安装磁盘")" 60
capture_console "success-installer-10-install-disk"
press ret # the single virtio disk
wait_for_screen "installation mode\|recovery possible" 60
if ocr_screen | grep -qi "installation mode"; then
wait_for_screen "$(lang_text "installation mode\|recovery possible" "安装方式\|无法恢复")" 60
if ocr_screen | grep -qi "$(lang_text "installation mode" "安装方式")"; then
capture_console "success-installer-11-install-mode"
press ret # "Full disk install" is preselected
fi
wait_for_screen "recovery possible" 60
wait_for_screen "$(lang_text "recovery possible" "无法恢复")" 60
capture_console "success-installer-12-disk-warning-encrypted"
if ! $ENCRYPT; then
press ctrl-c # toggles the confirm to the unencrypted flow
@@ -860,7 +895,8 @@ wait_for_install() {
while true; do
text=$(ocr_screen)
if grep -qi "Reboot Now" <<<"$text"; then
# OCR cannot read the Chinese reboot button, so Chinese runs match the title above it.
if grep -qi "$(lang_text "Reboot Now" "安装完成")" <<<"$text"; then
log "Install finished. Confirming the reboot prompt."
capture_console "success-installer-15-reboot"
press ret
@@ -876,7 +912,7 @@ wait_for_install() {
return 0
fi
if grep -qi "installation stopped" <<<"$text"; then
if grep -qi "$(lang_text "installation stopped" "安装已停止")" <<<"$text"; then
capture_console "failure-installer-installation-stopped"
echo "Install failed — screenshot saved to $RUN_DIR" >&2
return 1
+2 -1
View File
@@ -121,7 +121,8 @@ cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
# The selected omarchy-settings package is needed here so its post_install hook
# drops Omarchy's plymouthd.conf into /etc/plymouth before mkarchiso builds the
# live initramfs.
arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted)
# cage and foot give Chinese installs a console that draws Chinese.
arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted cage foot)
printf '%s\n' "${arch_packages[@]}" >> "$build_cache_dir/packages.x86_64"
# The live ISO boots linux-t2 (see airootfs/etc/mkinitcpio.d/linux-t2.preset), so
+58 -3
View File
@@ -10,7 +10,10 @@
# - COLUMNS/LINES so gum picks up real terminal size
set -euo pipefail
[[ $(tty) == /dev/tty1 ]] || exit 0
[[ $(tty) == /dev/tty1 || -n ${OMARCHY_CJK_CONSOLE:-} ]] || exit 0
if [[ -n ${OMARCHY_CJK_CONSOLE:-} ]]; then
touch "$XDG_RUNTIME_DIR/cjk-console-started"
fi
export OMARCHY_MIRROR="$(cat /root/omarchy_mirror)"
if [[ -f /root/omarchy_iso_ref ]]; then
@@ -39,7 +42,8 @@ set_tokyo_night_colors() {
echo -en "\033[0m"
clear
}
set_tokyo_night_colors
# foot sets the same palette from foot.ini; these escapes are VT-only.
[[ -n ${OMARCHY_CJK_CONSOLE:-} ]] || set_tokyo_night_colors
mkdir -p /var/log
touch "$OMARCHY_INSTALL_LOG_FILE"
@@ -90,6 +94,16 @@ warm_offline_mirror &
warm_pid=$!
trap 'kill "$warm_pid" 2>/dev/null' EXIT
# The CJK console's font comes from the offline mirror's noto-fonts-cjk, not the live image.
unpack_cjk_font() {
local packages=(/var/cache/omarchy/mirror/offline/noto-fonts-cjk-*.pkg.tar.zst)
if ((${#packages[@]} != 1)) || [[ ! -f ${packages[0]} ]]; then
echo "Expected one noto-fonts-cjk package in the offline mirror, found: ${packages[*]}" >&2
return 1
fi
bsdtar -xf "${packages[0]}" -C / usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc
}
cd /root
# Autoinstall: a cidata drive carrying the configurator's own output files
# stands in for the wizard. omarchy-cidata-load copies them into /root and
@@ -98,7 +112,43 @@ if /usr/local/bin/omarchy-cidata-load; then
echo "Autoinstall configuration found on cidata drive; skipping the configurator."
export OMARCHY_UI_INTERACTIVE=no
else
./configurator
# Chinese moves to cage + foot, which draw Chinese; English moves back to the VT, which applies keymaps.
cjk_relaunch=10
vt_return=11
if [[ -n ${OMARCHY_CJK_CONSOLE:-} ]]; then
export OMARCHY_VT_RETURN=$vt_return
elif command -v cage >/dev/null; then
export OMARCHY_CJK_RELAUNCH=$cjk_relaunch
fi
./configurator && status=0 || status=$?
while ((status == cjk_relaunch)); do
cjk_runtime=/run/omarchy-cjk-console
mkdir -p -m 700 "$cjk_runtime"
rm -f "$cjk_runtime/cjk-console-started" "$cjk_runtime/return-to-vt"
if ! unpack_cjk_font; then
echo "The Chinese font could not be unpacked from the offline mirror; continuing in English."
unset OMARCHY_CJK_RELAUNCH
OMARCHY_INSTALL_LANGUAGE=zh ./configurator && status=0 || status=$?
continue
fi
cage_status=0
env -u OMARCHY_CJK_RELAUNCH XDG_RUNTIME_DIR=$cjk_runtime OMARCHY_CJK_CONSOLE=1 OMARCHY_INSTALL_LANGUAGE=zh \
LANG=C.UTF-8 WLR_RENDERER=pixman cage -- foot -c /usr/share/omarchy-iso/foot.ini "$0" || cage_status=$?
if [[ -f $cjk_runtime/return-to-vt ]]; then
OMARCHY_INSTALL_LANGUAGE=en ./configurator && status=0 || status=$?
elif [[ -f $cjk_runtime/cjk-console-started ]]; then
exit "$cage_status"
else
echo "The Chinese console could not start (cage exit $cage_status); continuing in English."
unset OMARCHY_CJK_RELAUNCH
OMARCHY_INSTALL_LANGUAGE=zh ./configurator && status=0 || status=$?
fi
done
if [[ -n ${OMARCHY_CJK_CONSOLE:-} ]] && ((status == vt_return)); then
touch "$XDG_RUNTIME_DIR/return-to-vt"
exit 0
fi
((status == 0)) || exit "$status"
fi
# Deferred-provisioning installs skip the celebration/reboot prompt and reboot on
@@ -111,6 +161,11 @@ if [[ -f /root/defer-provisioning ]] ||
export OMARCHY_UI_DEFER_PROVISIONING=yes
fi
# Only Chinese reaches the install from the CJK console; English returns to the VT.
if [[ -n ${OMARCHY_CJK_CONSOLE:-} ]]; then
export OMARCHY_INSTALL_LANG=zh
fi
# The foreground dashboard is now the sole visible install UI owner. It starts
# the actual installer as a non-interactive child, logs child output, waits for
# completion, then renders the final installed-time/reboot prompt itself.
+180 -67
View File
@@ -24,6 +24,72 @@ if [[ ! -r $SETUP_FORM ]]; then
fi
source "$SETUP_FORM"
# Chinese text for OMARCHY_INSTALL_LANG=zh; disk-step failures stay English for support logs.
OMARCHY_ZH+=(
["Aborted installation"]="安装已中止"
["You can retry later by running: ./.automated_script.sh"]="稍后可运行 ./.automated_script.sh 重试"
["Let's setup your machine..."]="开始设置这台机器……"
["Press Ctrl+C to prepare this machine for another owner."]="按 Ctrl+C 为另一位使用者准备这台机器。"
["This prepares the machine for another owner."]="这会为另一位使用者准备这台机器。"
["The system installs now, but setup is delayed until first boot."]="系统现在安装,设置留到首次开机。"
["Yes, prepare for another owner"]="是,为他人准备"
["No, keep setting up"]="否,继续设置"
["Prepare this machine for another owner?"]="为另一位使用者准备这台机器?"
["Let's setup your user account..."]="开始设置用户账户……"
["Field"]="项目"
["Value"]="值"
["Username"]="用户名"
["Password"]="密码"
["Full name"]="姓名"
["Email address"]="邮箱"
["Hostname"]="主机名"
["Keyboard"]="键盘"
["Region"]="区域"
["[Skipped]"]="[已跳过]"
["Yes"]="是"
["No, change it"]="否,去修改"
["Does this look right?"]="以上信息正确吗?"
["Not enough free space on %s"]="%s 的空闲空间不足"
["%s has %s of usable free space; Omarchy needs at least 32GB."]="%s 有 %s 可用空闲空间;Omarchy 至少需要 32GB。"
["Open the partition tool to free at least 32GB on %s, then try again."]="打开分区工具,在 %s 上腾出至少 32GB,然后重试。"
["Back"]="返回"
["Open partition tool"]="打开分区工具"
["Return to installation mode?"]="返回安装方式选择?"
["Checking for BitLocker on %s"]="检查 %s 上的 BitLocker"
["BitLocker signature detected on %s."]="在 %s 上发现 BitLocker 签名。"
["Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again."]="在 Windows 中关闭 BitLocker,等磁盘解密完成后重试。"
["Suspending BitLocker is not enough — the drive stays encrypted."]="只暂停 BitLocker 不够,磁盘仍是加密的。"
["Aborted: BitLocker is enabled on this disk."]="已中止:这块磁盘开启了 BitLocker。"
["Checking existing EFI partitions on %s"]="检查 %s 上已有的 EFI 分区"
["Found a Windows ESP at %s — leaving it untouched."]="在 %s 发现 Windows ESP,保持不动。"
["Omarchy will create its own dedicated EFI partition in free space."]="Omarchy 会在空闲空间中创建自己的 EFI 分区。"
["A new EFI partition will be created in free space."]="会在空闲空间中新建 EFI 分区。"
["Analyzing free space on %s"]="分析 %s 的空闲空间"
["Install Omarchy in the %s of free space."]="在 %s 空闲空间中安装 Omarchy。"
["Press Ctrl+C for unencrypted install."]="按 Ctrl+C 改为不加密安装。"
["Yes, install"]="是,开始安装"
["Yes, install without encryption"]="是,不加密安装"
["Confirm installing on %s"]="确认安装到 %s"
["Creating partitions on %s"]="在 %s 上创建分区"
["Setting up LUKS2 on %s"]="在 %s 上设置 LUKS2 加密"
["Creating Btrfs filesystem and subvolumes"]="创建 Btrfs 文件系统和子卷"
["Let's select where to install Omarchy..."]="选择 Omarchy 的安装位置……"
["Select install disk"]="选择安装磁盘"
["Partition tool for %s"]="%s 的分区工具"
["Create unallocated free space for Omarchy, then write changes and quit."]="为 Omarchy 腾出未分配空间,然后写入更改并退出。"
["Do not create an Omarchy partition here — leave the target area as Free space."]="不要在这里创建 Omarchy 分区,目标区域保持为 Free space。"
["Tip: free-space install needs at least 32GB unallocated, plus 2GB more if no ESP already exists."]="提示:空闲空间安装至少需要 32GB 未分配空间;没有 ESP 时再加 2GB。"
["Open cfdisk"]="打开 cfdisk"
["Open partition tool for %s?"]="打开 %s 的分区工具?"
["Full disk install"]="整盘安装"
["Free space install (alongside existing data)"]="空闲空间安装(保留现有数据)"
["Choose a different disk"]="换一块磁盘"
["Let's select how to install Omarchy..."]="选择 Omarchy 的安装方式……"
["Select installation mode on %s"]="选择 %s 上的安装方式"
["Everything will be overwritten. There is no recovery possible."]="磁盘上的一切都会被覆盖,无法恢复。"
["Confirm overwriting %s"]="确认覆盖 %s"
)
# Partition creation helpers, shared with test/partition-numbering-test.sh so
# the numbering logic can be exercised without a real disk.
DISK_PARTITIONING="${DISK_PARTITIONING:-/usr/share/omarchy-iso/disk-partitioning.sh}"
@@ -176,9 +242,9 @@ greeter() {
}
abort() {
gum style "${1:-Aborted installation}"
gum style "${1:-$(omarchy_text "Aborted installation")}"
echo
gum style "You can retry later by running: ./.automated_script.sh"
gum style "$(omarchy_text "You can retry later by running: ./.automated_script.sh")"
exit 1
}
@@ -203,6 +269,30 @@ notice() {
# STEP 1: KEYBOARD LAYOUT
# The target's LANG follows the language picked on the first screen.
target_lang() {
if [[ $language == "zh" ]]; then
echo zh_CN.UTF-8
else
echo en_US.UTF-8
fi
}
# .automated_script.sh moves Chinese into the CJK console and English back to the VT, which applies keymaps.
apply_language() {
if [[ $language == "zh" && -n ${OMARCHY_CJK_RELAUNCH:-} ]]; then
exit "$OMARCHY_CJK_RELAUNCH"
fi
if [[ $language == "en" && -n ${OMARCHY_VT_RETURN:-} ]]; then
exit "$OMARCHY_VT_RETURN"
fi
if [[ $language == "zh" && -n ${OMARCHY_CJK_CONSOLE:-} ]]; then
export OMARCHY_INSTALL_LANG=zh
else
unset OMARCHY_INSTALL_LANG
fi
}
keyboard_form() {
# Ctrl+C is the hidden entry into deferred provisioning, offered on every
# visit: the user step unwinds back here and nothing is on disk yet. Callers
@@ -212,11 +302,24 @@ keyboard_form() {
while true; do
clear_logo
echo
say "Let's setup your machine..."
say --foreground 8 "Press Ctrl+C to prepare this machine for another owner."
say "$(omarchy_text "Let's setup your machine...")"
say --foreground 8 "$(omarchy_text "Press Ctrl+C to prepare this machine for another owner.")"
echo
omarchy_prompt_keyboard && status=0 || status=$?
status=0
if ! $language_chosen; then
omarchy_prompt_language && status=0 || status=$?
((status == 0)) && apply_language
fi
# Esc, Ctrl+C or a later "No, change it" asks the language again.
language_chosen=false
if ((status == 0)); then
local layouts=$OMARCHY_KEYBOARD_LAYOUTS selected="English (US)"
[[ $language == "zh" ]] && selected="Chinese (Simplified, Pinyin)"
# cage keeps its US layout, so the CJK console offers only US-keymap keyboards.
[[ -n ${OMARCHY_CJK_CONSOLE:-} ]] && layouts=$(awk -F'|' '$2 == "us"' <<<"$layouts")
OMARCHY_KEYBOARD_LAYOUTS=$layouts omarchy_prompt_keyboard "$selected" && status=0 || status=$?
fi
((status == 0)) && break
# Esc means "back", and nothing precedes the first screen, so re-ask.
@@ -245,11 +348,11 @@ keyboard_form() {
confirm_prepare_for_another_owner() {
clear_logo
echo
say "This prepares the machine for another owner."
say --foreground 8 "The system installs now, but setup is delayed until first boot."
say "$(omarchy_text "This prepares the machine for another owner.")"
say --foreground 8 "$(omarchy_text "The system installs now, but setup is delayed until first boot.")"
echo
gum confirm --affirmative "Yes, prepare for another owner" --negative "No, keep setting up" \
"Prepare this machine for another owner?"
gum confirm --affirmative "$(omarchy_text "Yes, prepare for another owner")" --negative "$(omarchy_text "No, keep setting up")" \
"$(omarchy_text "Prepare this machine for another owner?")"
}
# The user step. Deferred-provisioning installs skip it entirely — the machine's first owner
@@ -257,7 +360,7 @@ confirm_prepare_for_another_owner() {
# operator sets nothing user-specific.
user_form() {
step "Let's setup your user account..."
step "$(omarchy_text "Let's setup your user account...")"
# Each prompt reports 0 (set), OMARCHY_FORM_BACK (Esc), or OMARCHY_FORM_SIGNAL
# (Ctrl+C). Both non-zero cases unwind to user_step, which decides what they
@@ -295,22 +398,24 @@ user_step() {
region=$(cd /usr/share/omarchy-iso 2>/dev/null && python -m orchestrator.region "$timezone") || region="global"
region_row=""
if [[ $region != "global" ]]; then
region_row="\nRegion,$region"
region_row="\n$(omarchy_text Region),$region"
fi
# Add manual padding since gum table -p doesn't respect padding
echo -e "Field,Value
Username,$username
Password,$(printf "%${#password}s" | tr ' ' '*')
Full name,${full_name:-[Skipped]}
Email address,${email_address:-[Skipped]}
Hostname,$hostname
Timezone,$timezone$region_row
Keyboard,$keyboard" |
local skipped
skipped=$(omarchy_text "[Skipped]")
echo -e "$(omarchy_text Field),$(omarchy_text Value)
$(omarchy_text Username),$username
$(omarchy_text Password),$(printf "%${#password}s" | tr ' ' '*')
$(omarchy_text "Full name"),${full_name:-$skipped}
$(omarchy_text "Email address"),${email_address:-$skipped}
$(omarchy_text Hostname),$hostname
$(omarchy_text Timezone),$timezone$region_row
$(omarchy_text Keyboard),\"$keyboard_label\"" |
gum table -s "," -p | sed "s/^/${PADDING_LEFT_SPACES}/"
echo
if gum confirm --negative "No, change it" "Does this look right?"; then
if gum confirm --affirmative "$(omarchy_text "Yes")" --negative "$(omarchy_text "No, change it")" "$(omarchy_text "Does this look right?")"; then
break
else
keyboard_form
@@ -512,11 +617,11 @@ print_dry_run_files() {
# action so users have a way to actually fix the situation.
not_enough_space() {
local available="${1:-0}"
step "Not enough free space on $disk"
say --foreground 1 "$disk has $(to_gb $available) of usable free space; Omarchy needs at least 32GB."
say "Open the partition tool to free at least 32GB on $disk, then try again."
step "$(omarchy_text "Not enough free space on %s" "$disk")"
say --foreground 1 "$(omarchy_text "%s has %s of usable free space; Omarchy needs at least 32GB." "$disk" "$(to_gb $available)")"
say "$(omarchy_text "Open the partition tool to free at least 32GB on %s, then try again." "$disk")"
echo
if ! gum confirm --affirmative "Back" --negative "Open partition tool" "Return to installation mode?"; then
if ! gum confirm --affirmative "$(omarchy_text "Back")" --negative "$(omarchy_text "Open partition tool")" "$(omarchy_text "Return to installation mode?")"; then
open_partition_tool
fi
return 1
@@ -526,14 +631,14 @@ not_enough_space() {
# encryption confirm. Sets the globals run_partition_execute needs. Returns 1
# to fall back to the install-mode picker.
run_partition_decide() {
step "Checking for BitLocker on $disk"
step "$(omarchy_text "Checking for BitLocker on %s" "$disk")"
local bl_part
bl_part=$(detect_bitlocker || true)
if [[ -n "$bl_part" ]]; then
say --foreground 1 "BitLocker signature detected on $bl_part."
say "Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again."
say "Suspending BitLocker is not enough — the drive stays encrypted."
abort "Aborted: BitLocker is enabled on this disk."
say --foreground 1 "$(omarchy_text "BitLocker signature detected on %s." "$bl_part")"
say "$(omarchy_text "Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again.")"
say "$(omarchy_text "Suspending BitLocker is not enough — the drive stays encrypted.")"
abort "$(omarchy_text "Aborted: BitLocker is enabled on this disk.")"
fi
# Omarchy always creates its own dedicated ESP in free space and never
@@ -542,17 +647,17 @@ run_partition_decide() {
# Windows ESP is far too small for our Unified Kernel Images, and a shared
# ESP forces the install unencrypted. A separate Linux ESP keeps Windows
# and Omarchy fully isolated and keeps LUKS on the table.
step "Checking existing EFI partitions on $disk"
step "$(omarchy_text "Checking existing EFI partitions on %s" "$disk")"
local detected_win_esp
detected_win_esp=$(detect_windows_esp || true)
if [[ -n "$detected_win_esp" ]]; then
say "Found a Windows ESP at $detected_win_esp — leaving it untouched."
say "Omarchy will create its own dedicated EFI partition in free space."
say "$(omarchy_text "Found a Windows ESP at %s — leaving it untouched." "$detected_win_esp")"
say "$(omarchy_text "Omarchy will create its own dedicated EFI partition in free space.")"
else
say "A new EFI partition will be created in free space."
say "$(omarchy_text "A new EFI partition will be created in free space.")"
fi
step "Analyzing free space on $disk"
step "$(omarchy_text "Analyzing free space on %s" "$disk")"
partprobe "$disk" 2>/dev/null || true
sleep 1
@@ -627,21 +732,21 @@ run_partition_decide() {
while true; do
clear_logo
echo
say "Install Omarchy in the $(to_gb $INSTALL_MAX_B) of free space."
say "$(omarchy_text "Install Omarchy in the %s of free space." "$(to_gb $INSTALL_MAX_B)")"
case $mode in
encrypted)
say --foreground 8 "Press Ctrl+C for unencrypted install."
affirmative="Yes, install"
say --foreground 8 "$(omarchy_text "Press Ctrl+C for unencrypted install.")"
affirmative="$(omarchy_text "Yes, install")"
;;
unencrypted)
affirmative="Yes, install without encryption"
affirmative="$(omarchy_text "Yes, install without encryption")"
;;
esac
echo
gum confirm --affirmative "$affirmative" --negative "No, change it" \
"Confirm installing on $disk"
gum confirm --affirmative "$affirmative" --negative "$(omarchy_text "No, change it")" \
"$(omarchy_text "Confirm installing on %s" "$disk")"
confirm_status=$?
case $confirm_status in
@@ -699,7 +804,7 @@ run_partition_execute() {
say "[dry] partition numbers are assigned by parted at creation, then read back"
say "[dry] encrypted=$encrypt_installation kernel=$kernel_choice"
else
step "Creating partitions on $disk"
step "$(omarchy_text "Creating partitions on %s" "$disk")"
if $needs_mklabel; then
disk_step "initializing GPT on $disk" parted --script "$disk" mklabel gpt
partprobe "$disk" 2>/dev/null || true
@@ -737,7 +842,7 @@ run_partition_execute() {
disk_step "clearing stale signatures on $root_partition_device" wipefs -af "$root_partition_device"
if [[ "$encrypt_installation" == "true" ]]; then
step "Setting up LUKS2 on $root_partition_device"
step "$(omarchy_text "Setting up LUKS2 on %s" "$root_partition_device")"
# Kept as pipes rather than routed through disk_step: the passphrase must
# not become an argv the process table can show. Folding stderr into
# stdout still lands any error in the install log.
@@ -754,7 +859,7 @@ run_partition_execute() {
root_mapper="$root_partition_device"
fi
step "Creating Btrfs filesystem and subvolumes"
step "$(omarchy_text "Creating Btrfs filesystem and subvolumes")"
disk_step "creating the Btrfs filesystem on $root_mapper" \
mkfs.btrfs -f -L OMARCHY "$root_mapper"
wait_for_device "$root_mapper" || disk_abort_hook "Root device $root_mapper never appeared"
@@ -842,7 +947,7 @@ run_partition_execute() {
"locale_config": {
"kb_layout": "$keyboard",
"sys_enc": "UTF-8",
"sys_lang": "en_US.UTF-8"
"sys_lang": "$(target_lang)"
},
"mirror_config": {
"custom_repositories": [],
@@ -872,7 +977,7 @@ _EOF_
}
disk_form() {
step "Let's select where to install Omarchy..."
step "$(omarchy_text "Let's select where to install Omarchy...")"
# Don't offer the install media as an option (Arch ISO mounts it here)
local boot_source
@@ -898,19 +1003,19 @@ disk_form() {
fi
done <<<"$available_disks"
selected_display=$(echo "$disk_options" | gum choose --header "Select install disk") || abort
selected_display=$(echo "$disk_options" | gum choose --header "$(omarchy_text "Select install disk")") || abort
disk=$(echo "$selected_display" | awk '{print $1}')
}
# STEP 4: INSTALL MODE
open_partition_tool() {
step "Partition tool for $disk"
gum style "Create unallocated free space for Omarchy, then write changes and quit."
gum style --foreground 8 "Do not create an Omarchy partition here — leave the target area as Free space."
gum style --foreground 8 "Tip: free-space install needs at least 32GB unallocated, plus 2GB more if no ESP already exists."
step "$(omarchy_text "Partition tool for %s" "$disk")"
gum style "$(omarchy_text "Create unallocated free space for Omarchy, then write changes and quit.")"
gum style --foreground 8 "$(omarchy_text "Do not create an Omarchy partition here — leave the target area as Free space.")"
gum style --foreground 8 "$(omarchy_text "Tip: free-space install needs at least 32GB unallocated, plus 2GB more if no ESP already exists.")"
echo
gum confirm --affirmative "Open cfdisk" --negative "Back" "Open partition tool for $disk?" || return 0
gum confirm --affirmative "$(omarchy_text "Open cfdisk")" --negative "$(omarchy_text "Back")" "$(omarchy_text "Open partition tool for %s?" "$disk")" || return 0
clear
cfdisk "$disk" || true
@@ -943,17 +1048,17 @@ requires_full_disk_install() {
}
install_mode_form() {
local choices=("Full disk install")
local choices=("$(omarchy_text "Full disk install")")
# The free-space/protected path registers an EFI boot entry and is UEFI-only.
# Full-disk installs still support BIOS through the orchestrator's BIOS branch.
if [[ -d /sys/firmware/efi ]] && ! $full_disk_only; then
choices+=("Free space install (alongside existing data)")
choices+=("$(omarchy_text "Free space install (alongside existing data)")")
fi
choices+=("Choose a different disk")
choices+=("$(omarchy_text "Choose a different disk")")
step "Let's select how to install Omarchy..."
install_mode=$(gum choose --header "Select installation mode on $disk" "${choices[@]}") || abort
step "$(omarchy_text "Let's select how to install Omarchy...")"
install_mode=$(gum choose --header "$(omarchy_text "Select installation mode on %s" "$disk")" "${choices[@]}") || abort
}
confirm_disk_overwrite() {
@@ -963,17 +1068,17 @@ confirm_disk_overwrite() {
while true; do
clear_logo
echo
say "Everything will be overwritten. There is no recovery possible."
say "$(omarchy_text "Everything will be overwritten. There is no recovery possible.")"
if [[ $mode == "encrypted" ]]; then
say --foreground 8 "Press Ctrl+C for unencrypted install."
affirmative="Yes, install"
say --foreground 8 "$(omarchy_text "Press Ctrl+C for unencrypted install.")"
affirmative="$(omarchy_text "Yes, install")"
else
affirmative="Yes, install without encryption"
affirmative="$(omarchy_text "Yes, install without encryption")"
fi
echo
gum confirm --affirmative "$affirmative" --negative "No, change it" "Confirm overwriting ${disk}"
gum confirm --affirmative "$affirmative" --negative "$(omarchy_text "No, change it")" "$(omarchy_text "Confirm overwriting %s" "$disk")"
confirm_status=$?
case $confirm_status in
@@ -1012,13 +1117,13 @@ select_installation() {
# With no free-space option to offer, a full-disk install is the only
# mode, so skip the picker and go straight to the overwrite confirm.
if $full_disk_only; then
install_mode="Full disk install"
install_mode=$(omarchy_text "Full disk install")
else
install_mode_form
fi
case "$install_mode" in
"Full disk install")
"$(omarchy_text "Full disk install")")
if confirm_disk_overwrite; then
install_target="full_disk"
return 0
@@ -1027,13 +1132,13 @@ select_installation() {
# wants a different disk.
$full_disk_only && disk_form
;;
"Free space install"*)
"$(omarchy_text "Free space install (alongside existing data)")")
if run_partition_decide; then
install_target="free_space"
return 0
fi
;;
"Choose a different disk")
"$(omarchy_text "Choose a different disk")")
disk_form
;;
esac
@@ -1044,12 +1149,20 @@ select_installation() {
defer_provisioning=false
install_target="full_disk"
# A rerun arrives with the language chosen; otherwise English.
language="${OMARCHY_INSTALL_LANGUAGE:-en}"
language_chosen=false
# Let the live VT reach its settled width before the first screen so the logo
# isn't measured against the transient 80-column boot console.
wait_for_stable_terminal
greeter
if [[ -n ${OMARCHY_INSTALL_LANGUAGE:-} ]]; then
apply_language
language_chosen=true
else
greeter
fi
keyboard_form
@@ -1235,7 +1348,7 @@ cat <<-_EOF_ >user_configuration.json
"locale_config": {
"kb_layout": "$keyboard",
"sys_enc": "UTF-8",
"sys_lang": "en_US.UTF-8"
"sys_lang": "$(target_lang)"
},
"mirror_config": {
"custom_repositories": [],
@@ -88,6 +88,81 @@ tips=(
"Keep the system fresh with Update in the Omarchy menu"
)
# omarchy_text and its Chinese table come from the setup form the configurator uses.
SETUP_FORM="${SETUP_FORM:-/usr/share/omarchy-iso/setup-form.sh}"
[[ -r $SETUP_FORM ]] || SETUP_FORM="${OMARCHY_PATH:-/usr/share/omarchy}/install/provisioning/setup-form.sh"
source "$SETUP_FORM"
OMARCHY_ZH+=(
["Tip: %s"]="提示:%s"
["Installed Omarchy"]="Omarchy 安装完成"
["Installed Omarchy in %s"]="Omarchy 安装完成,用时 %s"
["Reboot Now"]="立即重启"
["Last log lines:"]="日志末尾:"
["Last target log lines:"]="目标系统日志末尾:"
["Press Enter to continue…"]="按回车继续……"
["No log uploader is available in this environment."]="这个环境没有日志上传工具。"
["Install log: %s"]="安装日志:%s"
["Target log: %s"]="目标系统日志:%s"
["Omarchy installation stopped"]="Omarchy 安装已停止"
["Installer exited with status %s"]="安装程序退出,状态码 %s"
["Get help at https://omarchy.org/discord"]="求助:https://omarchy.org/discord"
["What would you like to do?"]="接下来做什么?"
["Upload log for support"]="上传日志求助"
["View full log"]="查看完整日志"
["Drop to shell"]="进入 shell"
["Reboot"]="重启"
["Power off"]="关机"
["The install medium was misread"]="安装介质读取出错"
["This package is intact on this ISO, so the download and the write were fine:"]="这个软件包在 ISO 中完好,下载和写入都没有问题:"
["Try another USB port or another stick, and test this machine's memory"]="换一个 USB 口或换一个 U 盘,并检测这台机器的内存"
["The install medium is damaged"]="安装介质已损坏"
["This package no longer matches the checksum it was built with:"]="这个软件包和构建时的校验和不再一致:"
["Re-download the ISO, verify it with sha256sum, and write the USB again"]="重新下载 ISO,用 sha256sum 校验,再重新写入 U 盘"
["If a verified ISO fails here too, the USB stick itself is the problem"]="如果校验过的 ISO 在这里也失败,问题出在 U 盘本身"
["The install medium is damaged or misread"]="安装介质已损坏或读取出错"
["pacman rejected this package, read straight off this ISO:"]="pacman 拒绝了这个直接从 ISO 读出的软件包:"
["Verify your download with sha256sum, then write the USB again"]="用 sha256sum 校验下载的文件,再重新写入 U 盘"
["If the download verifies, the stick or this machine's memory is at fault"]="如果下载校验无误,问题出在 U 盘或这台机器的内存"
)
if [[ ${OMARCHY_INSTALL_LANG:-} == "zh" ]]; then
whimsy=(
"备好贴心的默认设置"
"省掉装机的折腾"
"挑选打磨过的软件包"
"调配协调的配色"
"准备悦目的像素"
"唤醒 Hyprland"
"接好顺手的界面"
"搭起漂亮的系统"
"写好趁手的命令"
"让机器焕然一新"
"擦亮每一个像素"
"调好终端的品味"
"织出流畅的工作流"
)
tips=(
"Super + Space 打开 Omarchy 菜单:应用、设置等"
"Super + K 查看全部快捷键"
"Super 是键盘上的 Windows 或 Command 键"
"用 Xournal++ 给 PDF 签名"
"用 LocalSend 和手机、电脑互传文件"
"菜单 安装 > 网页应用 把任意网站变成应用"
"Super + Return 打开终端,Super + Shift + Return 打开浏览器"
"图片用 Pinta,视频用 Kdenlive,文档用 LibreOffice"
"Super + Ctrl + Print 用 OCR 抓取屏幕文字"
"Print 截屏,Alt + Print 录屏"
"Super + Ctrl + R 设置提醒"
"菜单 风格 > 主题 切换主题"
"双击菜单栏让它透明"
"菜单 安装 > Windows 运行完整的 Windows 虚拟机"
"Super + Ctrl + V 打开剪贴板管理器"
"Super + 1 到 0 切换工作区,加 Shift 带上窗口"
"Super + Print 拾取屏幕上任意位置的颜色"
"用菜单里的 更新 让系统保持最新"
)
fi
cleanup() {
printf '%s%s' "$RESET" "$SHOW_CURSOR" >"$TTY_PATH" 2>/dev/null || true
}
@@ -286,8 +361,8 @@ left_padding() {
visible_len() {
local text="$1"
text="$(printf '%b' "$text" | sed -E $'s/\x1b\\[[0-9;?]*[A-Za-z]//g')"
printf '%s' "${#text}"
# wc -L counts display columns, so double-width CJK centers too.
printf '%b' "$text" | sed -E $'s/\x1b\\[[0-9;?]*[A-Za-z]//g' | wc -L
}
blank_line() { printf '\r%s\n' "$CLEAR_LINE"; }
@@ -678,7 +753,7 @@ render_dynamic() {
render_icon "$DISPLAY_PM"
message="$(truncate_for_width "$(current_whimsy)" "$PROGRESS_TEXT_WIDTH")"
tip="$(truncate_for_width "Tip: $(current_tip)" "$PROGRESS_TEXT_WIDTH")"
tip="$(truncate_for_width "$(omarchy_text "Tip: %s" "$(current_tip)")" "$PROGRESS_TEXT_WIDTH")"
printf '%s%d;1H' "$CSI" "$((TOP_ROW + ICON_HEIGHT + 1))"
center "${GREEN}$((DISPLAY_PM / 10))%${RESET}" "$PROGRESS_TEXT_WIDTH"
center "${GREEN}${message}${RESET}" "$PROGRESS_TEXT_WIDTH"
@@ -718,9 +793,13 @@ install_duration() {
}
render_finish() {
local duration effect_canvas_width title="Installed Omarchy"
local duration effect_canvas_width title
duration="$(install_duration || true)"
[[ -n $duration ]] && title+=" in ${duration}"
if [[ -n $duration ]]; then
title=$(omarchy_text "Installed Omarchy in %s" "$duration")
else
title=$(omarchy_text "Installed Omarchy")
fi
# ttfx centers its text two columns right of plain centering. A canvas two
# columns narrower aligns the animation with the title and fallback logo.
effect_canvas_width="$(term_cols)"
@@ -765,7 +844,7 @@ render_finish() {
}
reboot_prompt() {
local prompt_pad
local prompt_pad label
# Nobody to confirm; the OMARCHY_UI_AUTO_REBOOT check at the call site still
# applies, so setting both stops on the finish screen without a keyboard.
@@ -776,24 +855,31 @@ reboot_prompt() {
# gum allows. FINISH_HEIGHT budgets for the same three rows.
printf '%s%d;1H%s' "$CSI" "$((FINISH_TOP_ROW + LOGO_HEIGHT + 2))" "$CLEAR_LINE" >"$TTY_PATH"
prompt_pad=$(( ( $(term_cols) - 18 ) / 2 ))
# gum pads the button label by four columns on each side.
label=$(omarchy_text "Reboot Now")
prompt_pad=$(( ( $(term_cols) - $(visible_len "$label") - 8 ) / 2 ))
(( prompt_pad < 0 )) && prompt_pad=0
gum confirm \
--padding "0 0 0 $prompt_pad" \
--show-help=false \
--default \
--affirmative "Reboot Now" \
--affirmative "$label" \
--negative "" \
"" <"$TTY_PATH" >"$TTY_PATH" 2>&1
}
# Columns, not characters: each CJK character takes two.
truncate_for_width() {
local text="$1" width="$2"
if (( ${#text} > width )); then
printf '%s…' "${text:0:$((width - 1))}"
else
if (( $(printf '%s' "$text" | wc -L) <= width )); then
printf '%s' "$text"
return
fi
text=${text:0:$((width - 1))}
while (( $(printf '%s' "$text" | wc -L) > width - 1 )); do
text=${text%?}
done
printf '%s…' "$text"
}
render_failure_log_tail() {
@@ -819,8 +905,8 @@ render_failure_log_tail() {
fi
[[ -f $tail_log ]] || return 0
label="Last log lines:"
[[ $tail_log == /mnt/* ]] && label="Last target log lines:"
label=$(omarchy_text "Last log lines:")
[[ $tail_log == /mnt/* ]] && label=$(omarchy_text "Last target log lines:")
line_at "$CONTENT_WIDTH" 2 "${DIM}${label}${RESET}"
printf '\n'
tail -n "$lines" "$tail_log" 2>/dev/null | while IFS= read -r line; do
@@ -843,7 +929,7 @@ find_log_uploader() {
}
prompt_enter() {
printf '\nPress Enter to continue…' >"$TTY_PATH"
printf '\n%s' "$(omarchy_text "Press Enter to continue…")" >"$TTY_PATH"
IFS= read -r _ <"$TTY_PATH" || true
}
@@ -854,9 +940,9 @@ upload_failure_log() {
"$uploader" install >"$TTY_PATH" 2>&1 || true
else
{
echo "No log uploader is available in this environment."
echo "Install log: $LOG_FILE"
[[ -f /mnt/var/log/omarchy-install.log ]] && echo "Target log: /mnt/var/log/omarchy-install.log"
printf '%s\n' "$(omarchy_text "No log uploader is available in this environment.")"
printf '%s\n' "$(omarchy_text "Install log: %s" "$LOG_FILE")"
[[ -f /mnt/var/log/omarchy-install.log ]] && printf '%s\n' "$(omarchy_text "Target log: %s" /mnt/var/log/omarchy-install.log)"
} >"$TTY_PATH"
fi
prompt_enter
@@ -907,6 +993,8 @@ render_media_diagnosis() {
blank_line
continue
fi
# The support log keeps English; the screen shows the translation, and package names pass through.
[[ ${OMARCHY_INSTALL_LANG:-} == "zh" ]] && line=${OMARCHY_ZH[$line]:-$line}
if (( first )); then
center "${RED}$(truncate_for_width "$line" "$CONTENT_WIDTH")${RESET}" "$CONTENT_WIDTH"
first=0
@@ -922,8 +1010,8 @@ render_failure() {
blank_line
render_logo
blank_line
center "${RED}Omarchy installation stopped${RESET}" "$CONTENT_WIDTH"
center "Installer exited with status $status" "$CONTENT_WIDTH"
center "${RED}$(omarchy_text "Omarchy installation stopped")${RESET}" "$CONTENT_WIDTH"
center "$(omarchy_text "Installer exited with status %s" "$status")" "$CONTENT_WIDTH"
render_media_diagnosis "$media_diagnosis"
@@ -938,12 +1026,12 @@ render_failure() {
blank_line
render_failure_log_tail "$(media_diagnosis_rows "$media_diagnosis")"
blank_line
center "${DIM}Get help at https://omarchy.org/discord${RESET}" "$CONTENT_WIDTH"
center "${DIM}$(omarchy_text "Get help at https://omarchy.org/discord")${RESET}" "$CONTENT_WIDTH"
blank_line
}
failure_menu() {
local choice uploader
local choice uploader actions shown i
[[ ${OMARCHY_UI_FAILURE_ACTION:-} == "exit" ]] && return 0
# The failure screen and log tail have already rendered; exit with the
@@ -953,26 +1041,20 @@ failure_menu() {
while true; do
uploader=""
uploader=$(find_log_uploader || true)
if [[ -n $uploader ]]; then
choice=$(gum choose \
--height 6 \
--header "What would you like to do?" \
"Upload log for support" \
"View full log" \
"Drop to shell" \
"Reboot" \
"Power off" \
<"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
else
choice=$(gum choose \
--height 5 \
--header "What would you like to do?" \
"View full log" \
"Drop to shell" \
"Reboot" \
"Power off" \
<"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
fi
actions=("View full log" "Drop to shell" "Reboot" "Power off")
[[ -n $uploader ]] && actions=("Upload log for support" "${actions[@]}")
shown=()
for i in "${!actions[@]}"; do
shown+=("$(omarchy_text "${actions[i]}")")
done
choice=$(gum choose \
--height "$((${#actions[@]} + 1))" \
--header "$(omarchy_text "What would you like to do?")" \
"${shown[@]}" \
<"$TTY_PATH" 2>"$TTY_PATH") || choice=""
for i in "${!shown[@]}"; do
[[ ${shown[i]} == "$choice" ]] && choice=${actions[i]}
done
case "$choice" in
"Upload log for support")
@@ -0,0 +1,29 @@
# The Chinese installer's console: the VT's Tokyo Night palette, with Noto Sans Mono CJK from the offline mirror.
[main]
font=Noto Sans Mono CJK SC:size=13
# Noto's tall default rows leave the summary too few lines for the logo.
line-height=22px
[colors-dark]
background=1a1b26
foreground=a9b1d6
regular0=1a1b26
regular1=f7768e
regular2=9ece6a
regular3=e0af68
regular4=7aa2f7
regular5=bb9af7
regular6=7dcfff
regular7=a9b1d6
bright0=414868
bright1=f7768e
bright2=9ece6a
bright3=e0af68
bright4=7aa2f7
bright5=bb9af7
bright6=7dcfff
bright7=c0caf5
# cage draws no title bar, and a reserved one leaves a black strip.
[csd]
preferred=none
+7 -1
View File
@@ -273,6 +273,12 @@ mkdir -p "$omarchy_share"
printf 'logo bottom row%*s\n' 66 ''
} >"$omarchy_share/logo.txt"
# The ISO vendors the runtime's setup form; the dashboard only needs its text lookup.
cat >"$work/setup-form.sh" <<'FORM'
declare -gA OMARCHY_ZH=()
omarchy_text() { local text=$1; shift; printf -- "$text" "$@"; }
FORM
run_dashboard() {
local install_log="$1"
@@ -284,7 +290,7 @@ run_dashboard() {
"error": "Pacstrap failed. See /var/log/archinstall.log"}]}
STATE
: >"$screen"
script -qefc "stty rows 40 cols 120; PATH='$stubs:$PATH' OMARCHY_PATH='$omarchy_share' OMARCHY_UI_INTERACTIVE=no OMARCHY_UI_FAILURE_ACTION=exit OMARCHY_FAILURE_TAIL_LOG='$install_log' '$DASHBOARD' '$install_log' '$state_file' -- bash -c 'exit 1'" \
script -qefc "stty rows 40 cols 120; PATH='$stubs:$PATH' OMARCHY_PATH='$omarchy_share' SETUP_FORM='$work/setup-form.sh' OMARCHY_UI_INTERACTIVE=no OMARCHY_UI_FAILURE_ACTION=exit OMARCHY_FAILURE_TAIL_LOG='$install_log' '$DASHBOARD' '$install_log' '$state_file' -- bash -c 'exit 1'" \
"$screen" >/dev/null 2>&1
}
+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
#
# Every installer text needs an OMARCHY_ZH entry, or a Chinese install shows it in English.
set -euo pipefail
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
SETUP_FORM="${OMARCHY_SOURCE:-$ROOT/../omarchy}/install/provisioning/setup-form.sh"
fail() {
printf 'not ok - %s\n' "$1"
[[ -z ${2:-} ]] || printf '%s\n' "$2" | sed 's/^/# /'
exit 1
}
if [[ ! -r $SETUP_FORM ]]; then
printf 'ok - # skip the setup form is not checked out next to this repo (set OMARCHY_SOURCE)\n'
exit 0
fi
source "$SETUP_FORM"
for file in configs/airootfs/root/configurator configs/airootfs/usr/local/bin/omarchy-install-dashboard; do
eval "$(sed -n '/^OMARCHY_ZH+=(/,/^)/p' "$ROOT/$file")"
done
mapfile -t texts < <(
grep -ho 'omarchy_text "[^"$]*"' "$ROOT/configs/airootfs/root/configurator" "$ROOT/configs/airootfs/usr/local/bin/omarchy-install-dashboard" |
sed 's/^omarchy_text "//; s/"$//' | sort -u
)
((${#texts[@]} > 50)) || fail "the installer routes its text through omarchy_text" "found ${#texts[@]}"
for text in "${texts[@]}"; do
[[ -n ${OMARCHY_ZH[$text]:-} ]] || fail "every installer text has a Chinese entry" "missing: $text"
done
printf 'ok - every installer and dashboard text has a Chinese entry\n'
for action in "View full log" "Drop to shell" "Reboot" "Power off" "Upload log for support"; do
[[ -n ${OMARCHY_ZH[$action]:-} ]] || fail "labels passed through variables have Chinese entries" "missing: $action"
done
printf 'ok - labels passed through variables have Chinese entries\n'
mapfile -t diagnosis < <(grep -x ' *echo "[^"$]*"' "$ROOT/configs/airootfs/usr/local/bin/omarchy-install-diagnose-media" | sed 's/^ *echo "//; s/"$//')
((${#diagnosis[@]} >= 10)) || fail "the media diagnosis sentences are found" "found ${#diagnosis[@]}"
for line in "${diagnosis[@]}"; do
[[ -n ${OMARCHY_ZH[$line]:-} ]] || fail "every media diagnosis sentence has a Chinese entry" "missing: $line"
done
printf 'ok - every media diagnosis sentence has a Chinese entry\n'
[[ $(OMARCHY_INSTALL_LANG=zh omarchy_text "Confirm overwriting %s" /dev/vda) == "确认覆盖 /dev/vda" ]] ||
fail "Chinese text keeps its printf arguments"
[[ $(omarchy_text "Confirm overwriting %s" /dev/vda) == "Confirm overwriting /dev/vda" ]] ||
fail "English stays the default"
printf 'ok - omarchy_text formats Chinese and English alike\n'