diff --git a/builder/build-iso.sh b/builder/build-iso.sh index db8daa2..5e4ca44 100755 --- a/builder/build-iso.sh +++ b/builder/build-iso.sh @@ -118,7 +118,7 @@ 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) +arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted cage foot noto-fonts-cjk) 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 diff --git a/configs/airootfs/root/.automated_script.sh b/configs/airootfs/root/.automated_script.sh index 16904f8..f1ec36e 100644 --- a/configs/airootfs/root/.automated_script.sh +++ b/configs/airootfs/root/.automated_script.sh @@ -10,7 +10,29 @@ # - COLUMNS/LINES so gum picks up real terminal size set -euo pipefail -[[ $(tty) == /dev/tty1 ]] || exit 0 +[[ $(tty) == /dev/tty1 || -n ${OMARCHY_CN_GUI:-} ]] || exit 0 + +# OmarchyCN: run the installer in a CJK-capable graphical console when KMS +# allows; otherwise fall back to the original English VT flow. +if [[ -z ${OMARCHY_CN_GUI:-} ]] && command -v cage > /dev/null 2>&1; then + export XDG_RUNTIME_DIR=/run/omarchy-gui + mkdir -p "$XDG_RUNTIME_DIR" + chmod 700 "$XDG_RUNTIME_DIR" + rm -f "$XDG_RUNTIME_DIR/gui-started" + gui_status=0 + OMARCHY_CN_GUI=1 OMARCHY_INSTALL_LANG=zh WLR_RENDERER=pixman \ + cage -- foot -F "$0" || gui_status=$? + if [[ -f $XDG_RUNTIME_DIR/gui-started ]]; then + # GUI 已运行:退出码属于安装器,不再回退英文流程 + exit "$gui_status" + fi + echo "图形安装环境不可用,回退英文控制台 (falling back to the English VT installer)" + unset OMARCHY_INSTALL_LANG XDG_RUNTIME_DIR +fi + +if [[ -n ${OMARCHY_CN_GUI:-} ]]; then + touch "$XDG_RUNTIME_DIR/gui-started" +fi export OMARCHY_MIRROR="$(cat /root/omarchy_mirror)" if [[ -f /root/omarchy_iso_ref ]]; then diff --git a/configs/airootfs/root/configurator b/configs/airootfs/root/configurator index 4cd3db3..96c507e 100644 --- a/configs/airootfs/root/configurator +++ b/configs/airootfs/root/configurator @@ -9,6 +9,167 @@ OMARCHY_SETTINGS_PACKAGE="${OMARCHY_SETTINGS_PACKAGE:-omarchy-settings}" LOGO_PATH="$OMARCHY_PATH/logo.txt" +# Installer strings; the graphical console stage exports OMARCHY_INSTALL_LANG=zh +if [[ ${OMARCHY_INSTALL_LANG:-} == zh ]]; then + SYS_LANG="zh_CN.UTF-8" + L_TAGLINE='华丽、现代、观点鲜明的 Linux —— 中国版' + L_HINT='按回车开始安装' + L_ABORTED='安装已中止' + L_RETRY='稍后可重新运行: ./.automated_script.sh' + L_OWNER_PREP='这将把本机准备给另一位使用者。' + L_OWNER_PREP2='系统现在安装,但初始设置延迟到首次开机进行。' + L_OWNER_CONFIRM='为另一位使用者准备这台机器?' + L_OWNER_YES='是,为他人准备' + L_OWNER_NO='否,继续本人设置' + L_USER_STEP='开始设置你的用户账户……' + L_SUMMARY_OK='以上信息正确吗?' + L_SUMMARY_NO='不对,去修改' + L_F_FIELD='项目' + L_F_VALUE='值' + L_F_USER='用户名' + L_F_PASS='密码' + L_F_NAME='姓名' + L_F_EMAIL='邮箱' + L_F_HOST='主机名' + L_F_TZ='时区' + L_F_KB='键盘' + L_F_SKIP='[已跳过]' + L_NOSPACE_STEP='磁盘 %s 可用空间不足' + L_NOSPACE_1='%s 可用空间 %s;Omarchy 至少需要 32GB。' + L_NOSPACE_2='请用分区工具在该盘腾出至少 32GB,然后重试。' + L_NOSPACE_CONFIRM='返回安装方式选择?' + L_BACK='返回' + L_OPEN_PT='打开分区工具' + L_BL_STEP='检查 %s 上的 BitLocker' + L_BL_1='检测到 %s 存在 BitLocker 签名。' + L_BL_2='请在 Windows 中关闭 BitLocker 并等待解密完成后重试。' + L_BL_3='仅暂停 BitLocker 不够——磁盘仍处于加密状态。' + L_BL_ABORT='已中止:该磁盘启用了 BitLocker。' + L_EFI_STEP='检查 %s 上已有的 EFI 分区' + L_EFI_WIN='发现 Windows ESP 位于 %s——保持原样不动。' + L_EFI_OWN='Omarchy 将在空闲空间中创建自己的 EFI 分区。' + L_EFI_NEW='将在空闲空间中新建 EFI 分区。' + L_FREE_STEP='分析 %s 的空闲空间' + L_MKPART_STEP='在 %s 上创建分区' + L_LUKS_STEP='在 %s 上配置 LUKS2 加密' + L_BTRFS_STEP='创建 Btrfs 文件系统与子卷' + L_FREESAY='在 %s 空闲空间中安装 Omarchy。' + L_CTRLC_UNENC='按 Ctrl+C 改为不加密安装。' + L_YES_INSTALL='是,开始安装' + L_YES_NOENC='是,不加密安装' + L_NO_CHANGE='否,去修改' + L_CONFIRM_ON='确认安装到 %s' + L_DISK_STEP='选择要安装 Omarchy 的磁盘……' + L_DISK_HEADER='选择安装磁盘' + L_PT_STEP='%s 的分区工具' + L_PT_1='为 Omarchy 腾出未分配空间,然后写入更改并退出。' + L_PT_2='不要在这里创建 Omarchy 分区——目标区域保持为空闲空间即可。' + L_MODE_FULL='整盘安装' + L_MODE_FREE='空闲空间安装(保留现有数据)' + L_MODE_OTHER='换一块磁盘' + L_MODE_STEP='选择 Omarchy 的安装方式……' + L_MODE_HEADER='选择 %s 上的安装方式' + L_WIPE_WARN='磁盘将被完全覆写,数据无法恢复。' + L_WIPE_CONFIRM='确认覆写 %s' + L_TAGLINE_W=38 + L_HINT_W=14 + L_WELCOME='开始设置你的机器……' + L_OWNER_HINT='按 Ctrl+C 可改为「为他人准备这台机器」。' + L_CFDISK_CONFIRM='打开 %s 的分区工具?' + L_OPEN_CFDISK='打开 cfdisk' + L_DS_GPT='初始化 %s 的 GPT' + L_DS_ESPFLAG='标记分区 %s 为 ESP' + L_DS_WIPE='清除 %s 的残留签名' + L_DS_MKBTRFS='在 %s 创建 Btrfs 文件系统' + L_DS_MOUNT='挂载 %s' + L_DS_SUBVOL='创建子卷 %s' + L_DS_MOUNTROOT='挂载目标根分区' + L_DS_MOUNTHOME='挂载 /home' + L_DS_MOUNTLOG='挂载 /var/log' + L_DS_MOUNTCACHE='挂载软件包缓存' + L_DS_MKESP='在 %s 创建 ESP 文件系统' + L_DS_MOUNTESP='挂载 ESP' +else + SYS_LANG="en_US.UTF-8" + L_TAGLINE='Beautiful, Modern & Opinionated Linux by DHH' + L_HINT='Press Return to Start Install' + L_ABORTED='Aborted installation' + L_RETRY='You can retry later by running: ./.automated_script.sh' + L_OWNER_PREP='This prepares the machine for another owner.' + L_OWNER_PREP2='The system installs now, but setup is delayed until first boot.' + L_OWNER_CONFIRM='Prepare this machine for another owner?' + L_OWNER_YES='Yes, prepare for another owner' + L_OWNER_NO='No, keep setting up' + L_USER_STEP='Let'\''s setup your user account...' + L_SUMMARY_OK='Does this look right?' + L_SUMMARY_NO='No, change it' + L_F_FIELD='Field' + L_F_VALUE='Value' + L_F_USER='Username' + L_F_PASS='Password' + L_F_NAME='Full name' + L_F_EMAIL='Email address' + L_F_HOST='Hostname' + L_F_TZ='Timezone' + L_F_KB='Keyboard' + L_F_SKIP='[Skipped]' + L_NOSPACE_STEP='Not enough free space on %s' + L_NOSPACE_1='%s has %s of usable free space; Omarchy needs at least 32GB.' + L_NOSPACE_2='Open the partition tool to free at least 32GB, then try again.' + L_NOSPACE_CONFIRM='Return to installation mode?' + L_BACK='Back' + L_OPEN_PT='Open partition tool' + L_BL_STEP='Checking for BitLocker on %s' + L_BL_1='BitLocker signature detected on %s.' + L_BL_2='Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again.' + L_BL_3='Suspending BitLocker is not enough — the drive stays encrypted.' + L_BL_ABORT='Aborted: BitLocker is enabled on this disk.' + L_EFI_STEP='Checking existing EFI partitions on %s' + L_EFI_WIN='Found a Windows ESP at %s — leaving it untouched.' + L_EFI_OWN='Omarchy will create its own dedicated EFI partition in free space.' + L_EFI_NEW='A new EFI partition will be created in free space.' + L_FREE_STEP='Analyzing free space on %s' + L_MKPART_STEP='Creating partitions on %s' + L_LUKS_STEP='Setting up LUKS2 on %s' + L_BTRFS_STEP='Creating Btrfs filesystem and subvolumes' + L_FREESAY='Install Omarchy in the %s of free space.' + L_CTRLC_UNENC='Press Ctrl+C for unencrypted install.' + L_YES_INSTALL='Yes, install' + L_YES_NOENC='Yes, install without encryption' + L_NO_CHANGE='No, change it' + L_CONFIRM_ON='Confirm installing on %s' + L_DISK_STEP='Let'\''s select where to install Omarchy...' + L_DISK_HEADER='Select install disk' + L_PT_STEP='Partition tool for %s' + L_PT_1='Create unallocated free space for Omarchy, then write changes and quit.' + L_PT_2='Do not create an Omarchy partition here — leave the target area as Free space.' + L_MODE_FULL='Full disk install' + L_MODE_FREE='Free space install (alongside existing data)' + L_MODE_OTHER='Choose a different disk' + L_MODE_STEP='Let'\''s select how to install Omarchy...' + L_MODE_HEADER='Select installation mode on %s' + L_WIPE_WARN='Everything will be overwritten. There is no recovery possible.' + L_WIPE_CONFIRM='Confirm overwriting %s' + L_TAGLINE_W=44 + L_HINT_W=29 + L_WELCOME="Let's setup your machine..." + L_OWNER_HINT='Press Ctrl+C to prepare this machine for another owner.' + L_CFDISK_CONFIRM='Open partition tool for %s?' + L_OPEN_CFDISK='Open cfdisk' + L_DS_GPT='initializing GPT on %s' + L_DS_ESPFLAG='flagging partition %s as ESP' + L_DS_WIPE='clearing stale signatures on %s' + L_DS_MKBTRFS='creating the Btrfs filesystem on %s' + L_DS_MOUNT='mounting %s' + L_DS_SUBVOL='creating subvolume %s' + L_DS_MOUNTROOT='mounting the target root' + L_DS_MOUNTHOME='mounting /home' + L_DS_MOUNTLOG='mounting /var/log' + L_DS_MOUNTCACHE='mounting the package cache' + L_DS_MKESP='creating the ESP filesystem on %s' + L_DS_MOUNTESP='mounting the ESP' +fi + # The setup form — keyboard, account, hostname, and timezone questions, plus the # rules their answers are checked against — shared verbatim with the first-boot # owner setup that finishes a deferred install. build-iso.sh vendors it out of @@ -122,12 +283,12 @@ greeter() { printf '\033[%d;1H' "$logo_row" gum style --foreground 2 --padding "0 0 0 $PADDING_LEFT" "$(<"$LOGO_PATH")" - tagline="Beautiful, Modern & Opinionated Linux by DHH" - tpad=$(((cols - ${#tagline}) / 2)); (( tpad < 0 )) && tpad=0 + tagline="$L_TAGLINE" + tpad=$(((cols - L_TAGLINE_W) / 2)); (( tpad < 0 )) && tpad=0 printf '\033[%d;%dH%s' "$tagline_row" "$((tpad + 1))" "$tagline" - hint="Press Return to Start Install" - hpad=$(((cols - ${#hint}) / 2)); (( hpad < 0 )) && hpad=0 + hint="$L_HINT" + hpad=$(((cols - L_HINT_W) / 2)); (( hpad < 0 )) && hpad=0 printf '\033[%d;%dH\033[2m%s\033[0m' "$hint_row" "$((hpad + 1))" "$hint" # ColorShift the logo: a green base (indexed color 2) with a cyan accent (6) @@ -176,9 +337,9 @@ greeter() { } abort() { - gum style "${1:-Aborted installation}" + gum style "${1:-$L_ABORTED}" echo - gum style "You can retry later by running: ./.automated_script.sh" + gum style "$L_RETRY" exit 1 } @@ -212,8 +373,8 @@ 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 "$L_WELCOME" + say --foreground 8 "$L_OWNER_HINT" echo omarchy_prompt_keyboard && status=0 || status=$? @@ -245,11 +406,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 "$L_OWNER_PREP" + say --foreground 8 "$L_OWNER_PREP2" echo - gum confirm --affirmative "Yes, prepare for another owner" --negative "No, keep setting up" \ - "Prepare this machine for another owner?" + gum confirm --affirmative "$L_OWNER_YES" --negative "$L_OWNER_NO" \ + "$L_OWNER_CONFIRM" } # The user step. Deferred-provisioning installs skip it entirely — the machine's first owner @@ -257,7 +418,7 @@ confirm_prepare_for_another_owner() { # operator sets nothing user-specific. user_form() { - step "Let's setup your user account..." + step "$L_USER_STEP" # 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 @@ -291,18 +452,18 @@ user_step() { 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 -Keyboard,$keyboard" | + echo -e "$L_F_FIELD,$L_F_VALUE +$L_F_USER,$username +$L_F_PASS,$(printf "%${#password}s" | tr ' ' '*') +$L_F_NAME,${full_name:-$L_F_SKIP} +$L_F_EMAIL,${email_address:-$L_F_SKIP} +$L_F_HOST,$hostname +$L_F_TZ,$timezone +$L_F_KB,$keyboard" | 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 --negative "$L_SUMMARY_NO" "$L_SUMMARY_OK"; then break else keyboard_form @@ -504,11 +665,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 "$(printf "$L_NOSPACE_STEP" "$disk")" + say --foreground 1 "$(printf "$L_NOSPACE_1" "$disk" "$(to_gb $available)")" + say "$L_NOSPACE_2" echo - if ! gum confirm --affirmative "Back" --negative "Open partition tool" "Return to installation mode?"; then + if ! gum confirm --affirmative "$L_BACK" --negative "$L_OPEN_PT" "$L_NOSPACE_CONFIRM"; then open_partition_tool fi return 1 @@ -518,14 +679,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 "$(printf "$L_BL_STEP" "$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 "$(printf "$L_BL_1" "$bl_part")" + say "$L_BL_2" + say "$L_BL_3" + abort "$L_BL_ABORT" fi # Omarchy always creates its own dedicated ESP in free space and never @@ -534,17 +695,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 "$(printf "$L_EFI_STEP" "$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 "$(printf "$L_EFI_WIN" "$detected_win_esp")" + say "$L_EFI_OWN" else - say "A new EFI partition will be created in free space." + say "$L_EFI_NEW" fi - step "Analyzing free space on $disk" + step "$(printf "$L_FREE_STEP" "$disk")" partprobe "$disk" 2>/dev/null || true sleep 1 @@ -619,21 +780,21 @@ run_partition_decide() { while true; do clear_logo echo - say "Install Omarchy in the $(to_gb $INSTALL_MAX_B) of free space." + say "$(printf "$L_FREESAY" "$(to_gb $INSTALL_MAX_B)")" case $mode in encrypted) - say --foreground 8 "Press Ctrl+C for unencrypted install." - affirmative="Yes, install" + say --foreground 8 "$L_CTRLC_UNENC" + affirmative="$L_YES_INSTALL" ;; unencrypted) - affirmative="Yes, install without encryption" + affirmative="$L_YES_NOENC" ;; esac echo - gum confirm --affirmative "$affirmative" --negative "No, change it" \ - "Confirm installing on $disk" + gum confirm --affirmative "$affirmative" --negative "$L_NO_CHANGE" \ + "$(printf "$L_CONFIRM_ON" "$disk")" confirm_status=$? case $confirm_status in @@ -691,9 +852,9 @@ 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 "$(printf "$L_MKPART_STEP" "$disk")" if $needs_mklabel; then - disk_step "initializing GPT on $disk" parted --script "$disk" mklabel gpt + disk_step "$(printf "$L_DS_GPT" "$disk")" parted --script "$disk" mklabel gpt partprobe "$disk" 2>/dev/null || true sleep 1 fi @@ -709,7 +870,7 @@ run_partition_execute() { disk_abort_hook "Could not create the root partition on $disk" root_part_num="$created_partition_number" - disk_step "flagging partition $efi_part_num as ESP" \ + disk_step "$(printf "$L_DS_ESPFLAG" "$efi_part_num")" \ parted --script "$disk" set "$efi_part_num" esp on efi_dev=$(partition_path "$disk" "$efi_part_num") @@ -725,11 +886,11 @@ run_partition_execute() { # Both partitions are ours and brand new, but the space they occupy may # carry signatures from whatever was deleted to free it. - disk_step "clearing stale signatures on $efi_dev" wipefs -af "$efi_dev" - disk_step "clearing stale signatures on $root_partition_device" wipefs -af "$root_partition_device" + disk_step "$(printf "$L_DS_WIPE" "$efi_dev")" wipefs -af "$efi_dev" + disk_step "$(printf "$L_DS_WIPE" "$root_partition_device")" wipefs -af "$root_partition_device" if [[ "$encrypt_installation" == "true" ]]; then - step "Setting up LUKS2 on $root_partition_device" + step "$(printf "$L_LUKS_STEP" "$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. @@ -746,31 +907,31 @@ run_partition_execute() { root_mapper="$root_partition_device" fi - step "Creating Btrfs filesystem and subvolumes" - disk_step "creating the Btrfs filesystem on $root_mapper" \ + step "$L_BTRFS_STEP" + disk_step "$(printf "$L_DS_MKBTRFS" "$root_mapper")" \ mkfs.btrfs -f -L OMARCHY "$root_mapper" wait_for_device "$root_mapper" || disk_abort_hook "Root device $root_mapper never appeared" mkdir -p /mnt/btrfs-root - disk_step "mounting $root_mapper" mount "$root_mapper" /mnt/btrfs-root + disk_step "$(printf "$L_DS_MOUNT" "$root_mapper")" mount "$root_mapper" /mnt/btrfs-root for subvol in @ @home @log @pkg; do - disk_step "creating subvolume $subvol" btrfs subvolume create "/mnt/btrfs-root/$subvol" + disk_step "$(printf "$L_DS_SUBVOL" "$subvol")" btrfs subvolume create "/mnt/btrfs-root/$subvol" done umount /mnt/btrfs-root rmdir /mnt/btrfs-root - disk_step "mounting the target root" \ + disk_step "$L_DS_MOUNTROOT" \ mount -o noatime,compress=zstd,subvol=@ "$root_mapper" /mnt mkdir -p /mnt/home /mnt/var/log /mnt/var/cache/pacman/pkg /mnt"$esp_mount_in_target" - disk_step "mounting /home" \ + disk_step "$L_DS_MOUNTHOME" \ mount -o noatime,compress=zstd,subvol=@home "$root_mapper" /mnt/home - disk_step "mounting /var/log" \ + disk_step "$L_DS_MOUNTLOG" \ mount -o noatime,compress=zstd,subvol=@log "$root_mapper" /mnt/var/log - disk_step "mounting the package cache" \ + disk_step "$L_DS_MOUNTCACHE" \ mount -o noatime,compress=zstd,subvol=@pkg "$root_mapper" /mnt/var/cache/pacman/pkg - disk_step "creating the ESP filesystem on $efi_dev" mkfs.fat -F32 -n OMARCHY_EFI "$efi_dev" - disk_step "mounting the ESP" mount "$efi_dev" /mnt"$esp_mount_in_target" + disk_step "$(printf "$L_DS_MKESP" "$efi_dev")" mkfs.fat -F32 -n OMARCHY_EFI "$efi_dev" + disk_step "$L_DS_MOUNTESP" mount "$efi_dev" /mnt"$esp_mount_in_target" # The orchestrator's first act is to verify this handoff. Check it here so # a failure names the step that broke rather than surfacing later as @@ -798,7 +959,10 @@ run_partition_execute() { "auth_config": {}, "audio_config": { "audio": "pipewire" }, "bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false }, - "custom_commands": [], + "custom_commands": [ + "sed -i -e s/^#zh_CN.UTF-8/zh_CN.UTF-8/ -e s/^#en_US.UTF-8/en_US.UTF-8/ /etc/locale.gen", + "locale-gen" + ], "omarchy_install": { "mode": "protected", "target_mount": "/mnt", @@ -832,7 +996,7 @@ run_partition_execute() { "locale_config": { "kb_layout": "$keyboard", "sys_enc": "UTF-8", - "sys_lang": "en_US.UTF-8" + "sys_lang": "$SYS_LANG" }, "mirror_config": { "custom_repositories": [], @@ -862,7 +1026,7 @@ _EOF_ } disk_form() { - step "Let's select where to install Omarchy..." + step "$L_DISK_STEP" # Don't offer the install media as an option (Arch ISO mounts it here) local boot_source @@ -888,19 +1052,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 "$L_DISK_HEADER") || 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." + step "$(printf "$L_PT_STEP" "$disk")" + gum style "$L_PT_1" + gum style --foreground 8 "$L_PT_2" gum style --foreground 8 "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 "$L_OPEN_CFDISK" --negative "$L_BACK" "$(printf "$L_CFDISK_CONFIRM" "$disk")" || return 0 clear cfdisk "$disk" || true @@ -933,17 +1097,17 @@ requires_full_disk_install() { } install_mode_form() { - local choices=("Full disk install") + local choices=("$L_MODE_FULL") # 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+=("$L_MODE_FREE") fi - choices+=("Choose a different disk") + choices+=("$L_MODE_OTHER") - step "Let's select how to install Omarchy..." - install_mode=$(gum choose --header "Select installation mode on $disk" "${choices[@]}") || abort + step "$L_MODE_STEP" + install_mode=$(gum choose --header "$(printf "$L_MODE_HEADER" "$disk")" "${choices[@]}") || abort } confirm_disk_overwrite() { @@ -953,17 +1117,17 @@ confirm_disk_overwrite() { while true; do clear_logo echo - say "Everything will be overwritten. There is no recovery possible." + say "$L_WIPE_WARN" if [[ $mode == "encrypted" ]]; then - say --foreground 8 "Press Ctrl+C for unencrypted install." - affirmative="Yes, install" + say --foreground 8 "$L_CTRLC_UNENC" + affirmative="$L_YES_INSTALL" else - affirmative="Yes, install without encryption" + affirmative="$L_YES_NOENC" fi echo - gum confirm --affirmative "$affirmative" --negative "No, change it" "Confirm overwriting ${disk}" + gum confirm --affirmative "$affirmative" --negative "No, change it" "$(printf "$L_WIPE_CONFIRM" "${disk}")" confirm_status=$? case $confirm_status in @@ -1002,13 +1166,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="$L_MODE_FULL" else install_mode_form fi case "$install_mode" in - "Full disk install") + "$L_MODE_FULL") if confirm_disk_overwrite; then install_target="full_disk" return 0 @@ -1017,13 +1181,13 @@ select_installation() { # wants a different disk. $full_disk_only && disk_form ;; - "Free space install"*) + "$L_MODE_FREE") if run_partition_decide; then install_target="free_space" return 0 fi ;; - "Choose a different disk") + "$L_MODE_OTHER") disk_form ;; esac @@ -1136,7 +1300,10 @@ cat <<-_EOF_ >user_configuration.json "auth_config": {}, "audio_config": { "audio": "pipewire" }, "bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false }, - "custom_commands": [], + "custom_commands": [ + "sed -i -e s/^#zh_CN.UTF-8/zh_CN.UTF-8/ -e s/^#en_US.UTF-8/en_US.UTF-8/ /etc/locale.gen", + "locale-gen" + ], "omarchy_install": { "mode": "full_disk", "defer_provisioning": $defer_provisioning, @@ -1221,7 +1388,7 @@ cat <<-_EOF_ >user_configuration.json "locale_config": { "kb_layout": "$keyboard", "sys_enc": "UTF-8", - "sys_lang": "en_US.UTF-8" + "sys_lang": "$SYS_LANG" }, "mirror_config": { "custom_repositories": [], diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard index 91e960b..9d45f6a 100755 --- a/configs/airootfs/usr/local/bin/omarchy-install-dashboard +++ b/configs/airootfs/usr/local/bin/omarchy-install-dashboard @@ -53,6 +53,44 @@ export GUM_CONFIRM_UNSELECTED_BACKGROUND="${GUM_CONFIRM_UNSELECTED_BACKGROUND:-0 child_pid="" child_pgid="" +if [[ ${OMARCHY_INSTALL_LANG:-} == zh ]]; then +tips=( + "Super + Space 打开 Omarchy 菜单:应用、设置与更多" + "Super + K 查看全部快捷键" + "Super 就是键盘上的 Windows / Command 键" + "用 Xournal++ 给 PDF 签名" + "用 LocalSend 与手机、电脑互传文件" + "菜单 Install > Web App 可把任意网站变成应用" + "Super + Return 打开终端,Super + Shift + Return 打开浏览器" + "图片用 Pinta,视频剪辑用 Kdenlive" + "Super + Ctrl + Print 用 OCR 抓取屏幕文字" + "Print 截屏,Alt + Print 录屏" + "Super + Ctrl + R 设置提醒" + "菜单 Style > Theme 一键换主题" + "双击顶栏可切换透明" + "菜单 Install > Windows 可运行完整 Windows 虚拟机" + "Super + Ctrl + V 打开剪贴板管理器" + "Super + 1 到 0 切换工作区,加 Shift 带窗口一起" + "Super + Print 拾取屏幕任意位置的颜色" + "菜单里的 Update 保持系统常新" +) +L_INSTALLING="正在安装 Omarchy CN" +L_TIP="提示:" +L_DONE_IN="安装完成,用时 %s" +L_STOPPED="Omarchy 安装已停止" +L_EXIT_STATUS="安装器退出,状态码 %s" +L_ENTER="按回车继续……" +L_REBOOT_NOW="立即重启" +L_FAIL_HEADER="接下来做什么?" +L_C_UPLOAD="上传日志求助" +L_C_VIEWLOG="查看完整日志" +L_C_SHELL="进入 Shell" +L_C_REBOOT="重启" +L_C_POWEROFF="关机" +L_NO_UPLOADER="当前环境没有可用的日志上传工具。" +L_LOG_AT="安装日志: %s" +L_TARGET_LOG="目标系统日志: /mnt/var/log/omarchy-install.log" +else tips=( "Super + Space opens the Omarchy menu for apps, settings, and more" "Super + K shows all the key bindings" @@ -61,7 +99,7 @@ tips=( "Share files with phones and laptops using LocalSend" "Turn any website into an app with Install > Web App in the menu" "Super + Return opens a terminal, Super + Shift + Return the browser" - "Edit images with Pinta, videos with Kdenlive, docs with LibreOffice" + "Edit images with Pinta and videos with Kdenlive" "Super + Ctrl + Print grabs text off the screen with OCR" "Print takes a screenshot, Alt + Print records the screen" "Set a reminder with Super + Ctrl + R" @@ -73,6 +111,23 @@ tips=( "Super + Print picks a color from anywhere on screen" "Keep the system fresh with Update in the Omarchy menu" ) +L_INSTALLING="Installing Omarchy" +L_TIP="Tip:" +L_DONE_IN="Installed Omarchy in %s" +L_STOPPED="Omarchy installation stopped" +L_EXIT_STATUS="Installer exited with status %s" +L_ENTER="Press Enter to continue…" +L_REBOOT_NOW="Reboot Now" +L_FAIL_HEADER="What would you like to do?" +L_C_UPLOAD="Upload log for support" +L_C_VIEWLOG="View full log" +L_C_SHELL="Drop to shell" +L_C_REBOOT="Reboot" +L_C_POWEROFF="Power off" +L_NO_UPLOADER="No log uploader is available in this environment." +L_LOG_AT="Install log: %s" +L_TARGET_LOG="Target log: /mnt/var/log/omarchy-install.log" +fi cleanup() { printf '%s%s' "$RESET" "$SHOW_CURSOR" >"$TTY_PATH" 2>/dev/null || true @@ -452,13 +507,13 @@ render_dynamic() { LAST_PM=$pm printf '%s%d;1H' "$CSI" "$DYNAMIC_ROW" - center "Installing Omarchy" "$CONTENT_WIDTH" + center "$L_INSTALLING" "$CONTENT_WIDTH" blank_line line_at "$CONTENT_WIDTH" $(( (CONTENT_WIDTH - 34) / 2 )) "" progress_bar "$pm" 34 printf '\n' blank_line - center "${DIM}Tip:${RESET} ${GREEN}$(current_tip)${RESET}" "$CONTENT_WIDTH" + center "${DIM}${L_TIP}${RESET} ${GREEN}$(current_tip)${RESET}" "$CONTENT_WIDTH" printf '%s' "$CLEAR_TO_END" } @@ -511,7 +566,7 @@ render_finish() { printf '%s%s%s%d;1H' "$SHOW_CURSOR" "$CLEAR" "$CSI" "$FINISH_TOP_ROW" render_logo blank_line - center "Installed Omarchy in ${duration}" "$CONTENT_WIDTH" + center "$(printf "$L_DONE_IN" "${duration}")" "$CONTENT_WIDTH" blank_line } >"$TTY_PATH" @@ -556,7 +611,7 @@ reboot_prompt() { --padding "0 0 0 $prompt_pad" \ --show-help=false \ --default \ - --affirmative "Reboot Now" \ + --affirmative "$L_REBOOT_NOW" \ --negative "" \ "" <"$TTY_PATH" >"$TTY_PATH" 2>&1 } @@ -617,7 +672,7 @@ find_log_uploader() { } prompt_enter() { - printf '\nPress Enter to continue…' >"$TTY_PATH" + printf '\n%s' "$L_ENTER" >"$TTY_PATH" IFS= read -r _ <"$TTY_PATH" || true } @@ -628,9 +683,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" + echo "$L_NO_UPLOADER" + printf "$L_LOG_AT\n" "$LOG_FILE" + [[ -f /mnt/var/log/omarchy-install.log ]] && echo "$L_TARGET_LOG" } >"$TTY_PATH" fi prompt_enter @@ -696,8 +751,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}${L_STOPPED}${RESET}" "$CONTENT_WIDTH" + center "$(printf "$L_EXIT_STATUS" "$status")" "$CONTENT_WIDTH" render_media_diagnosis "$media_diagnosis" @@ -730,40 +785,40 @@ failure_menu() { 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" + --header "$L_FAIL_HEADER" \ + "$L_C_UPLOAD" \ + "$L_C_VIEWLOG" \ + "$L_C_SHELL" \ + "$L_C_REBOOT" \ + "$L_C_POWEROFF" \ + <"$TTY_PATH" 2>"$TTY_PATH") || choice="$L_C_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" + --header "$L_FAIL_HEADER" \ + "$L_C_VIEWLOG" \ + "$L_C_SHELL" \ + "$L_C_REBOOT" \ + "$L_C_POWEROFF" \ + <"$TTY_PATH" 2>"$TTY_PATH") || choice="$L_C_SHELL" fi case "$choice" in - "Upload log for support") + "$L_C_UPLOAD") upload_failure_log render_failure "${failure_status:-1}" "${failure_summary:-}" "${failure_media_diagnosis:-}" >"$TTY_PATH" 2>/dev/null || true ;; - "View full log") + "$L_C_VIEWLOG") view_failure_log render_failure "${failure_status:-1}" "${failure_summary:-}" "${failure_media_diagnosis:-}" >"$TTY_PATH" 2>/dev/null || true ;; - "Reboot") + "$L_C_REBOOT") reboot 2>/dev/null || systemctl reboot 2>/dev/null || true ;; - "Power off") + "$L_C_POWEROFF") poweroff 2>/dev/null || systemctl poweroff 2>/dev/null || true ;; - "Drop to shell"|"") + "$L_C_SHELL"|"") return 0 ;; esac