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..780ff6a 100644
--- a/configs/airootfs/root/.automated_script.sh
+++ b/configs/airootfs/root/.automated_script.sh
@@ -10,7 +10,20 @@
 #   - 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"
+  if OMARCHY_CN_GUI=1 OMARCHY_INSTALL_LANG=zh WLR_RENDERER=pixman     cage -- foot -F "$0"; then
+    exit 0
+  fi
+  echo "图形安装环境不可用，回退英文控制台 (falling back to the English VT installer)"
+  unset OMARCHY_INSTALL_LANG XDG_RUNTIME_DIR
+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..24a1bf8 100644
--- a/configs/airootfs/root/configurator
+++ b/configs/airootfs/root/configurator
@@ -9,6 +9,165 @@ 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
+  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
+  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 +281,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 +335,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 +371,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 +404,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 +416,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 +450,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 +663,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 +677,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 +693,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 +778,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 +850,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 +868,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 +884,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 +905,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
@@ -862,7 +1021,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 +1047,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 +1092,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 +1112,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 +1161,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 +1176,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
diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard
index 91e960b..2a615e2 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，文档用 LibreOffice"
+  "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"
@@ -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,34 +785,34 @@ 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")
