#!/bin/bash
## Author: shizuku
## Modified: 2026-10-16
## License: MIT
## 项目: 自用 Linux 换源脚本（shizuku）

## 定制方法
# 只需要在头部（此处）定义全局变量即可，具体详见官网文档，简单写几个例子
# SOURCE="www.example.com"  # 指定软件源地址
# BRANCH="branch"           # 指定软件源仓库
# WEB_PROTOCOL="https"      # 指定 Web 协议

## 软件源列表
# 中国大陆默认
mirror_list_default=(
    "mirrors.aliyun.com"
    "mirrors.tencent.com"
    "mirrors.huaweicloud.com"
    "mirrors.cmecloud.cn"
    "mirrors.ctyun.cn"
    "mirrors.163.com"
    "mirrors.volces.com"
    "mirrors.tuna.tsinghua.edu.cn"
    "mirrors.pku.edu.cn"
    "mirrors.zju.edu.cn"
    "mirrors.nju.edu.cn"
    "mirror.lzu.edu.cn"
    "mirror.sjtu.edu.cn"
    "mirrors.hust.edu.cn"
    "mirrors.ustc.edu.cn"
    "mirror.iscas.ac.cn"
    "mirrors.cstcloud.cn"
    "__OFFICIAL_SOURCE_TAG__"
)
# 中国大陆教育网
mirror_list_edu=(
    "mirrors.pku.edu.cn"
    "mirrors.bfsu.edu.cn"
    "mirrors.cqu.edu.cn"
    "mirrors.cqupt.edu.cn"
    "mirrors.hust.edu.cn"
    "mirrors.jlu.edu.cn"
    "mirrors.jcut.edu.cn"
    "mirror.lzu.edu.cn"
    "mirrors.nju.edu.cn"
    "mirrors.njtech.edu.cn"
    "mirrors.njupt.edu.cn"
    "mirrors.sustech.edu.cn"
    "mirror.nyist.edu.cn"
    "mirrors.qlu.edu.cn"
    "mirrors.tuna.tsinghua.edu.cn"
    "mirrors.sdu.edu.cn"
    "mirror.sjtu.edu.cn"
    "mirrors.sjtug.sjtu.edu.cn"
    "mirrors.wsyu.edu.cn"
    "mirrors.xjtu.edu.cn"
    "mirrors.nwafu.edu.cn"
    "mirrors.zju.edu.cn"
    "mirrors.ustc.edu.cn"
    "__OFFICIAL_SOURCE_TAG__"
)
# 境外以及海外地区
mirror_list_abroad=(
    "mirrors.xtom.hk"
    "mirror.01link.hk"
    "download.nus.edu.sg/mirror"
    "mirror.sg.gs"
    "mirrors.xtom.sg"
    "free.nchc.org.tw"
    "mirror.ossplanet.net"
    "linux.cs.nycu.edu.tw"
    "mirror.twds.com.tw"
    "mirror.anigil.com"
    "ftp.udx.icscoe.jp/Linux"
    "ftp.jaist.ac.jp/pub/Linux"
    "linux2.yz.yamagata-u.ac.jp/pub/Linux"
    "mirrors.xtom.jp"
    "mirrors.gbnetwork.com"
    "mirror.kku.ac.th"
    "mirror.vorboss.net"
    "mirror.dogado.de"
    "mirrors.xtom.de"
    "ftp.halifax.rwth-aachen.de"
    "ftp.agdsn.de"
    "mirror.in2p3.fr/pub/linux"
    "mirrors.ircam.fr/pub"
    "eclats.crans.org"
    "ftp.crihan.fr"
    "mirrors.xtom.nl"
    "eu.edge.kernel.org"
    "mirrors.xtom.ee"
    "mirror.netsite.dk"
    "mirrors.dotsrc.org"
    "mirror.accum.se"
    "ftp.lysator.liu.se"
    "mirror.yandex.ru"
    "ftp.belnet.be/mirror"
    "ftp.fi.muni.cz/pub/linux"
    "ftp.sh.cvut.cz"
    "mirror.karneval.cz/pub/linux"
    "mirrors.kernel.org"
    "mirrors.mit.edu"
    "mirror.math.princeton.edu/pub"
    "ftp-chi.osuosl.org/pub"
    "mirror.fcix.net"
    "mirrors.xtom.com"
    "mirror.steadfast.net"
    "mirror.it.ubc.ca"
    "mirror.xenyth.net"
    "mirrors.switch.ca"
    "mirror.pop-sc.rnp.br/mirror"
    "mirror.uepg.br"
    "gsl-syd.mm.fcix.net"
    "mirror.aarnet.edu.au/pub"
    "mirror.datamossa.io"
    "mirror.amaze.com.au"
    "mirrors.xtom.au"
    "mirror.fsmg.org.nz"
    "mirror.liquidtelecom.com"
    "mirror.dimensiondata.com"
    "__OFFICIAL_SOURCE_TAG__"
)

## 配置需要区分公网地址和内网地址的软件源（不分地域）
# 需要同时在两个数组变量中分别定义软件源地址，并且保证排列顺序一致
# 软件源公网地址列表
mirror_list_extranet=(
    "mirrors.aliyun.com"
    "mirrors.tencent.com"
    "mirrors.huaweicloud.com"
    "mirrors.ctyun.cn"
    "mirrors.volces.com"
)
# 软件源内网地址列表
mirror_list_intranet=(
    "mirrors.cloud.aliyuncs.com"
    "mirrors.tencentyun.com"
    "mirrors.myhuaweicloud.com"
    "internal.mirrors.ctyun.cn"
    "mirrors.ivolces.com"
)

##############################################################################

## 定义系统判定变量
SYSTEM_DEBIAN="Debian"
SYSTEM_UBUNTU="Ubuntu"

## 定义系统版本文件
File_LinuxRelease=/etc/os-release
File_DebianVersion=/etc/debian_version

## 定义软件源相关文件或目录
Dir_AptAdditionalSources=/etc/apt/sources.list.d
Dir_AptAdditionalSourcesBackup=$Dir_AptAdditionalSources.bak
File_AptSourceList=/etc/apt/sources.list
File_AptSourceListBackup=$File_AptSourceList.bak
File_DebianSources=$Dir_AptAdditionalSources/debian.sources
File_DebianSourcesBackup=$File_DebianSources.bak
File_UbuntuSources=$Dir_AptAdditionalSources/ubuntu.sources
File_UbuntuSourcesBackup=$File_UbuntuSources.bak

## 定义颜色和样式变量
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
PURPLE='\033[35m'
AZURE='\033[36m'
PLAIN='\033[0m'
BOLD='\033[1m'
SUCCESS="\033[1;32m✔${PLAIN}"
COMPLETE="\033[1;32m✔${PLAIN}"
WARN="\033[1;43m WARN ${PLAIN}"
ERROR="\033[1;31m✘${PLAIN}"
FAIL="\033[1;31m✘${PLAIN}"
TIP="\033[1;44m TIP ${PLAIN}"
WORKING="\033[1;36m◉${PLAIN}"

function main() {
    permission_judgment
    collect_system_info
    check_command_options
    run_start
    choose_mirrors
    choose_protocol
    backup_original_mirrors
    remove_original_mirrors
    change_mirrors_main
    upgrade_software
    run_end
}

function handle_command_options() {
    while [ $# -gt 0 ]; do
        case "$1" in
        ## 海外模式
        --abroad)
            USE_ABROAD_SOURCE="true"
            ;;
        ## 中国大陆教育网模式
        --edu)
            USE_EDU_SOURCE="true"
            ;;
        ## 指定软件源地址
        --source)
            if [ "$2" ]; then
                echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
                if [ $? -eq 0 ]; then
                    command_error "$2" "$(msg "error.cmd.options.validAddress")"
                else
                    SOURCE="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
                    shift
                fi
            else
                command_error "$1" "$(msg "error.cmd.options.sourceAddress")"
            fi
            ;;
        --source-security)
            if [ "$2" ]; then
                echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
                if [ $? -eq 0 ]; then
                    command_error "$2" "$(msg "error.cmd.options.validAddress")"
                else
                    SOURCE_SECURITY="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
                    shift
                fi
            else
                command_error "$1" "$(msg "error.cmd.options.sourceAddress")"
            fi
            ;;
        ## 指定软件源仓库
        --branch)
            if [ "$2" ]; then
                SOURCE_BRANCH="$2"
                shift
            else
                command_error "$1" "$(msg "error.cmd.options.sourceRepository")"
            fi
            ;;
        --branch-security)
            if [ "$2" ]; then
                SOURCE_SECURITY_BRANCH="$2"
                shift
            else
                command_error "$1" "$(msg "error.cmd.options.sourceRepository")"
            fi
            ;;
        ## 指定 Debian 系操作系统的版本代号
        --codename)
            if [ "$2" ]; then
                DEBIAN_CODENAME="$2"
                shift
            else
                command_error "$1" "$(msg "error.cmd.options.codename")"
            fi
            ;;
        ## 使用官方源
        --use-official-source)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    USE_OFFICIAL_SOURCE="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## 使用内网地址
        --intranet | --use-intranet-source)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    USE_INTRANET_SOURCE="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## Web 协议（HTTP/HTTPS）
        --protocol | --web-protocol)
            if [ "$2" ]; then
                case "$2" in
                http | https | HTTP | HTTPS)
                    WEB_PROTOCOL="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.protocol")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.needProtocol")"
            fi
            ;;
        ## 备份原有软件源
        --backup)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    BACKUP="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## 忽略覆盖备份提示
        --ignore-backup-tips)
            IGNORE_BACKUP_TIPS="true"
            ;;
        ## 更新软件包
        --upgrade-software | --updata-software)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    UPGRADE_SOFTWARE="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## 在更新软件包后清理下载缓存
        --clean-cache)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    CLEAN_CACHE="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## Locale
        --lang)
            if [ "$2" ]; then
                local lang_norm="${2//_/-}"
                lang_norm="${lang_norm,,}"
                case "$lang_norm" in
                zh | zh-cn | zh-hans | zh-hans-*)
                    init_msg_pack "zh-hans"
                    shift
                    ;;
                zh-hant | zh-hant-* | zh-tw | zh-hk)
                    init_msg_pack "zh-hant"
                    shift
                    ;;
                en | en-us | en-*)
                    init_msg_pack "en"
                    shift
                    ;;
                auto)
                    choose_display_language
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.validLangKey")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.langKey")"
            fi
            ;;
        --zh | --zh-[Cc][Nn])
            init_msg_pack "zh-hans"
            ;;
        --en | --en-[Uu][Ss])
            init_msg_pack "en"
            ;;
        --zh-[Hh]an[st])
            init_msg_pack "$1"
            ;;
        ## 清除屏幕上的所有内容
        --clean-screen)
            if [ "$2" ]; then
                case "$2" in
                [Tt]rue | [Ff]alse)
                    CLEAN_SCREEN="${2,,}"
                    shift
                    ;;
                *)
                    command_error "$2" "$(msg "error.cmd.options.boolean")"
                    ;;
                esac
            else
                command_error "$1" "$(msg "error.cmd.options.boolean")"
            fi
            ;;
        ## 打印源文件修改前后差异
        --print-diff)
            PRINT_DIFF="true"
            ;;
        ## 纯净模式
        --pure-mode)
            PURE_MODE="true"
            ;;
        ## 命令帮助
        --help)
            echo -e "\n$(msg "commands.help" "https://linux.goose.gs.cn")\n"
            exit
            ;;
        *)
            command_error "$1"
            ;;
        esac
        shift
    done
    ## 设置部分功能的默认值
    BACKUP="${BACKUP:-"true"}"
    USE_OFFICIAL_SOURCE="${USE_OFFICIAL_SOURCE:-"false"}"
    IGNORE_BACKUP_TIPS="${IGNORE_BACKUP_TIPS:-"false"}"
    PRINT_DIFF="${PRINT_DIFF:-"false"}"
    PURE_MODE="${PURE_MODE:-"false"}"
}

function run_start() {
    if [ -z "${CLEAN_SCREEN}" ]; then
        [ -z "${SOURCE}" ] && clear
    elif [ "${CLEAN_SCREEN}" == "true" ]; then
        clear
    fi
    if [[ "${PURE_MODE}" == "true" ]]; then
        return
    fi
    local system_name="${SYSTEM_PRETTY_NAME:-"${SYSTEM_NAME} ${SYSTEM_VERSION_ID}"}"
    local arch="${DEVICE_ARCH}"
    local date_time="$(date "+%Y-%m-%d %H:%M")"
    local time_zone="$(timedatectl status 2>/dev/null | grep "Time zone" | awk -F ':' '{print$2}' | awk -F ' ' '{print$1}')"
    echo -e "  \033[1;35m  ____  _   _ ___ _____ _   _ _  __ _   _ \033[0m"
    echo -e "  \033[1;35m / ___|| | | |_ _|__  /| | | | |/ /| | | |\033[0m"
    echo -e "  \033[1;35m \___ \| |_| || |  / / | | | | ' / | | | |\033[0m"
    echo -e "  \033[1;35m  ___) |  _  || | / /_ | |_| | . \ | |_| |\033[0m"
    echo -e "  \033[1;35m |____/|_| |_|___/____| \___/|_|\_\ \___/ \033[0m"
    echo -e ''
    echo -e "$(msg "start.welcome")"
    echo -e ''
    echo -e "$(msg "start.runtimeEnv") ${BLUE}${system_name} ${arch}${PLAIN}"
    echo -e "$(msg "start.dateTime") ${BLUE}${date_time} ${time_zone}${PLAIN}"
    sleep 1 >/dev/null 2>&1
}

function run_end() {
    if [[ "${PURE_MODE}" == "true" ]]; then
        echo ''
        return
    fi
    echo -e "\n\033[3;1mPowered by \033[35mshizuku\033[0m\n"
}

function output_error() {
    [ "$1" ] && echo -e "\n$ERROR $1\n"
    exit 1
}

function command_error() {
    local tmp_text="$(msg "error.cmd.options.needConfirm")"
    if [[ "${2}" ]]; then
        tmp_text="$(msg "error.cmd.options.needSpecify" "${2}")"
    fi
    output_error "$(msg "error.cmd.options.invalid" "${BLUE}$1${PLAIN}" "${tmp_text}")"
}

function unsupport_system_error() {
    output_error "$(msg "error.unsupportSystem" "${1}")"
}

function input_error() {
    echo -e "\n$WARN $(msg "error.input" "${1}")"
}

function command_exists() {
    command -v "$@" &>/dev/null
}

function permission_judgment() {
    if [ $UID -ne 0 ]; then
        local change_cmd="su root"
        if command_exists sudo; then
            change_cmd="sudo -i"
        fi
        output_error "$(msg "error.needRoot" "${BLUE}${change_cmd}${PLAIN}")"
    fi
}

function get_os_release_value() {
    grep -E "^${1}=" $File_LinuxRelease | cut -d= -f2- | sed "s/[\'\"]//g"
}

function collect_system_info() {
    if [ ! -s "${File_LinuxRelease}" ]; then
        unsupport_system_error "$(msg "error.unknownSystem")"
    fi
    ## 定义系统名称
    SYSTEM_NAME="$(get_os_release_value NAME)"
    SYSTEM_PRETTY_NAME="$(get_os_release_value PRETTY_NAME)"
    ## 定义系统版本号
    SYSTEM_VERSION_ID="$(get_os_release_value VERSION_ID)"
    SYSTEM_VERSION_ID_MAJOR="${SYSTEM_VERSION_ID%%.*}"
    SYSTEM_VERSION_ID_MINOR="${SYSTEM_VERSION_ID#*.}"
    ## 定义系统ID
    SYSTEM_ID="$(get_os_release_value ID)"
    ## 判定当前系统派系
    if [ -s "${File_DebianVersion}" ]; then
        SYSTEM_FACTIONS="${SYSTEM_DEBIAN}"
    else
        unsupport_system_error "$(msg "error.unknownSystem")"
    fi
    ## 判定系统类型、版本、版本号
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        if command_exists lsb_release; then
            SYSTEM_JUDGMENT="$(lsb_release -is)"
            SYSTEM_VERSION_CODENAME="${DEBIAN_CODENAME:-"$(lsb_release -cs)"}"
        else
            ## https://codeberg.org/gioele/lsb-release-minimal
            SYSTEM_JUDGMENT="${SYSTEM_ID^}"
            if [ "${SYSTEM_NAME}" ]; then
                if [[ "${SYSTEM_ID,,}" == "${SYSTEM_NAME,,}" ]]; then
                    SYSTEM_JUDGMENT="${SYSTEM_NAME}"
                fi
            fi
            SYSTEM_VERSION_CODENAME="${DEBIAN_CODENAME:-"$(get_os_release_value VERSION_CODENAME)"}"
        fi
        ## 针对特定系统的判定
        if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_DEBIAN}" ]]; then
            ## 尚未正式发布的版本
            if [[ -z "${SYSTEM_VERSION_ID}" && "${SYSTEM_VERSION_CODENAME}" == "forky" ]]; then
                SYSTEM_VERSION_ID="14"
                SYSTEM_VERSION_ID_MAJOR="${SYSTEM_VERSION_ID%%.*}"
                SYSTEM_VERSION_ID_MINOR="${SYSTEM_VERSION_ID#*.}"
            fi
            ## 是否使用 DEB822 格式
            if [[ "${SYSTEM_VERSION_ID_MAJOR}" && "${SYSTEM_VERSION_ID_MAJOR}" -ge 13 ]]; then
                USE_DEB822_FORMAT="true"
            fi
        fi
        if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_UBUNTU}" ]]; then
            ## 是否使用 DEB822 格式
            if [[ "${SYSTEM_VERSION_ID_MAJOR}" && "${SYSTEM_VERSION_ID_MAJOR}" -ge 24 ]]; then
                USE_DEB822_FORMAT="true"
            fi
        fi
        ;;
    esac
    ## 判断系统及版本是否适配
    local is_supported="true"
    case "${SYSTEM_JUDGMENT}" in
    "${SYSTEM_DEBIAN}")
        if [[ ! "${SYSTEM_VERSION_ID_MAJOR}" =~ ^(1[0-4]|[8-9])$ ]]; then
            is_supported="false"
        fi
        if [[ "${SYSTEM_VERSION_CODENAME}" == "sid" ]]; then
            if [[ "${PURE_MODE}" != "true" ]]; then
                echo -e "\n${WARN} $(msg "warn.unstableDebian" "${BLUE}unstable(sid)${PLAIN}")\n"
            fi
        fi
        ;;
    "${SYSTEM_UBUNTU}")
        if [[ ! "${SYSTEM_VERSION_ID_MAJOR}" =~ ^(1[4-9]|2[0-6])$ ]]; then
            is_supported="false"
        fi
        ;;
    *)
        unsupport_system_error "$(msg "error.unknownVersion")"
        ;;
    esac
    if [[ "${is_supported}" == "false" ]]; then
        unsupport_system_error "$(msg "error.unsupportVersion")"
    fi
    ## 判定系统处理器架构
    DEVICE_ARCH_RAW="$(uname -m)"
    case "${DEVICE_ARCH_RAW}" in
    x86_64)
        DEVICE_ARCH="x86_64"
        ;;
    aarch64)
        DEVICE_ARCH="ARM64"
        ;;
    armv8l)
        DEVICE_ARCH="ARMv8_32"
        ;;
    armv7l)
        DEVICE_ARCH="ARMv7"
        ;;
    armv6l)
        DEVICE_ARCH="ARMv6"
        ;;
    armv5tel)
        DEVICE_ARCH="ARMv5"
        ;;
    i686)
        DEVICE_ARCH="x86_32"
        ;;
    *)
        DEVICE_ARCH="${DEVICE_ARCH_RAW}"
        ;;
    esac
    ## 定义软件源仓库名称
    if [[ -z "${SOURCE_BRANCH}" ]]; then
        ## 默认为系统名称小写，替换空格
        SOURCE_BRANCH="${SYSTEM_JUDGMENT,,}"
        SOURCE_BRANCH="${SOURCE_BRANCH// /-}"
        ## 处理特殊的仓库名称
        case "${SYSTEM_JUDGMENT}" in
        "${SYSTEM_DEBIAN}")
            case "${SYSTEM_VERSION_ID_MAJOR}" in
            8 | 9 | 10 | 11)
                SOURCE_BRANCH="debian-archive/debian"
                ;;
            *)
                SOURCE_BRANCH="debian"
                ;;
            esac
            ;;
        "${SYSTEM_UBUNTU}")
            if [[ "${DEVICE_ARCH_RAW}" == "x86_64" || "${DEVICE_ARCH_RAW}" == *i?86* ]]; then
                SOURCE_BRANCH="ubuntu"
            else
                SOURCE_BRANCH="ubuntu-ports"
            fi
            ;;
        esac
    fi
    ## 定义软件源更新文字
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        SYNC_MIRROR_TEXT="$(msg "source.sync.text1")"
        ;;
    esac
    ## 判断是否可以使用高级交互式选择器
    CAN_USE_ADVANCED_INTERACTIVE_SELECTION="false"
    if command_exists tput; then
        CAN_USE_ADVANCED_INTERACTIVE_SELECTION="true"
    fi
}

## 命令选项兼容性判断
function check_command_options() {
    if [[ "${USE_ABROAD_SOURCE}" == "true" && "${USE_EDU_SOURCE}" == "true" ]]; then
        output_error "$(msg "error.cmd.options.unsupportTwoSourceMode")"
    fi
}

## 选择显示语言
function choose_display_language() {
    local result
    if command_exists tput; then
        local -a lang_key_labels=()
        local language_text
        for ((i = 0; i < ${#MESSAGE_LANG_KEYS[@]}; i++)); do
            language_text="${MESSAGE_LANG_DISPLAY[${MESSAGE_LANG_KEYS[$i]}]}"
            if [[ "${language_text}" ]]; then
                lang_key_labels+=("${language_text}")
            else
                lang_key_labels+=("")
            fi
        done
        interactive_select_list "MESSAGE_LANG_KEYS" "\n ${BOLD}Please select the display language:${PLAIN}\n" "lang_key_labels"
        result="${_SELECT_RESULT%%@@*}"
    else
        echo ''
        for ((i = 0; i < ${#MESSAGE_LANG_KEYS[@]}; i++)); do
            echo -e " $((i + 1)). ${MESSAGE_LANG_DISPLAY[${MESSAGE_LANG_KEYS[$i]}]}"
        done
        local CHOICE="$(echo -e "\n${BOLD}└─ Please select and enter the display language [ 1-${#MESSAGE_LANG_KEYS[@]} ]：${PLAIN}")"
        while true; do
            read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
            case "${INPUT}" in
            [1-9] | [1-9][0-9])
                local tmp_result="${MESSAGE_LANG_KEYS[$((INPUT - 1))]}"
                if [[ -z "${tmp_result}" ]]; then
                    echo -e "\n$WARN $(msg "warn.needValidNumberIndex")"
                else
                    result="${tmp_result}"
                    break
                fi
                ;;
            *)
                echo -e "\n$WARN $(msg "warn.needInputNumberIndex")"
                ;;
            esac
        done
    fi
    init_msg_pack "${result}"
}

## 选择软件源
function choose_mirrors() {
    ## 打印软件源列表
    function print_mirrors_list() {
        local tmp_name tmp_index i j
        function StringLength() {
            local text=$1
            echo "${#text}"
        }
        local -a list_arr=()
        local list_arr_sum="$(eval echo \${#$1[@]})"
        for ((i = 0; i < $list_arr_sum; i++)); do
            list_arr[$i]="$(eval echo \${$1[i]})"
        done
        local name_width=${2:-"30"}
        local -a list_labels=()
        if [[ "${3}" ]]; then
            eval "list_labels=(\"\${${3}[@]}\")"
        fi
        if command_exists printf; then
            local tmp_uchar_1 tmp_uchar_2 tmp_uchar_3 tmp_uchar_4 tmp_default_length tmp_length tmp_unicode_length tmp_spaces_nums tmp_max_length
            for ((i = 0; i < ${#list_arr[@]}; i++)); do
                if [[ "${list_labels[$i]}" ]]; then
                    tmp_name="${list_labels[$i]}"
                else
                    tmp_name="${list_arr[i]}"
                fi
                tmp_index=$((i + 1))
                tmp_default_length="${name_width}"
                tmp_uchar_1=$(echo "${tmp_name}" | grep -c "“")
                tmp_uchar_2=$(echo "${tmp_name}" | grep -c "”")
                tmp_uchar_3=$(echo "${tmp_name}" | grep -c "‘")
                tmp_uchar_4=$(echo "${tmp_name}" | grep -c "’")
                [[ "${tmp_uchar_1}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_1))
                [[ "${tmp_uchar_2}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_2))
                [[ "${tmp_uchar_3}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_3))
                [[ "${tmp_uchar_4}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_4))
                tmp_length=$(StringLength "${tmp_name}")
                tmp_unicode_length=$(StringLength "$(echo "${tmp_name}" | sed "s|[0-9a-zA-Z -~]||g; s| ||g")")
                tmp_max_length=$((tmp_default_length + tmp_unicode_length))
                tmp_spaces_nums=$((((tmp_default_length - tmp_unicode_length - tmp_length)) / 2))
                if [[ $tmp_spaces_nums -gt 0 ]]; then
                    tmp_name="${tmp_name}$(printf '%*s' ${tmp_spaces_nums} '')"
                fi
                printf "❖  %-${tmp_max_length}s %4s\n" "${tmp_name}" "${tmp_index})"
            done
        else
            for ((i = 0; i < ${#list_arr[@]}; i++)); do
                if [[ "${list_labels[$i]}" ]]; then
                    tmp_name="${list_labels[$i]}"
                else
                    tmp_name="${list_arr[i]}"
                fi
                tmp_index=$((i + 1))
                echo -e " ❖  $tmp_index. ${tmp_name}"
            done
        fi
    }

    ## 选择使用软件源内网地址
    function choose_use_intranet_address() {
        local intranet_source
        for ((i = 0; i < ${#mirror_list_extranet[@]}; i++)); do
            if [[ "${SOURCE}" == "${mirror_list_extranet[i]}" ]]; then
                intranet_source="${mirror_list_intranet[i]}"
                break
            else
                continue
            fi
        done
        if [[ -z "${USE_INTRANET_SOURCE}" ]]; then
            if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
                echo ''
                interactive_select_boolean "${BOLD}$(msg "interaction.source.type.select")${PLAIN}" "$(msg "interaction.source.type.public")" "$(msg "interaction.source.type.intranet")"
                if [[ "${_SELECT_RESULT}" == "false" ]]; then
                    SOURCE="${intranet_source}"
                    ONLY_HTTP="true" # 强制使用 HTTP 协议
                    [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN $(msg "warn.usedIntranetSource")"
                fi
            else
                local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.source.type.usePublicAddress")? [Y/n] ${PLAIN}")"
                read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
                [[ -z "${INPUT}" ]] && INPUT=Y
                case "${INPUT}" in
                [Yy] | [Yy][Ee][Ss]) ;;
                [Nn] | [Nn][Oo])
                    SOURCE="${intranet_source}"
                    ONLY_HTTP="true" # 强制使用 HTTP 协议
                    [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN $(msg "warn.usedIntranetSource")"
                    ;;
                *)
                    input_error "$(msg "error.defaultBehavior.noUseIntranetSource")"
                    ;;
                esac
            fi
        elif [[ "${USE_INTRANET_SOURCE}" == "true" ]]; then
            SOURCE="${intranet_source}"
        fi
    }

    if [[ -z "${SOURCE}" ]]; then
        ## 使用官方源
        if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then
            return
        fi

        local mirror_list_name mirror_list_label_msg_prefix mirror_list_print_length
        if [[ "${USE_ABROAD_SOURCE}" = "true" ]]; then
            mirror_list_name="mirror_list_abroad"
            mirror_list_label_msg_prefix="mirrors.abroad"
            mirror_list_print_length=56
        elif [[ "${USE_EDU_SOURCE}" = "true" ]]; then
            mirror_list_name="mirror_list_edu"
            mirror_list_label_msg_prefix="mirrors.edu"
            mirror_list_print_length=32
        else
            mirror_list_name="mirror_list_default"
            mirror_list_label_msg_prefix="mirrors.default"
            mirror_list_print_length=32
        fi
        local -a mirror_list_labels=()
        local mirror_list_length=$(eval "echo \${#${mirror_list_name}[@]}")
        local label_msg_index label_msg_content
        for ((i = 0; i < ${mirror_list_length}; i++)); do
            label_msg_index="${mirror_list_label_msg_prefix}.${i}"
            label_msg_content="$(msg "${label_msg_index}")"
            if [[ "${label_msg_content}" == "${label_msg_index}" ]]; then
                mirror_list_labels+=("")
            else
                mirror_list_labels+=("${label_msg_content}")
            fi
        done

        if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
            interactive_select_list "${mirror_list_name}" "\n ${BOLD}$(msg "interaction.source.select")${PLAIN}\n" "mirror_list_labels"
            SOURCE="${_SELECT_RESULT%%@@*}"
            echo -e "\n${GREEN}➜${PLAIN}  ${BOLD}${_SELECT_RESULT#*@@}${PLAIN}" | sed "s| · |-|g"
        else
            echo ''
            print_mirrors_list "${mirror_list_name}" "${mirror_list_print_length}" "mirror_list_labels"
            local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.source.selectAndInput") [ 1-$(eval echo \${#${mirror_list_name}[@]}) ]：${PLAIN}")"
            while true; do
                read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
                case "${INPUT}" in
                [1-9] | [1-9][0-9] | [1-9][0-9][0-9])
                    local tmp_result="$(eval echo \${${mirror_list_name}[$((INPUT - 1))]})"
                    if [[ -z "${tmp_result}" ]]; then
                        echo -e "\n$WARN $(msg "warn.needValidNumberIndex")"
                    else
                        SOURCE="${tmp_result}"
                        break
                    fi
                    ;;
                *)
                    echo -e "\n$WARN $(msg "warn.needInputNumberIndex")"
                    ;;
                esac
            done
        fi

        ## 使用官方源
        if [[ "${SOURCE}" == "__OFFICIAL_SOURCE_TAG__" ]]; then
            USE_OFFICIAL_SOURCE="true"
            SOURCE=""
            return
        fi
    fi

    ## 选择软件源内网地址
    if [[ "${mirror_list_extranet[*]}" =~ (^|[^[:alpha:]])"${SOURCE}"([^[:alpha:]]|$) ]]; then
        choose_use_intranet_address
    fi
}

## 选择同步或更新软件源所使用的 Web 协议（ HTTP/HTTPS）
function choose_protocol() {
    if [[ -z "${WEB_PROTOCOL}" ]]; then
        if [[ "${ONLY_HTTP}" == "true" ]]; then
            WEB_PROTOCOL="http"
        else
            if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
                echo ''
                interactive_select_boolean "${BOLD}$(msg "interaction.protocol.select")${PLAIN}" "HTTP" "HTTPS"
                if [[ "${_SELECT_RESULT}" == "true" ]]; then
                    WEB_PROTOCOL="http"
                else
                    WEB_PROTOCOL="https"
                fi
            else
                local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.protocol.useHttp")? [Y/n] ${PLAIN}")"
                read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
                [[ -z "${INPUT}" ]] && INPUT=Y
                case "${INPUT}" in
                [Yy] | [Yy][Ee][Ss])
                    WEB_PROTOCOL="http"
                    ;;
                [Nn] | [Nn][Oo])
                    WEB_PROTOCOL="https"
                    ;;
                *)
                    input_error "$(msg "error.defaultBehavior.https")"
                    WEB_PROTOCOL="https"
                    ;;
                esac
            fi
        fi
    fi
    WEB_PROTOCOL="${WEB_PROTOCOL,,}"
}

## 备份原有软件源（文件/目录）
function backup_original_mirrors() {
    function backup_file() {
        local target_file=$1
        local backup_file=$2
        local type="$3"
        ## 判断是否存在源文件
        [ -f "${target_file}" ] || touch "${target_file}"
        if [ ! -s "${target_file}" ]; then
            return
        fi
        ## 判断是否存在已备份的源文件
        if [ -s "${backup_file}" ]; then
            if [[ "${IGNORE_BACKUP_TIPS}" != "false" ]]; then
                return
            fi
            local ask_text="$(msg "interaction.backup.skipOverwrite.sourceFile" "${type}")?"
            if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
                echo ''
                interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
                if [[ "${_SELECT_RESULT}" == "false" ]]; then
                    echo ''
                    cp -rvf "${target_file}" "${backup_file}" 2>&1
                    BACKED_UP="true"
                fi
            else
                local CHOICE_BACKUP="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
                read -rp "${CHOICE_BACKUP}" INPUT || { echo ""; exit 1; }
                [[ -z "${INPUT}" ]] && INPUT=Y
                case "${INPUT}" in
                [Yy] | [Yy][Ee][Ss]) ;;
                [Nn] | [Nn][Oo])
                    echo ''
                    cp -rvf "${target_file}" "${backup_file}" 2>&1
                    BACKED_UP="true"
                    ;;
                *)
                    input_error "$(msg "error.defaultBehavior.noOverwrite")"
                    ;;
                esac
            fi
        else
            echo ''
            cp -rvf "${target_file}" "${backup_file}" 2>&1
            BACKED_UP="true"
            echo -e "\n$COMPLETE $(msg "info.backuped.sourceFile" "${type}")"
            sleep 1s
        fi
    }
    function backup_dir() {
        local target_dir=$1
        local backup_dir=$2
        local file_type="${3:-repo}"
        [ -d "${target_dir}" ] || mkdir -p "${target_dir}"
        [ -d "${backup_dir}" ] || mkdir -p "${backup_dir}"
        ## 判断是否存在软件源文件
        ls "${target_dir}" | grep "\.${file_type}$" -q
        if [ $? -ne 0 ]; then
            return
        fi
        ## 判断是否存在已备份的软件源文件
        ls "${backup_dir}" | grep "\.${file_type}$" -q
        if [ $? -eq 0 ]; then
            if [[ "${IGNORE_BACKUP_TIPS}" != "false" ]]; then
                return
            fi
            local ask_text="$(msg "interaction.backup.skipOverwrite.sourceFile" "${file_type}")?"
            if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
                echo ''
                interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
                if [[ "${_SELECT_RESULT}" == "false" ]]; then
                    echo ''
                    cp -rvf $target_dir/* "${backup_dir}" 2>&1
                    BACKED_UP="true"
                fi
            else
                local CHOICE_BACKUP="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
                read -rp "${CHOICE_BACKUP}" INPUT || { echo ""; exit 1; }
                [[ -z "${INPUT}" ]] && INPUT=Y
                case "${INPUT}" in
                [Yy] | [Yy][Ee][Ss]) ;;
                [Nn] | [Nn][Oo])
                    echo ''
                    cp -rvf $target_dir/* "${backup_dir}" 2>&1
                    BACKED_UP="true"
                    ;;
                *)
                    input_error "$(msg "error.defaultBehavior.noOverwrite")"
                    ;;
                esac
            fi
        else
            echo ''
            cp -rvf $target_dir/* "${backup_dir}" 2>&1
            BACKED_UP="true"
            echo -e "\n$COMPLETE $(msg "info.backuped.sourceFile" "${file_type}")"
            sleep 1s
        fi
    }

    BACKED_UP="false" # 是否已备份
    if [[ "${BACKUP}" == "true" ]]; then
        case "${SYSTEM_FACTIONS}" in
        "${SYSTEM_DEBIAN}")
            # Debian DEB822 格式源文件
            if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_DEBIAN}" ]] && [ -f "${File_DebianSources}" ]; then
                backup_file $File_DebianSources $File_DebianSourcesBackup "debian.sources"
                USE_DEB822_FORMAT="true"
            fi
            # Ubuntu DEB822 格式源文件
            if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_UBUNTU}" ]] && [ -f "${File_UbuntuSources}" ]; then
                backup_file $File_UbuntuSources $File_UbuntuSourcesBackup "ubuntu.sources"
                USE_DEB822_FORMAT="true"
            fi
            # /etc/apt/sources.list
            if [[ -s "${File_AptSourceList}" ]]; then
                if [[ "${USE_DEB822_FORMAT}" == "true" ]]; then
                    grep -Eqv '^#|^$' $File_AptSourceList
                    if [ $? -eq 0 ]; then
                        backup_file $File_AptSourceList $File_AptSourceListBackup "sources.list"
                    fi
                else
                    backup_file $File_AptSourceList $File_AptSourceListBackup "sources.list"
                fi
            fi
            ;;
        esac
    fi
}

## 移除原有软件源
function remove_original_mirrors() {
    function clear_file() {
        [ -f "$1" ] && sed -i '1,$d' "$1"
    }

    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        # /etc/apt/sources.list.d
        [ -d "${Dir_AptAdditionalSources}" ] || mkdir -p $Dir_AptAdditionalSources
        # Debian DEB822 格式源文件
        if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_DEBIAN}" ]] && [ -f "${File_DebianSources}" ]; then
            clear_file $File_DebianSources
            USE_DEB822_FORMAT="true"
        fi
        # Ubuntu DEB822 格式源文件
        if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_UBUNTU}" ]] && [ -f "${File_UbuntuSources}" ]; then
            clear_file $File_UbuntuSources
            USE_DEB822_FORMAT="true"
        fi
        # /etc/apt/sources.list
        if [[ "${USE_DEB822_FORMAT}" != "true" ]]; then
            [ -s "${File_AptSourceList}" ] && clear_file $File_AptSourceList
        fi
        ## 解决软件源冲突
        if [[ "${USE_DEB822_FORMAT}" == "true" ]] && [ -s "${File_AptSourceList}" ]; then
            if [[ "${SOURCE_BRANCH}" ]]; then
                # 转义仓库路径中的 / 以避免 sed 地址提前闭合（如 debian-archive/debian）
                local _escaped_branch="${SOURCE_BRANCH//\//\\/}"
                sed -e "/^deb\(-src\)\? http.*\/${_escaped_branch}/d" \
                    -e "/^#[[:space:]]*deb\(-src\)\? http.*\/${_escaped_branch}/d" \
                    -e "/^#.*\(默认禁用源码镜像以提高更新速度\|安全更新软件源\|预发布软件源\).*/d" \
                    -i \
                    $File_AptSourceList
            fi
        fi
        ;;
    esac
}

## 换源
function change_mirrors_main() {
    ## 打印软件源内容修改前后差异
    function print_diff() {
        ## 单一文件
        function diff_file() {
            local diff_file=$1
            local origin_file=$2
            if [ -s "${diff_file}" ] && [ -s "${origin_file}" ]; then
                if [[ "$(cat "${diff_file}")" != "$(cat "${origin_file}")" ]]; then
                    echo -e "\n${BLUE}${diff_file}${PLAIN} -> ${BLUE}${origin_file}${PLAIN}"
                    diff "${diff_file}" "${origin_file}" -d --color=always -I -B -E
                fi
            fi
        }
        if command_exists diff && [[ "${BACKED_UP}" == "true" ]]; then
            case "${SYSTEM_FACTIONS}" in
            "${SYSTEM_DEBIAN}")
                if [[ "${USE_DEB822_FORMAT}" == "true" ]]; then
                    # DEB822 格式源文件
                    if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_DEBIAN}" ]]; then
                        diff_file $File_DebianSourcesBackup $File_DebianSources
                    fi
                    if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_UBUNTU}" ]]; then
                        diff_file $File_UbuntuSourcesBackup $File_UbuntuSources
                    fi
                else
                    diff_file $File_AptSourceListBackup $File_AptSourceList
                fi
                ;;
            esac
        fi
    }
    if [[ -z "${SOURCE}" ]]; then
        USE_OFFICIAL_SOURCE="true"
    fi
    ## 调用换源函数
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        change_mirrors_Debian
        ;;
    esac
    ## 比较差异
    if [[ "${PRINT_DIFF}" == "true" ]]; then
        print_diff
    fi
    ## 更新软件源
    local -a commands=()
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        commands+=("apt-get update")
        ;;
    esac
    if [[ "${PURE_MODE}" == "true" ]]; then
        local exec_cmd=""
        for cmd in "${commands[@]}"; do
            if [[ -z "${exec_cmd}" ]]; then
                exec_cmd="${cmd}"
            else
                exec_cmd="${exec_cmd} ; ${cmd}"
            fi
        done
        echo ''
        animate_exec "${exec_cmd}" "${SYNC_MIRROR_TEXT}"
        if [ $? -ne 0 ]; then
            echo ''
            exit 1
        fi
    else
        echo -e "\n$WORKING ${SYNC_MIRROR_TEXT}...\n"
        for cmd in "${commands[@]}"; do
            eval "${cmd}"
        done
        if [ $? -eq 0 ]; then
            echo -e "\n$SUCCESS $(msg "source.changeResult.success")"
        else
            echo -e "\n$FAIL $(msg "source.changeResult.fail" "${SYNC_MIRROR_TEXT}")"
            echo -e "\n$TIP $(msg "source.changeResult.tipTitle" "${SYNC_MIRROR_TEXT}")"
            echo -e "\n1. $(msg "source.changeResult.tip1")"
            echo -e "2. $(msg "source.changeResult.tip2" "${WEB_PROTOCOL}://${SOURCE}/${SOURCE_BRANCH}")"
            echo -e "      $(msg "source.changeResult.tip2.1")"
            echo -e "      $(msg "source.changeResult.tip2.2")"
            echo -e "      $(msg "source.changeResult.tip2.3")"
            echo -e "3. $(msg "source.changeResult.tip3")\n"
            exit 1
        fi
    fi
}

## 升级软件包
function upgrade_software() {
    local ask_text=""
    ## 交互确认
    if [[ -z "${UPGRADE_SOFTWARE}" ]]; then
        UPGRADE_SOFTWARE="false"
        ask_text="$(msg "interaction.upgrade.skip")?"
        if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
            echo ''
            interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
            if [[ "${_SELECT_RESULT}" == "false" ]]; then
                UPGRADE_SOFTWARE="true"
            fi
        else
            local CHOICE="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
            read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
            [[ -z "${INPUT}" ]] && INPUT=Y
            case "${INPUT}" in
            [Yy] | [Yy][Ee][Ss]) ;;
            [Nn] | [Nn][Oo])
                UPGRADE_SOFTWARE="true"
                ;;
            *)
                input_error "$(msg "error.defaultBehavior.noUpdate")"
                ;;
            esac
        fi
    fi
    if [[ "${UPGRADE_SOFTWARE}" == "false" ]]; then
        return
    fi
    if [[ -z "${CLEAN_CACHE}" ]]; then
        CLEAN_CACHE="false"
        ask_text="$(msg "interaction.cache.autoClean")?"
        if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
            echo ''
            interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
            if [[ "${_SELECT_RESULT}" == "true" ]]; then
                CLEAN_CACHE="true"
            fi
        else
            local CHOICE="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
            read -rp "${CHOICE}" INPUT || { echo ""; exit 1; }
            [[ -z "${INPUT}" ]] && INPUT=Y
            case "${INPUT}" in
            [Yy] | [Yy][Ee][Ss])
                CLEAN_CACHE="true"
                ;;
            [Nn] | [Nn][Oo]) ;;
            *)
                input_error "$(msg "error.defaultBehavior.noClean")"
                ;;
            esac
        fi
    fi
    local -a commands=()
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        commands+=("apt-get upgrade -y")
        ;;
    esac
    if [[ "${PURE_MODE}" == "true" ]]; then
        local exec_cmd=""
        for cmd in "${commands[@]}"; do
            if [[ -z "${exec_cmd}" ]]; then
                exec_cmd="${cmd}"
            else
                exec_cmd="${exec_cmd} ; ${cmd}"
            fi
        done
        echo ''
        animate_exec "${exec_cmd}" "$(msg "work.upgradeSoftware")"
    else
        echo ''
        for cmd in "${commands[@]}"; do
            eval "${cmd}"
        done
    fi
    if [[ "${CLEAN_CACHE}" == "false" ]]; then
        return
    fi
    case "${SYSTEM_FACTIONS}" in
    "${SYSTEM_DEBIAN}")
        apt-get autoremove -y >/dev/null 2>&1
        apt-get clean >/dev/null 2>&1
        ;;
    esac
}

##############################################################################

## 更换基于 Debian 系 Linux 发行版的软件源
function change_mirrors_Debian() {
    local apt_source_file=$File_AptSourceList
    local apt_source_content=""
    function write_apt_source() {
        if [[ -n "${apt_source_content}" ]]; then
            echo "${apt_source_content}" >>$apt_source_file
        fi
        apt_source_content="" # 重置内容
    }

    ## 注释的提示语句
    local deb_src_disabled_tips="## $(msg "source.comment.disabledSourceCode")"
    local security_source_tips="## $(msg "source.comment.securitySource")"
    local proposed_source_tips="## $(msg "source.comment.proposedSource")"

    ## one-line 格式模板
    function _template_deb() {
        echo "${1} ${WEB_PROTOCOL}://${2}/ ${3} ${4}"
    }
    function gen_deb() {
        echo "$(_template_deb "deb" "${1}" "${2}" "${3}")
$(_template_deb "deb-src" "${1}" "${2}" "${3}" | sed -e "s|^|# |g")"
    }
    function gen_deb_unsrc() {
        echo "$(_template_deb "deb" "${1}" "${2}" "${3}")"
    }
    function gen_deb_disabled() {
        echo "$(gen_deb "${1}" "${2}" "${3}" | sed -e "s|^|# |g")"
    }
    function gen_deb_unsrc_disabled() {
        echo "$(gen_deb_unsrc "${1}" "${2}" "${3}" | sed -e "s|^|# |g")"
    }
    function gen_deb_security() {
        echo "${security_source_tips}
$(gen_deb "${1}" "${2}-security" "${3}")"
    }

    ## DEB822 格式模板
    function _template_deb822() {
        echo "Types: ${1}
URIs: ${WEB_PROTOCOL}://${2}/
Suites: ${3}
Components: ${4}
Signed-By: /usr/share/keyrings/${_DEB822_ARCHIVE_KEYRING:-"${SYSTEM_JUDGMENT,,}-archive-keyring.gpg"}"
    }
    function gen_deb822() {
        echo "$(_template_deb822 "deb" "${1}" "${2}" "${3}")

$(_template_deb822 "deb-src" "${1}" "${2}" "${3}" | sed -e "s|^|# |g")"
    }
    function gen_deb822_unsrc() {
        echo "$(_template_deb822 "deb" "${1}" "${2}" "${3}")"
    }
    function gen_deb822_disabled() {
        echo "$(_template_deb822 "deb" "${1}" "${2}" "${3}" | sed -e "s|^|# |g")

$(_template_deb822 "deb-src" "${1}" "${2}" "${3}" | sed -e "s|^|# |g")"
    }
    function gen_deb822_security() {
        echo "${security_source_tips}
$(gen_deb822 "${1}" "${2}-security" "${3}")"
    }

    ## 获取 Debian 软件源相关信息
    function _template_get_debian_info() {
        local data1="${1}"
        local data2="${2}"
        local mode="${3:-main}"
        local codename="${4}"
        local result="${data2}"
        case "${mode}" in
        "security")
            local -a majors=(8 9 10)
            local -a codenames=("jessie" "stretch" "buster")
            ;;
        *)
            local -a majors=(8 9 10 11)
            local -a codenames=("jessie" "stretch" "buster" "bullseye")
            ;;
        esac
        if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_DEBIAN}" ]]; then
            for version in "${majors[@]}"; do
                if [[ "${SYSTEM_VERSION_ID_MAJOR}" == "${version}" ]]; then
                    result="${data1}"
                    break
                fi
            done
        else
            for name in "${codenames[@]}"; do
                if [[ "${codename}" == "${name}" ]]; then
                    result="${data1}"
                    break
                fi
            done
        fi
        echo "${result}"
    }
    function get_debian_official_source() {
        # 注：使用官方源时仓库分支固定为 debian，应同此方法一起使用
        _template_get_debian_info "archive.debian.org" "deb.debian.org" "main" "${1}"
    }
    function get_debian_source_branch() {
        _template_get_debian_info "debian-archive/debian" "debian" "main" "${1}"
    }
    function get_debian_repo_components() {
        _template_get_debian_info "main contrib non-free" "main contrib non-free non-free-firmware" "main" "${1}"
    }
    function get_debian_official_source_security_host() {
        # 注：使用官方源时仓库分支固定为 debian-security，应同此方法一起使用
        _template_get_debian_info "archive.debian.org" "security.debian.org" "security" "${1}"
    }
    function get_debian_source_security_branch() {
        _template_get_debian_info "debian-archive/debian-security" "debian-security" "security" "${1}"
    }

    ## Debian 软件源模板
    # debian-backports: https://ftp.debian.org/debian/dists
    # debian-security: https://security.debian.org/debian-security/dists
    function _template_gen_debian_source() {
        local mode="${4:-main}"
        local type="${5:-deb}"
        local result=""

        case "${mode}" in
        "security")
            case "${2}" in
            "forky" | "trixie" | "bookworm" | "bullseye" | "oldoldstable" | "oldstable" | "stable" | "testing")
                if [[ "${type}" == "deb822" ]]; then
                    result="$(gen_deb822_security "${1}" "${2}" "${3}")"
                else
                    result="$(gen_deb_security "${1}" "${2}" "${3}")"
                fi
                ;;
            "jessie" | "stretch" | "buster")
                # 旧版 Debian 安全更新仓库路径为 dists/<codename>/updates
                if [[ "${type}" == "deb822" ]]; then
                    result="${security_source_tips}
$(gen_deb822 "${1}" "${2}/updates" "${3}")"
                else
                    result="${security_source_tips}
$(gen_deb "${1}" "${2}/updates" "${3}")"
                fi
                ;;
            *)
                result=""
                ;;
            esac
            ;;

        *)
            case "${2}" in
            "forky" | "trixie" | "bookworm" | "stable" | "oldstable" | "testing")
                if [[ "${type}" == "deb822" ]]; then
                    result="${deb_src_disabled_tips}
$(gen_deb822 "${1}" "${2} ${2}-updates ${2}-backports" "${3}")"
                else
                    result="$(gen_deb "${1}" "${2}" "${3}")
$(gen_deb "${1}" "${2}-updates" "${3}")
$(gen_deb "${1}" "${2}-backports" "${3}")"
                fi
                ;;
            "sid")
                # sid（不稳定版）无 -updates / -backports 仓库
                if [[ "${type}" == "deb822" ]]; then
                    result="${deb_src_disabled_tips}
$(gen_deb822 "${1}" "${2}" "${3}")"
                else
                    result="$(gen_deb "${1}" "${2}" "${3}")"
                fi
                ;;
            *)
                if [[ "${type}" == "deb822" ]]; then
                    result="${deb_src_disabled_tips}
$(gen_deb822 "${1}" "${2} ${2}-updates" "${3}")"
                else
                    result="$(gen_deb "${1}" "${2}" "${3}")
$(gen_deb "${1}" "${2}-updates" "${3}")"
                fi
                ;;
            esac
            ;;
        esac
        echo "${result}"
    }
    function gen_debian_deb() {
        _template_gen_debian_source "${1}" "${2}" "${3}" "main" "deb"
    }
    function gen_debian_deb_security() {
        _template_gen_debian_source "${1}" "${2}" "${3}" "security" "deb"
    }
    function gen_debian_deb822() {
        _template_gen_debian_source "${1}" "${2}" "${3}" "main" "deb822"
    }
    function gen_debian_deb822_security() {
        _template_gen_debian_source "${1}" "${2}" "${3}" "security" "deb822"
    }

    ## Ubuntu 软件源模板
    function gen_ubuntu_deb() {
        echo "$(gen_deb "${1}" "${2}" "${3}")
$(gen_deb "${1}" "${2}-updates" "${3}")
$(gen_deb "${1}" "${2}-backports" "${3}")
${proposed_source_tips}
$(gen_deb_disabled "${1}" "${2}-proposed" "${3}")"
    }
    function gen_ubuntu_deb822() {
        echo "${deb_src_disabled_tips}
$(gen_deb822 "${1}" "${2} ${2}-updates ${2}-backports" "${3}")

${proposed_source_tips}
$(gen_deb822_disabled "${1}" "${2}-proposed" "${3}")"
    }

    # 注：SOURCE_<XXX> SOURCE_<XXX>_BRANCH 系列变量默认为空值，仅在自定义（使用相关命令选项）时提供，需注意逻辑顺序
    local repo_components=""                          # 软件源仓库区域
    local source_address="${SOURCE}/${SOURCE_BRANCH}" # 软件源地址

    local source_security_host="${SOURCE}"
    local source_security_branch="${SOURCE_BRANCH}"
    local source_security_address=""

    case "${SYSTEM_JUDGMENT}" in
    "${SYSTEM_DEBIAN}")
        repo_components="$(get_debian_repo_components "${SYSTEM_VERSION_CODENAME}")"
        if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then
            SOURCE="$(get_debian_official_source "${SYSTEM_VERSION_CODENAME}")"
            SOURCE_BRANCH="debian"
            source_address="${SOURCE}/${SOURCE_BRANCH}"
        fi
        # 安全更新源
        source_security_branch="$(get_debian_source_security_branch "${SYSTEM_VERSION_CODENAME}")"
        if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then
            source_security_host="$(get_debian_official_source_security_host "${SYSTEM_VERSION_CODENAME}")"
            source_security_branch="debian-security"
        fi
        source_security_address="${SOURCE_SECURITY:-${source_security_host}}/${SOURCE_SECURITY_BRANCH:-${source_security_branch}}"
        ## DEB822 / one-line
        if [[ "${USE_DEB822_FORMAT}" == "true" ]]; then
            apt_source_file="${File_DebianSources}"
            if [[ "${SYSTEM_VERSION_CODENAME}" != "sid" ]]; then
                apt_source_content="$(gen_debian_deb822 "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")

$(gen_debian_deb822_security "${source_security_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
            else
                apt_source_content="$(gen_debian_deb822 "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
            fi
        else
            apt_source_file="${File_AptSourceList}"
            if [[ "${SYSTEM_VERSION_CODENAME}" != "sid" ]]; then
                apt_source_content="${deb_src_disabled_tips}
$(gen_debian_deb "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")
$(gen_debian_deb_security "${source_security_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
            else
                apt_source_content="${deb_src_disabled_tips}
$(gen_deb "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
            fi
        fi
        write_apt_source
        ;;

    "${SYSTEM_UBUNTU}")
        repo_components="main restricted universe multiverse"
        if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then
            if [[ "${DEVICE_ARCH_RAW}" == "x86_64" || "${DEVICE_ARCH_RAW}" == *i?86* ]]; then
                SOURCE="archive.ubuntu.com"
                SOURCE_BRANCH="ubuntu"
                source_address="${SOURCE}/${SOURCE_BRANCH}"
                # 安全更新源（官方源）
                source_security_host="security.ubuntu.com"
                source_security_branch="ubuntu"
            else
                SOURCE="ports.ubuntu.com"
                SOURCE_BRANCH="" # 官方源默认无分支（注：有 ubuntu-ports 重定向）
                source_address="${SOURCE}"
                # 安全更新源（官方源）
                source_security_host="ports.ubuntu.com"
                source_security_branch="ubuntu-ports"
            fi
        fi
        # 安全更新源
        source_security_address="${SOURCE_SECURITY:-${source_security_host}}/${SOURCE_SECURITY_BRANCH:-${source_security_branch}}"
        ## DEB822 / one-line
        if [[ "${USE_DEB822_FORMAT}" == "true" ]]; then
            apt_source_file="${File_UbuntuSources}"
            apt_source_content="$(gen_ubuntu_deb822 "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")

$(gen_deb822_security "${source_security_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
        else
            apt_source_file="${File_AptSourceList}"
            apt_source_content="${deb_src_disabled_tips}
$(gen_ubuntu_deb "${source_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")
$(gen_deb_security "${source_security_address}" "${SYSTEM_VERSION_CODENAME}" "${repo_components}")"
        fi
        write_apt_source
        ;;

    esac
}

function interactive_select_list() {
    _SELECT_RESULT=""
    eval "local -a __values=(\"\${${1}[@]}\")"
    local -a __labels=()
    local message="${2}"
    local selected=0
    local start=0
    local page_size=$(($(tput lines 2>/dev/null) - 3))
    if [[ "${3}" ]]; then
        eval "__labels=(\"\${${3}[@]}\")"
    fi
    function clear_menu() {
        tput rc 2>/dev/null
        for ((i = 0; i < ${#__values[@]} + 1; i++)); do
            echo -e "\r\033[K"
        done
        tput rc 2>/dev/null
    }
    function cleanup() {
        clear_menu
        tput rc 2>/dev/null
        tput cnorm 2>/dev/null
        tput rmcup 2>/dev/null
        echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
        exit 130
    }
    function draw_menu() {
        tput clear 2>/dev/null
        tput cup 0 0 2>/dev/null
        echo -e "${message}"
        local end=$((start + page_size - 1))
        local label
        if [ $end -ge ${#__values[@]} ]; then
            end=${#__values[@]}-1
        fi
        for ((i = start; i <= end; i++)); do
            if [[ "${__labels[$i]}" ]]; then
                label="${__labels[$i]}"
            else
                label="${__values[$i]}"
            fi
            if [ "$i" -eq "${selected}" ]; then
                echo -e "\e[34;4m➤ ${label}\e[0m"
            else
                echo -e "  ${label}"
            fi
        done
    }
    function read_key() {
        IFS= read -rsn1 key
        if [[ $key == $'\x1b' ]]; then
            IFS= read -rsn2 key
            key="$key"
        fi
        echo "$key"
    }
    tput smcup 2>/dev/null
    tput sc 2>/dev/null
    tput civis 2>/dev/null
    trap "cleanup" INT TERM
    draw_menu
    while true; do
        key=$(read_key)
        case "$key" in
        "[A" | "w" | "W")
            if [ "${selected}" -gt 0 ]; then
                selected=$((selected - 1))
                if [ "${selected}" -lt "$start" ]; then
                    start=$((start - 1))
                fi
            fi
            ;;
        "[B" | "s" | "S")
            if [ "${selected}" -lt $((${#__values[@]} - 1)) ]; then
                selected=$((selected + 1))
                if [ "${selected}" -ge $((start + page_size)) ]; then
                    start=$((start + 1))
                fi
            fi
            ;;
        "")
            tput rmcup
            break
            ;;
        *) ;;
        esac
        draw_menu
    done
    tput cnorm 2>/dev/null
    tput rmcup 2>/dev/null
    trap - INT TERM
    _SELECT_RESULT="${__values[${selected}]}"
    if [ "${__labels[${selected}]}" ]; then
        _SELECT_RESULT="${_SELECT_RESULT}@@${__labels[${selected}]}"
    fi
}

function interactive_select_boolean() {
    _SELECT_RESULT=""
    local selected=0
    local message="$1"
    local positive_title="${2:-"$(msg "interaction.common.yes")"}"
    local negative_title="${3:-"$(msg "interaction.common.no")"}"
    local menu_height=3
    local original_line
    function store_position() {
        original_line=$(tput lines 2>/dev/null)
    }
    function clear_menu() {
        for ((i = 0; i < $menu_height; i++)); do
            tput cuu1 2>/dev/null
            tput el 2>/dev/null
        done
    }
    function cleanup() {
        clear_menu
        tput cnorm 2>/dev/null
        echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
        exit 130
    }
    function draw_menu() {
        echo -e "╭─ ${message}"
        echo -e "│"
        if [ "${selected}" -eq 0 ]; then
            echo -e "╰─ \033[34m●\033[0m ${positive_title}\033[2m / ○ ${negative_title}\033[0m"
        else
            echo -e "╰─ \033[2m○ ${positive_title} / \033[0m\033[34m●\033[0m ${negative_title}"
        fi
    }
    function read_key() {
        IFS= read -rsn1 key
        if [[ $key == $'\x1b' ]]; then
            IFS= read -rsn2 key
            key="$key"
        fi
        echo "$key"
    }
    tput civis 2>/dev/null
    store_position
    trap "cleanup" INT TERM
    draw_menu
    while true; do
        key=$(read_key)
        case "$key" in
        "[D" | "a" | "A")
            if [ "${selected}" -gt 0 ]; then
                selected=$((selected - 1))
                clear_menu
                draw_menu
            fi
            ;;
        "[C" | "d" | "D")
            if [ "${selected}" -lt 1 ]; then
                selected=$((selected + 1))
                clear_menu
                draw_menu
            fi
            ;;
        "")
            clear_menu
            break
            ;;
        *) ;;
        esac
    done
    echo -e "╭─ ${message}"
    echo -e "│"
    if [ "${selected}" -eq 0 ]; then
        echo -e "╰─ \033[32m●\033[0m \033[1m${positive_title}\033[0m\033[2m / ○ ${negative_title}\033[0m"
        _SELECT_RESULT="true"
    else
        echo -e "╰─ \033[2m○ ${positive_title} / \033[0m\033[32m●\033[0m \033[1m${negative_title}\033[0m"
        _SELECT_RESULT="false"
    fi
    tput cnorm 2>/dev/null
    trap - INT TERM
}

function animate_exec() {
    local cmd="$1"
    local title="$2"
    local max_lines=${3:-5}
    local spinner_style="${4:-dots}"
    local refresh_rate="${5:-0.1}"
    local scroll_mode="${6:-1}"
    if [[ "${scroll_mode}" == "0" ]]; then
        echo -e "◉ ${title} \n"
        eval "${cmd}"
        return $?
    fi
    local -A spinners=([dots]="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏" [circle]="◐ ◓ ◑ ◒" [classic]="-\\ |/")
    local -A recommended_rates=([dots]="0.08" [circle]="0.12" [classic]="0.12")
    [[ -z "${spinners[$spinner_style]}" ]] && spinner_style="dots"
    [[ "${refresh_rate}" == "0.1" ]] && refresh_rate="${recommended_rates[$spinner_style]}"
    local term_width=$(tput cols 2>/dev/null || echo 80)
    local display_width=$((term_width - 2))
    function simple_truncate() {
        local line="$1"
        local truncate_marker="..."
        local max_length=$((display_width - 3))
        if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then
            echo "${line}"
            return
        fi
        local non_ascii_count=$(echo "${line}" | sed "s|[0-9a-zA-Z -~]||g; s| ||g" | wc -m)
        local total_length=${#line}
        local display_length=$((total_length + non_ascii_count))
        local quote_count=0
        [[ $(echo "${line}" | grep -c "“") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "“")"))
        [[ $(echo "${line}" | grep -c "”") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "”")"))
        [[ $(echo "${line}" | grep -c "‘") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "‘")"))
        [[ $(echo "${line}" | grep -c "’") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "’")"))
        display_length=$((display_length - quote_count))
        if [[ $display_length -le $display_width ]]; then
            echo "$line"
            return
        fi
        local result=""
        local current_width=0
        local i=0
        while [ $i -lt ${#line} ]; do
            local char="${line:$i:1}"
            local char_width=1
            if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"\-\/\!\·] ]]; then
                if [[ "$char" != "“" && "$char" != "”" && "$char" != "‘" && "$char" != "’" ]]; then
                    char_width=2
                fi
            fi
            if [[ $((current_width + char_width)) -gt $max_length ]]; then
                echo "${result}${truncate_marker}"
                return
            fi
            result+="${char}"
            current_width=$((current_width + char_width))
            ((i++))
        done
        echo "${line}"
    }
    function cleanup() {
        [ -f "${temp_file}" ] && rm -f "${temp_file}"
        tput cnorm 2>/dev/null
        echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
        exit 130
    }
    function make_temp_file() {
        local -a temp_dirs=("." "/tmp")
        local tmp_file=""
        for dir in "${temp_dirs[@]}"; do
            [[ ! -d "${dir}" || ! -w "${dir}" ]] && continue
            tmp_file="${dir}/animate_exec_$$_$(date +%s)"
            touch "${tmp_file}" 2>/dev/null || continue
            if [[ -f "${tmp_file}" && -w "${tmp_file}" ]]; then
                echo "${tmp_file}"
                return
            fi
        done
        echo "${tmp_file}"
    }
    function update_display() {
        local current_size=$(wc -c <"${temp_file}" 2>/dev/null || echo 0)
        if [[ $current_size -le $last_size ]]; then
            return 1
        fi
        local -a lines=()
        mapfile -t -n "${max_lines}" lines < <(tail -n "$max_lines" "${temp_file}")
        local -a processed_lines=()
        for ((i = 0; i < ${#lines[@]}; i++)); do
            processed_lines[i]=$(simple_truncate "${lines[i]}")
        done
        tput cud1 2>/dev/null
        echo -ne "\r\033[K"
        tput cud1 2>/dev/null
        for ((i = 0; i < $max_lines; i++)); do
            echo -ne "\r\033[K"
            [[ $i -lt ${#processed_lines[@]} ]] && echo -ne "\033[2m${processed_lines[$i]}\033[0m"
            [[ $i -lt $((max_lines - 1)) ]] && tput cud1 2>/dev/null
        done
        for ((i = 0; i < $max_lines + 1; i++)); do
            tput cuu1 2>/dev/null
        done
        last_size=$current_size
        return 0
    }
    local spinner_frames=(${spinners[$spinner_style]})
    local temp_file="$(make_temp_file)"
    trap "cleanup" INT TERM
    tput civis 2>/dev/null
    echo ''
    echo ''
    for ((i = 0; i < $max_lines; i++)); do
        echo ''
    done
    eval "${cmd}" >"${temp_file}" 2>&1 &
    local cmd_pid=$!
    local last_size=0
    local spin_idx=0
    tput cuu $((max_lines + 2)) 2>/dev/null
    sleep 0.05
    echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]"
    spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
    update_display
    local update_count=0
    local adaptive_rate=$refresh_rate
    while kill -0 $cmd_pid 2>/dev/null; do
        echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]"
        spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
        if update_display; then
            update_count=$((update_count + 1))
            if [[ $update_count -gt 5 ]]; then
                adaptive_rate=$(awk "BEGIN {print $adaptive_rate * 1.5; exit}")
                [[ $(awk "BEGIN {print ($adaptive_rate > 0.5); exit}") -eq 1 ]] && adaptive_rate=0.5
                update_count=0
            fi
        else
            update_count=0
            adaptive_rate=$refresh_rate
        fi
        sleep $adaptive_rate
    done
    wait $cmd_pid
    local exit_status=$?
    update_display
    if [ $exit_status -eq 0 ]; then
        echo -ne "\r\033[K◉ ${title} [\033[1m\033[32m✓\033[0m]\n"
    else
        echo -ne "\r\033[K◉ ${title} [\033[1m\033[31m✗\033[0m]\n"
    fi
    echo -ne "\r\033[K\n"
    local actual_lines=$(wc -l <"${temp_file}" 2>/dev/null || echo 0)
    [[ $actual_lines -gt $max_lines ]] && actual_lines=$max_lines
    if [[ $actual_lines -gt 0 ]]; then
        local -a final_lines=()
        mapfile -t -n "$actual_lines" final_lines < <(tail -n "$actual_lines" "${temp_file}")

        for ((i = 0; i < actual_lines; i++)); do
            local line=$(simple_truncate "${final_lines[$i]}")
            echo -ne "\r\033[K\033[2m${line}\033[0m\n"
        done
    fi
    tput cnorm 2>/dev/null
    rm -f "${temp_file}"
    trap - INT TERM
    return $exit_status
}

##############################################################################

MESSAGE_LANG_DEFAULT='zh-hans'
MESSAGE_LANG_KEYS=(
    "zh-hans"
    "zh-hant"
    "en"
)
declare -A MESSAGE_LANG_DISPLAY=(
    ['zh-hans']='简体中文'
    ['zh-hant']='繁體中文'
    ['en']='English'
)
declare -A MESSAGE_CONTENTS

function msg() {
    local key="$1"
    shift
    local text="${MESSAGE_CONTENTS[${key}]}"
    if [[ -z "${text}" ]]; then
        echo "${key}"
        return
    fi
    while [[ $# -gt 0 ]]; do
        if [[ "${text}" == *"{}"* ]]; then
            text="${text/\{\}/$1}"
        else
            break
        fi
        shift
    done
    echo "${text}"
}

function init_msg_pack() {
    function load_pack() {
        local func_name="${1}"
        if declare -f "${func_name}" >/dev/null 2>&1; then
            eval "${func_name}"
        fi
    }
    local current_lang="${1:-${MESSAGE_LANG_DEFAULT}}"
    current_lang="$(echo "${current_lang}" | sed 's/^-*//')"
    current_lang="${current_lang,,}"
    if [[ "${MESSAGE_LANG_DISPLAY[${current_lang}]}" ]]; then
        current_lang="${current_lang//-/_}"
        load_pack "msg_pack_${current_lang}"
    fi
}

function msg_pack_zh_hans() {
    MESSAGE_CONTENTS=(
        ['start.welcome']='shizuku · GNU/Linux 一键更换系统软件源'
        ['start.runtimeEnv']='运行环境'
        ['start.dateTime']='系统时间'
        ['end.moreInfo']='脚本运行完毕'
        ['error.cmd.options.needConfirm']='请确认后重新输入'
        ['error.cmd.options.needSpecify']='请在该选项后指定{}'
        ['error.cmd.options.invalid']='命令选项 {} 无效，{}！'
        ['error.cmd.options.validAddress']='有效的地址'
        ['error.cmd.options.sourceAddress']='软件源地址'
        ['error.cmd.options.sourceRepository']='软件源仓库'
        ['error.cmd.options.codename']='版本代号'
        ['error.cmd.options.boolean']=' true 或 false '
        ['error.cmd.options.protocol']=' http 或 https '
        ['error.cmd.options.needProtocol']=' Web 协议(http/https)'
        ['error.cmd.options.validLangKey']='有效的语言 ID '
        ['error.cmd.options.langKey']='语言 '
        ['error.cmd.options.unsupportTwoSourceMode']='不可同时使用两种软件源模式，请确认后重试！'
        ['error.cmd.options.unsupportCodename']='当前系统不支持使用指定版本代号命令选项，请确认后重试！'
        ['error.cmd.options.unsupportEPEL']='当前系统不支持安装 EPEL 附加软件包，请确认后重试！'
        ['error.unsupportSystem']='不支持当前操作系统（{}），请前往官网查看支持列表！'
        ['error.unsupportVersion']='不支持当前系统版本'
        ['error.unknownSystem']='未知系统'
        ['error.unknownVersion']='系统版本未知'
        ['error.input']='输入错误，{}！'
        ['error.needRoot']='权限不足，请切换至 root 账户后运行本脚本，切换命令 {}'
        ['error.defaultBehavior.https']='默认使用 HTTPS 协议'
        ['error.defaultBehavior.noReplace']='默认不更换'
        ['error.defaultBehavior.noOverwrite']='默认不覆盖'
        ['error.defaultBehavior.noUpdate']='默认不更新'
        ['error.defaultBehavior.noClean']='默认不清理'
        ['error.defaultBehavior.noUseIntranetSource']='默认不使用内网地址'
        ['warn.usedIntranetSource']='已切换至内网专用地址，仅限在特定环境下使用！'
        ['warn.unstableDebian']='检测到当前系统为 {} 版本，可能会产生一些无法预料的问题。'
        ['warn.EPEL7']='Extra Packages for Enterprise Linux 7 已结束生命周期并被官方移至归档库！'
        ['warn.needValidNumberIndex']='请输入有效的数字序号！'
        ['warn.needInputNumberIndex']='请输入数字序号！'
        ['info.backuped.sourceFile']='已备份原有 {} 源文件'
        ['tip.EPEL7']='目前部分镜像站没有同步该归档仓库，若换源后出现错误那么请先检查目标镜像站是否支持该仓库。'
        ['interaction.source.type.public']='公网'
        ['interaction.source.type.intranet']='内网'
        ['interaction.source.type.select']='请选择软件源的网络地址(访问方式)：'
        ['interaction.source.type.usePublicAddress']='默认使用软件源的公网地址，是否继续'
        ['interaction.source.select']='请选择你想使用的软件源：'
        ['interaction.source.selectAndInput']='请选择并输入你想使用的软件源'
        ['interaction.protocol.select']='请选择软件源网络协议：'
        ['interaction.protocol.useHttp']='软件源是否使用 HTTP 协议'
        ['interaction.epel.skipReplace']='检测到系统已安装 EPEL 附加软件包，是否替换/覆盖软件源'
        ['interaction.epel.install']='是否安装 EPEL 附加软件包'
        ['interaction.backup.skipOverwrite.sourceFile']='检测到系统存在已备份的 {} 源文件，是否跳过覆盖备份'
        ['interaction.upgrade.skip']='是否跳过更新软件包'
        ['interaction.cache.autoClean']='在更新软件包后，是否自动清理下载缓存'
        ['interaction.common.tip']='提示'
        ['interaction.common.operationCanceled']='操作已取消'
        ['interaction.common.yes']='是'
        ['interaction.common.no']='否'
        ['work.upgradeSoftware']='更新软件包'
        ['work.installEPELPackage']='安装 epel-release 软件包...'
        ['source.sync.text1']='更新软件源'
        ['source.sync.text2']='生成软件源缓存'
        ['source.sync.text3']='刷新软件源'
        ['source.sync.text4']='同步软件源'
        ['source.sync.text5']='更新二进制缓存与频道源'
        ['source.comment.disabledSourceCode']='默认禁用源码镜像以提高更新速度，如需启用请自行取消注释'
        ['source.comment.securitySource']='安全更新软件源'
        ['source.comment.proposedSource']='预发布软件源（不建议启用）'
        ['source.changeResult.success']='软件源更换完毕'
        ['source.changeResult.fail']='软件源更换完毕，但{}失败'
        ['source.changeResult.tipTitle']='请再次执行脚本并更换相同软件源后进行尝试，若仍然{}失败那么可能由以下原因导致：'
        ['source.changeResult.tip1']='网络连通性问题：例如连接异常、由地区影响的网络间歇式中断、禁止外部访问、软件源网站防火墙阻断等'
        ['source.changeResult.tip2']='目标软件源异常：请手动前往软件源（镜像站）地址进行验证：{}'
        ['source.changeResult.tip2.1']='若报错内容是提示某个文件不存在那么有可能是软件源的问题，多常见于正在同步中的软件源仓库'
        ['source.changeResult.tip2.2']='若报错内容是目录（path）不存在也有可能是目标软件源不存在当前系统镜像仓库，即不支持当前系统'
        ['source.changeResult.tip2.3']='建议更换其它镜像站进行尝试，少数情况下软件源若处于同步中状态则可能会出现文件同步错误问题'
        ['source.changeResult.tip3']='原有软件源报错：请先排除系统原有的其它软件源报错，因为脚本不会干预这些无关的软件源配置，解决后重新运行脚本即可'
        ['commands.help']='命令选项(名称/含义/值)：

  --abroad                     使用境外以及海外软件源                                             无
  --edu                        使用中国大陆教育网软件源                                           无
  --source                     指定软件源地址(域名或IP)                                           地址
  --source-security            指定 Debian / Ubuntu 系统 security 仓库的软件源地址(域名或IP)      地址
  --branch                     指定软件源仓库(路径)                                               仓库名
  --branch-security            指定 Debian 系统 security 仓库的软件源仓库(路径)                   仓库名
  --codename                   指定 Debian 系操作系统的版本代号                                   代号名称
  --protocol                   指定 Web 协议                                                      http 或 https
  --use-intranet-source        是否优先使用内网软件源地址                                         true 或 false
  --use-official-source        是否使用目标操作系统的官方软件源                                   true 或 false
  --backup                     是否备份原有软件源                                                 true 或 false
  --upgrade-software           是否更新软件包                                                     true 或 false
  --clean-cache                是否在更新软件包后清理下载缓存                                     true 或 false
  --clean-screen               是否在运行前清除屏幕上的所有内容                                   true 或 false
  --lang                       指定脚本输出的语言                                                 语言
  --ignore-backup-tips         忽略覆盖备份提示                                                   无
  --print-diff                 打印源文件修改前后差异                                             无
  --pure-mode                  纯净模式，精简打印内容                                             无
  --help                       查看帮助菜单                                                       无

问题报告 {}'
        ['mirrors.default.0']='阿里云'
        ['mirrors.default.1']='腾讯云'
        ['mirrors.default.2']='华为云'
        ['mirrors.default.3']='移动云'
        ['mirrors.default.4']='天翼云'
        ['mirrors.default.5']='网易'
        ['mirrors.default.6']='火山引擎'
        ['mirrors.default.7']='清华大学'
        ['mirrors.default.8']='北京大学'
        ['mirrors.default.9']='浙江大学'
        ['mirrors.default.10']='南京大学'
        ['mirrors.default.11']='兰州大学'
        ['mirrors.default.12']='上海交通大学'
        ['mirrors.default.13']='华中科技大学'
        ['mirrors.default.14']='中国科学技术大学'
        ['mirrors.default.15']='中国科学院软件研究所'
        ['mirrors.default.16']='中国科技云'
        ['mirrors.default.17']='官方源'
        ['mirrors.edu.0']='北京大学'
        ['mirrors.edu.1']='北京外国语大学'
        ['mirrors.edu.2']='重庆大学'
        ['mirrors.edu.3']='重庆邮电大学'
        ['mirrors.edu.4']='华中科技大学'
        ['mirrors.edu.5']='吉林大学'
        ['mirrors.edu.6']='荆楚理工学院'
        ['mirrors.edu.7']='兰州大学'
        ['mirrors.edu.8']='南京大学'
        ['mirrors.edu.9']='南京工业大学'
        ['mirrors.edu.10']='南京邮电大学'
        ['mirrors.edu.11']='南方科技大学'
        ['mirrors.edu.12']='南阳理工学院'
        ['mirrors.edu.13']='齐鲁工业大学'
        ['mirrors.edu.14']='清华大学'
        ['mirrors.edu.15']='山东大学'
        ['mirrors.edu.16']='上海交通大学（思源）'
        ['mirrors.edu.17']='上海交通大学（致远）'
        ['mirrors.edu.18']='武昌首义学院'
        ['mirrors.edu.19']='西安交通大学'
        ['mirrors.edu.20']='西北农林科技大学'
        ['mirrors.edu.21']='浙江大学'
        ['mirrors.edu.22']='中国科学技术大学'
        ['mirrors.edu.23']='官方源'
        ['mirrors.abroad.0']='亚洲 · xTom · 香港'
        ['mirrors.abroad.1']='亚洲 · 01Link · 香港'
        ['mirrors.abroad.2']='亚洲 · 新加坡国立大学(NUS) · 新加坡'
        ['mirrors.abroad.3']='亚洲 · SG.GS · 新加坡'
        ['mirrors.abroad.4']='亚洲 · xTom · 新加坡'
        ['mirrors.abroad.5']='亚洲 · 自由软件实验室(NCHC) · 台湾'
        ['mirrors.abroad.6']='亚洲 · OSS Planet · 台湾'
        ['mirrors.abroad.7']='亚洲 · 国立阳明交通大学 · 台湾'
        ['mirrors.abroad.8']='亚洲 · Taiwan Digital Streaming · 台湾'
        ['mirrors.abroad.9']='亚洲 · AniGil Linux Archive · 韩国'
        ['mirrors.abroad.10']='亚洲 · 工业网络安全中心(ICSCoE) · 日本'
        ['mirrors.abroad.11']='亚洲 · 北陆先端科学技术大学院大学(JAIST) · 日本'
        ['mirrors.abroad.12']='亚洲 · 山形大学 · 日本'
        ['mirrors.abroad.13']='亚洲 · xTom · 日本'
        ['mirrors.abroad.14']='亚洲 · GB Network Solutions · 马来西亚'
        ['mirrors.abroad.15']='亚洲 · 孔敬大学 · 泰国'
        ['mirrors.abroad.16']='欧洲 · Vorboss Ltd · 英国'
        ['mirrors.abroad.17']='欧洲 · dogado · 德国'
        ['mirrors.abroad.18']='欧洲 · xTom · 德国'
        ['mirrors.abroad.19']='欧洲 · 亚琛工业大学(RWTH Aachen) · 德国'
        ['mirrors.abroad.20']='欧洲 · 德累斯顿大学(AG DSN) · 德国'
        ['mirrors.abroad.21']='欧洲 · CCIN2P3 · 法国'
        ['mirrors.abroad.22']='欧洲 · Ircam · 法国'
        ['mirrors.abroad.23']='欧洲 · Crans · 法国'
        ['mirrors.abroad.24']='欧洲 · CRIHAN · 法国'
        ['mirrors.abroad.25']='欧洲 · xTom · 荷兰'
        ['mirrors.abroad.26']='欧洲 · Linux Kernel · 荷兰'
        ['mirrors.abroad.27']='欧洲 · xTom · 爱沙尼亚'
        ['mirrors.abroad.28']='欧洲 · netsite · 丹麦'
        ['mirrors.abroad.29']='欧洲 · Dotsrc · 丹麦'
        ['mirrors.abroad.30']='欧洲 · Academic Computer Club · 瑞典'
        ['mirrors.abroad.31']='欧洲 · Lysator · 瑞典'
        ['mirrors.abroad.32']='欧洲 · Yandex · 俄罗斯'
        ['mirrors.abroad.33']='欧洲 · Belgian Research Network · 比利时'
        ['mirrors.abroad.34']='欧洲 · 马萨里克大学信息学院 · 捷克'
        ['mirrors.abroad.35']='欧洲 · 捷克理工大学学生会俱乐部(Silicon Hill) · 捷克'
        ['mirrors.abroad.36']='欧洲 · Vodafone · 捷克'
        ['mirrors.abroad.37']='北美 · Linux Kernel · 美国'
        ['mirrors.abroad.38']='北美 · 麻省理工学院(MIT) · 美国'
        ['mirrors.abroad.39']='北美 · 普林斯顿大学数学系 · 美国'
        ['mirrors.abroad.40']='北美 · 俄勒冈州立大学开源实验室 · 美国'
        ['mirrors.abroad.41']='北美 · Fremont Cabal Internet Exchange(FCIX) · 美国'
        ['mirrors.abroad.42']='北美 · xTom · 美国'
        ['mirrors.abroad.43']='北美 · Steadfast · 美国'
        ['mirrors.abroad.44']='北美 · 不列颠哥伦比亚大学 · 加拿大'
        ['mirrors.abroad.45']='北美 · GoCodeIT · 加拿大'
        ['mirrors.abroad.46']='北美 · Switch · 加拿大'
        ['mirrors.abroad.47']='南美 · PoP-SC · 巴西'
        ['mirrors.abroad.48']='南美 · 蓬塔格罗萨州立大学 · 巴西'
        ['mirrors.abroad.49']='大洋 · Fremont Cabal Internet Exchange(FCIX) · 澳大利亚'
        ['mirrors.abroad.50']='大洋 · AARNet · 澳大利亚'
        ['mirrors.abroad.51']='大洋 · DataMossa · 澳大利亚'
        ['mirrors.abroad.52']='大洋 · Amaze · 澳大利亚'
        ['mirrors.abroad.53']='大洋 · xTom · 澳大利亚'
        ['mirrors.abroad.54']='大洋 · Free Software Mirror Group · 新西兰'
        ['mirrors.abroad.55']='非洲 · Liquid Telecom · 肯尼亚'
        ['mirrors.abroad.56']='非洲 · Dimension Data · 南非'
        ['mirrors.abroad.57']='官方源'
    )
}

function msg_pack_zh_hant() {
    MESSAGE_CONTENTS=(
        ['start.welcome']='shizuku · GNU/Linux 一鍵更換系統軟體源'
        ['start.runtimeEnv']='執行環境'
        ['start.dateTime']='系統時間'
        ['end.moreInfo']='腳本執行完畢'
        ['error.cmd.options.needConfirm']='請確認後重新輸入'
        ['error.cmd.options.needSpecify']='請在該選項後指定{}'
        ['error.cmd.options.invalid']='命令選項 {} 無效，{}！'
        ['error.cmd.options.validAddress']='有效的地址'
        ['error.cmd.options.sourceAddress']='軟體源地址'
        ['error.cmd.options.sourceRepository']='軟體源倉庫'
        ['error.cmd.options.codename']='版本代號'
        ['error.cmd.options.boolean']=' true 或 false '
        ['error.cmd.options.protocol']=' http 或 https '
        ['error.cmd.options.needProtocol']=' Web 協定(http/https)'
        ['error.cmd.options.validLangKey']='有效的語言 ID '
        ['error.cmd.options.langKey']='語言 ID '
        ['error.cmd.options.unsupportTwoSourceMode']='不可同時使用兩種軟體源模式，請確認後重試！'
        ['error.cmd.options.unsupportCodename']='當前系統不支援使用指定版本代號命令選項，請確認後重試！'
        ['error.cmd.options.unsupportEPEL']='當前系統不支援安裝 EPEL 附加軟體包，請確認後重試！'
        ['error.unsupportSystem']='不支援當前系統（{}），請前往官網查看支援清單！'
        ['error.unsupportVersion']='不支援當前系統版本'
        ['error.unknownSystem']='未知系統'
        ['error.unknownVersion']='系統版本未知'
        ['error.input']='輸入錯誤，{}！'
        ['error.needRoot']='權限不足，請切換至 root 帳戶後執行本腳本，切換指令 {}'
        ['error.defaultBehavior.https']='預設使用 HTTPS 協定'
        ['error.defaultBehavior.noReplace']='預設不更換'
        ['error.defaultBehavior.noOverwrite']='預設不覆蓋'
        ['error.defaultBehavior.noUpdate']='預設不更新'
        ['error.defaultBehavior.noClean']='預設不清理'
        ['error.defaultBehavior.noUseIntranetSource']='預設不使用內網地址'
        ['warn.usedIntranetSource']='已切換至內網專用地址，僅限在特定環境下使用！'
        ['warn.unstableDebian']='偵測到當前系統為 {} 版本，可能會產生一些無法預料的問題。'
        ['warn.EPEL7']='Extra Packages for Enterprise Linux 7 已結束生命週期並被官方移至封存庫！'
        ['warn.needValidNumberIndex']='請輸入有效的數字序號！'
        ['warn.needInputNumberIndex']='請輸入數字序號！'
        ['info.backuped.sourceFile']='已備份原有 {} 源檔案'
        ['tip.EPEL7']='目前部分鏡像站尚未同步該封存倉庫，若換源後出現錯誤請先檢查目標鏡像站是否支援該倉庫。'
        ['interaction.source.type.public']='外網'
        ['interaction.source.type.intranet']='內網'
        ['interaction.source.type.select']='請選擇軟體源的網路地址（存取方式）：'
        ['interaction.source.type.usePublicAddress']='預設使用軟體源的外網地址，是否繼續'
        ['interaction.source.select']='請選擇你想使用的軟體源：'
        ['interaction.source.selectAndInput']='請選擇並輸入你想使用的軟體源'
        ['interaction.protocol.select']='請選擇軟體源網路協定：'
        ['interaction.protocol.useHttp']='軟體源是否使用 HTTP 协议'
        ['interaction.epel.skipReplace']='偵測到系統已安裝 EPEL 附加軟體包，是否替換/覆蓋軟體源'
        ['interaction.epel.install']='是否安裝 EPEL 附加軟體包'
        ['interaction.backup.skipOverwrite.sourceFile']='偵測到系統存在已備份的 {} 源檔案，是否跳過覆蓋備份'
        ['interaction.upgrade.skip']='是否跳過更新軟體包'
        ['interaction.cache.autoClean']='在更新軟體包後，是否自動清理下載快取'
        ['interaction.common.tip']='提示'
        ['interaction.common.operationCanceled']='操作已取消'
        ['interaction.common.yes']='是'
        ['interaction.common.no']='否'
        ['work.upgradeSoftware']='更新軟體包'
        ['work.installEPELPackage']='正在安裝 epel-release 套件...'
        ['source.sync.text1']='更新軟體源'
        ['source.sync.text2']='產生軟體源快取'
        ['source.sync.text3']='重新整理軟體源'
        ['source.sync.text4']='同步軟體源'
        ['source.sync.text5']='更新二進位快取與頻道來源'
        ['source.comment.disabledSourceCode']='預設停用原始碼鏡像以提升更新速度，如需啟用請自行取消註解'
        ['source.comment.securitySource']='安全更新軟體源'
        ['source.comment.proposedSource']='預發布軟體源（不建議啟用）'
        ['source.changeResult.success']='軟體源更換完畢'
        ['source.changeResult.fail']='軟體源更換完畢，但{}失敗'
        ['source.changeResult.tipTitle']='請再次執行腳本並更換相同軟體源後重試，若仍然{}失敗則可能由下列原因造成：'
        ['source.changeResult.tip1']='網路連通性問題：例如連線異常、地域因素造成的網路間歇中斷、禁止外部存取、軟體源站台防火牆阻斷等'
        ['source.changeResult.tip2']='目標軟體源異常：請手動前往軟體源（鏡像站）地址進行驗證：{}'
        ['source.changeResult.tip2.1']='若錯誤內容是提示某個檔案不存在，可能是軟體源的問題，常見於鏡像站同步期間'
        ['source.changeResult.tip2.2']='若錯誤內容是目錄不存在，也可能代表目標軟體源不含當前系統的鏡像倉庫，即不支援當前系統'
        ['source.changeResult.tip2.3']='建議更換其他鏡像站再試，少數情況下軟體源若處於同步中狀態則可能會出現檔案同步錯誤'
        ['source.changeResult.tip3']='原有軟體源報錯：請先排除系統其他軟體源的錯誤，因為腳本不會處理無關的軟體源配置，解決後重新執行腳本即可'
        ['commands.help']='命令選項(名稱/含義/值)：

  --abroad                     使用境外以及海外軟體源                                                 無
  --edu                        使用中國大陸教育網軟體源                                               無
  --source                     指定軟體源地址(網域名稱或IP)                                           位址
  --source-security            指定 Debian / Ubuntu 系統 security 倉庫的軟體源地址(網域名稱或IP)      位址
  --branch                     指定軟體源倉庫(路徑)                                                   倉庫名稱
  --branch-security            指定 Debian 系統 security 倉庫的軟體源倉庫(路徑)                       倉庫名稱
  --codename                   指定 Debian 系 / openKylin 系統的版本代號                              代號名稱
  --protocol                   指定 Web 協定                                                          http 或 https
  --use-intranet-source        是否優先使用內部網路軟體源位址                                         true 或 false
  --use-official-source        是否使用目標系統的官方軟體源                                           true 或 false
  --backup                     是否備份原有軟體源                                                     true 或 false
  --upgrade-software           是否更新軟體包                                                         true 或 false
  --clean-cache                是否在更新軟體包後清理下載快取                                         true 或 false
  --clean-screen               是否在運行前清除螢幕上的所有內容                                       true 或 false
  --lang                       指定腳本輸出的語言                                                     语言
  --ignore-backup-tips         忽略覆蓋備份提示                                                       無
  --print-diff                 是否列印原始文件修改前後差異                                           無
  --pure-mode                  純淨模式，精簡列印內容                                                 無
  --help                       查看幫助選單                                                           無

問題報告 {}'
        ['mirrors.default.0']='阿里雲'
        ['mirrors.default.1']='騰訊雲'
        ['mirrors.default.2']='華為雲'
        ['mirrors.default.3']='移動雲'
        ['mirrors.default.4']='天翼雲'
        ['mirrors.default.5']='網易'
        ['mirrors.default.6']='火山引擎'
        ['mirrors.default.7']='清華大學'
        ['mirrors.default.8']='北京大學'
        ['mirrors.default.9']='浙江大學'
        ['mirrors.default.10']='南京大學'
        ['mirrors.default.11']='蘭州大學'
        ['mirrors.default.12']='上海交通大學'
        ['mirrors.default.13']='華中科技大學'
        ['mirrors.default.14']='中國科學技術大學'
        ['mirrors.default.15']='中國科學院軟件研究所'
        ['mirrors.default.16']='中國科技雲'
        ['mirrors.default.17']='官方源'
        ['mirrors.edu.0']='北京大學'
        ['mirrors.edu.1']='北京外國語大學'
        ['mirrors.edu.2']='重慶大學'
        ['mirrors.edu.3']='重慶郵電大學'
        ['mirrors.edu.4']='華中科技大學'
        ['mirrors.edu.5']='吉林大學'
        ['mirrors.edu.6']='荊楚理工學院'
        ['mirrors.edu.7']='蘭州大學'
        ['mirrors.edu.8']='南京大學'
        ['mirrors.edu.9']='南京工業大學'
        ['mirrors.edu.10']='南京郵電大學'
        ['mirrors.edu.11']='南方科技大學'
        ['mirrors.edu.12']='南陽理工學院'
        ['mirrors.edu.13']='齊魯工業大學'
        ['mirrors.edu.14']='清華大學'
        ['mirrors.edu.15']='山東大學'
        ['mirrors.edu.16']='上海交通大學（思源）'
        ['mirrors.edu.17']='上海交通大學（致遠）'
        ['mirrors.edu.18']='武昌首義學院'
        ['mirrors.edu.19']='西安交通大學'
        ['mirrors.edu.20']='西北農林科技大學'
        ['mirrors.edu.21']='浙江大學'
        ['mirrors.edu.22']='中國科學技術大學'
        ['mirrors.edu.23']='官方源'
        ['mirrors.abroad.0']='亞洲 · xTom · 香港'
        ['mirrors.abroad.1']='亞洲 · 01Link · 香港'
        ['mirrors.abroad.2']='亞洲 · 新加坡國立大學(NUS) · 新加坡'
        ['mirrors.abroad.3']='亞洲 · SG.GS · 新加坡'
        ['mirrors.abroad.4']='亞洲 · xTom · 新加坡'
        ['mirrors.abroad.5']='亞洲 · 自由軟體實驗室(NCHC) · 臺灣'
        ['mirrors.abroad.6']='亞洲 · OSS Planet · 臺灣'
        ['mirrors.abroad.7']='亞洲 · 國立陽明交通大學 · 臺灣'
        ['mirrors.abroad.8']='亞洲 · Taiwan Digital Streaming · 臺灣'
        ['mirrors.abroad.9']='亞洲 · AniGil Linux Archive · 韓國'
        ['mirrors.abroad.10']='亞洲 · 工業網路安全中心(ICSCoE) · 日本'
        ['mirrors.abroad.11']='亞洲 · 北陸先端科學技術大學院大學(JAIST) · 日本'
        ['mirrors.abroad.12']='亞洲 · 山形大學 · 日本'
        ['mirrors.abroad.13']='亞洲 · xTom · 日本'
        ['mirrors.abroad.14']='亞洲 · GB Network Solutions · 馬來西亞'
        ['mirrors.abroad.15']='亞洲 · 孔敬大學 · 泰國'
        ['mirrors.abroad.16']='歐洲 · Vorboss Ltd · 英國'
        ['mirrors.abroad.17']='歐洲 · dogado · 德國'
        ['mirrors.abroad.18']='歐洲 · xTom · 德國'
        ['mirrors.abroad.19']='歐洲 · 亞琛工業大學(RWTH Aachen) · 德國'
        ['mirrors.abroad.20']='歐洲 · 德勒斯登大學(AG DSN) · 德國'
        ['mirrors.abroad.21']='歐洲 · CCIN2P3 · 法國'
        ['mirrors.abroad.22']='歐洲 · Ircam · 法國'
        ['mirrors.abroad.23']='歐洲 · Crans · 法國'
        ['mirrors.abroad.24']='歐洲 · CRIHAN · 法國'
        ['mirrors.abroad.25']='歐洲 · xTom · 荷蘭'
        ['mirrors.abroad.26']='歐洲 · Linux Kernel · 荷蘭'
        ['mirrors.abroad.27']='歐洲 · xTom · 愛沙尼亞'
        ['mirrors.abroad.28']='歐洲 · netsite · 丹麥'
        ['mirrors.abroad.29']='歐洲 · Dotsrc · 丹麥'
        ['mirrors.abroad.30']='歐洲 · Academic Computer Club · 瑞典'
        ['mirrors.abroad.31']='歐洲 · Lysator · 瑞典'
        ['mirrors.abroad.32']='歐洲 · Yandex · 俄羅斯'
        ['mirrors.abroad.33']='歐洲 · Belgian Research Network · 比利時'
        ['mirrors.abroad.34']='歐洲 · 馬薩里克大學信息學院 · 捷克'
        ['mirrors.abroad.35']='歐洲 · 捷克理工大學學生會俱樂部(Silicon Hill) · 捷克'
        ['mirrors.abroad.36']='歐洲 · Vodafone · 捷克'
        ['mirrors.abroad.37']='北美 · Linux Kernel · 美國'
        ['mirrors.abroad.38']='北美 · 麻省理工學院(MIT) · 美國'
        ['mirrors.abroad.39']='北美 · 普林斯頓大學數學系 · 美國'
        ['mirrors.abroad.40']='北美 · 俄勒岡州立大學開源實驗室 · 美國'
        ['mirrors.abroad.41']='北美 · Fremont Cabal Internet Exchange(FCIX) · 美國'
        ['mirrors.abroad.42']='北美 · xTom · 美國'
        ['mirrors.abroad.43']='北美 · Steadfast · 美國'
        ['mirrors.abroad.44']='北美 · 不列顛哥倫比亞大學 · 加拿大'
        ['mirrors.abroad.45']='北美 · GoCodeIT · 加拿大'
        ['mirrors.abroad.46']='北美 · Switch · 加拿大'
        ['mirrors.abroad.47']='南美 · PoP-SC · 巴西'
        ['mirrors.abroad.48']='南美 · 蓬塔格羅薩州立大學 · 巴西'
        ['mirrors.abroad.49']='大洋 · Fremont Cabal Internet Exchange(FCIX) · 澳大利亞'
        ['mirrors.abroad.50']='大洋 · AARNet · 澳大利亞'
        ['mirrors.abroad.51']='大洋 · DataMossa · 澳大利亞'
        ['mirrors.abroad.52']='大洋 · Amaze · 澳大利亞'
        ['mirrors.abroad.53']='大洋 · xTom · 澳大利亞'
        ['mirrors.abroad.54']='大洋 · Free Software Mirror Group · 紐西蘭'
        ['mirrors.abroad.55']='非洲 · Liquid Telecom · 肯亞'
        ['mirrors.abroad.56']='非洲 · Dimension Data · 南非'
        ['mirrors.abroad.57']='官方源'
    )
}

function msg_pack_en() {
    MESSAGE_CONTENTS=(
        ['start.welcome']='shizuku · GNU/Linux mirror switching script'
        ['start.runtimeEnv']='Runtime Env'
        ['start.dateTime']='System Time'
        ['end.moreInfo']='Script execution completed'
        ['error.cmd.options.needConfirm']='Please confirm and re-enter'
        ['error.cmd.options.needSpecify']='Please specify {} after this option'
        ['error.cmd.options.invalid']='Command option {} is invalid, {}!'
        ['error.cmd.options.validAddress']='a valid address'
        ['error.cmd.options.sourceAddress']='mirror address'
        ['error.cmd.options.sourceRepository']='mirror repository'
        ['error.cmd.options.codename']='version codename'
        ['error.cmd.options.boolean']=' true or false '
        ['error.cmd.options.protocol']=' http or https '
        ['error.cmd.options.needProtocol']=' Web protocol(http/https)'
        ['error.cmd.options.validLangKey']='A valid language ID '
        ['error.cmd.options.langKey']='language ID '
        ['error.cmd.options.unsupportTwoSourceMode']='Two mirror modes cannot be used at the same time. Please retry!'
        ['error.cmd.options.unsupportCodename']='The current system does not support specifying a codename. Please retry!'
        ['error.cmd.options.unsupportEPEL']='The current system cannot install the EPEL add-on package. Please retry!'
        ['error.unsupportSystem']='The operating system ({}) is not supported. Check the supported list on the website!'
        ['error.unsupportVersion']='The current system version is not supported'
        ['error.unknownSystem']='Unknown system'
        ['error.unknownVersion']='System version unknown'
        ['error.input']='Input error, {}!'
        ['error.needRoot']='Insufficient privileges, please run this script as root. Switch command: {}'
        ['error.defaultBehavior.https']='HTTPS is used by default'
        ['error.defaultBehavior.noReplace']='No replacement by default'
        ['error.defaultBehavior.noOverwrite']='No overwrite by default'
        ['error.defaultBehavior.noUpdate']='No update by default'
        ['error.defaultBehavior.noClean']='No cleanup by default'
        ['error.defaultBehavior.noUseIntranetSource']='Intranet mirrors are disabled by default'
        ['warn.usedIntranetSource']='Switched to an intranet-only mirror. Use only in the intended environment!'
        ['warn.unstableDebian']='Detected system version {} which may cause unpredictable issues.'
        ['warn.EPEL7']='Extra Packages for Enterprise Linux 7 reached end-of-life and was moved to the archive!'
        ['warn.needValidNumberIndex']='Please enter a valid number index!'
        ['warn.needInputNumberIndex']='Please enter a number index!'
        ['info.backuped.sourceFile']='Backed up existing {} source file'
        ['tip.EPEL7']="Some mirrors don't sync that archive. If errors occur, verify the target mirror provides it."
        ['interaction.source.type.public']='Public'
        ['interaction.source.type.intranet']='Intranet'
        ['interaction.source.type.select']='Please select network address (access method) for mirror:'
        ['interaction.source.type.usePublicAddress']='Use public network address for mirror by default, continue'
        ['interaction.source.select']='Please select the mirror you want to use:'
        ['interaction.source.selectAndInput']='Please select and enter the mirror you want to use'
        ['interaction.protocol.select']='Please select network protocol for mirror:'
        ['interaction.protocol.useHttp']='Use HTTP protocol for mirror'
        ['interaction.epel.skipReplace']='EPEL already installed. Replace/overwrite mirror'
        ['interaction.epel.install']='Install EPEL add-on package'
        ['interaction.backup.skipOverwrite.sourceFile']='A backup of {} already exists. Skip overwriting'
        ['interaction.upgrade.skip']='Skip upgrading packages'
        ['interaction.cache.autoClean']='Clean download cache after upgrading packages'
        ['interaction.common.tip']='Tip'
        ['interaction.common.operationCanceled']='Operation canceled'
        ['interaction.common.yes']='Yes'
        ['interaction.common.no']='No'
        ['work.upgradeSoftware']='Upgrading packages'
        ['work.installEPELPackage']='Installing epel-release package...'
        ['source.sync.text1']='Update APT package index'
        ['source.sync.text2']='Generate mirror cache'
        ['source.sync.text3']='Refreshing mirrors'
        ['source.sync.text4']='Synchronizing mirrors'
        ['source.sync.text5']='Updating binary cache and channels'
        ['source.comment.disabledSourceCode']='Source code mirrors are disabled by default for speed. Uncomment to enable'
        ['source.comment.securitySource']='Security updates'
        ['source.comment.proposedSource']='Proposed repository (not recommended)'
        ['source.changeResult.success']='Mirror switching complete'
        ['source.changeResult.fail']='Mirror switching complete, but {} failed'
        ['source.changeResult.tipTitle']='Run the script again with the same mirror. If {} still fails, consider:'
        ['source.changeResult.tip1']='Connectivity issues: unstable network, blocked access, or mirror-side firewall rules'
        ['source.changeResult.tip2']='Mirror issues: test the mirror manually at {}'
        ['source.changeResult.tip2.1']='If a file is missing, the mirror might still be syncing'
        ['source.changeResult.tip2.2']='If a path is missing, the mirror may not provide this system repository'
        ['source.changeResult.tip2.3']='Try another mirror. Syncing mirrors may temporarily serve broken content'
        ['source.changeResult.tip3']='Original mirrors failing: fix unrelated mirror errors and rerun the script'
        ['commands.help']='Command options(name/meaning/value):

  --abroad                     Use overseas mirrors                                                         none
  --edu                        Use China education network mirrors                                          none
  --source                     Specify mirror address (domain or IP)                                        address
  --source-security            Specify Debian/Ubuntu security repo address (domain or IP)                   address
  --branch                     Specify mirror repository (repo name)                                        repo name
  --branch-security            Specify Debian security repo (path)                                          repo name
  --codename                   Specify Debian/openKylin codename                                            codename
  --protocol                   Specify web protocol                                                         http or https
  --use-intranet-source        Prefer intranet mirror address                                               true or false
  --use-official-source        Use official mirror of target OS                                             true or false
  --backup                     Backup original mirrors                                                      true or false
  --upgrade-software           Upgrade packages                                                             true or false
  --clean-cache                Clean cache after upgrade                                                    true or false
  --clean-screen               Clear screen before running                                                  true or false
  --lang                       Specify the language of the script output                                    language
  --ignore-backup-tips         Ignore backup overwrite prompt                                               none
  --print-diff                 Print diff before and after modification                                     none
  --pure-mode                  Pure mode, minimal output                                                    none
  --help                       Show help menu                                                               none

Issue Report {}'
        ['mirrors.default.0']='Alibaba Cloud'
        ['mirrors.default.1']='Tencent Cloud'
        ['mirrors.default.2']='Huawei Cloud'
        ['mirrors.default.3']='China Mobile Cloud'
        ['mirrors.default.4']='China Telecom Cloud'
        ['mirrors.default.5']='NetEase'
        ['mirrors.default.6']='Volcengine'
        ['mirrors.default.7']='Tsinghua University'
        ['mirrors.default.8']='Peking University'
        ['mirrors.default.9']='Zhejiang University'
        ['mirrors.default.10']='Nanjing University'
        ['mirrors.default.11']='Lanzhou University'
        ['mirrors.default.12']='Shanghai Jiao Tong University'
        ['mirrors.default.13']='Huazhong University of Science and Technology'
        ['mirrors.default.14']='University of Science and Technology of China'
        ['mirrors.default.15']='Institute of Software, Chinese Academy of Sciences'
        ['mirrors.default.16']='China Science & Technology Cloud'
        ['mirrors.default.17']='Official Source'
        ['mirrors.edu.0']='Peking University'
        ['mirrors.edu.1']='Beijing Foreign Studies University'
        ['mirrors.edu.2']='Chongqing University'
        ['mirrors.edu.3']='Chongqing University of Posts and Telecommunications'
        ['mirrors.edu.4']='Huazhong University of Science and Technology'
        ['mirrors.edu.5']='Jilin University'
        ['mirrors.edu.6']='Jingchu University of Technology'
        ['mirrors.edu.7']='Lanzhou University'
        ['mirrors.edu.8']='Nanjing University'
        ['mirrors.edu.9']='Nanjing Tech University'
        ['mirrors.edu.10']='Nanjing University of Posts and Telecommunications'
        ['mirrors.edu.11']='Southern University of Science and Technology'
        ['mirrors.edu.12']='Nanyang Institute of Technology'
        ['mirrors.edu.13']='Qilu University of Technology'
        ['mirrors.edu.14']='Tsinghua University'
        ['mirrors.edu.15']='Shandong University'
        ['mirrors.edu.16']='Shanghai Jiao Tong University (Siyuan)'
        ['mirrors.edu.17']='Shanghai Jiao Tong University (Zhiyuan)'
        ['mirrors.edu.18']='Wuchang Shouyi University'
        ['mirrors.edu.19']="Xi'an Jiaotong University"
        ['mirrors.edu.20']='Northwest A&F University'
        ['mirrors.edu.21']='Zhejiang University'
        ['mirrors.edu.22']='University of Science and Technology of China'
        ['mirrors.edu.23']='Official Source'
        ['mirrors.abroad.0']='AS · xTom · Hong Kong'
        ['mirrors.abroad.1']='AS · 01Link · Hong Kong'
        ['mirrors.abroad.2']='AS · National University of Singapore (NUS) · Singapore'
        ['mirrors.abroad.3']='AS · SG.GS · Singapore'
        ['mirrors.abroad.4']='AS · xTom · Singapore'
        ['mirrors.abroad.5']='AS · NCHC Free Software Lab · Taiwan'
        ['mirrors.abroad.6']='AS · OSS Planet · Taiwan'
        ['mirrors.abroad.7']='AS · National Yang Ming Chiao Tung University · Taiwan'
        ['mirrors.abroad.8']='AS · Taiwan Digital Streaming · Taiwan'
        ['mirrors.abroad.9']='AS · AniGil Linux Archive · Korea'
        ['mirrors.abroad.10']='AS · ICSCoE (Industrial Cyber Security Center of Excellence) · Japan'
        ['mirrors.abroad.11']='AS · JAIST (Japan Advanced Institute of Science and Technology) · Japan'
        ['mirrors.abroad.12']='AS · Yamagata University · Japan'
        ['mirrors.abroad.13']='AS · xTom · Japan'
        ['mirrors.abroad.14']='AS · GB Network Solutions · Malaysia'
        ['mirrors.abroad.15']='AS · Khon Kaen University · Thailand'
        ['mirrors.abroad.16']='EU · Vorboss Ltd · United Kiongdom'
        ['mirrors.abroad.17']='EU · dogado · Germany'
        ['mirrors.abroad.18']='EU · xTom · Germany'
        ['mirrors.abroad.19']='EU · RWTH Aachen University · Germany'
        ['mirrors.abroad.20']='EU · Dresden University (AG DSN) · Germany'
        ['mirrors.abroad.21']='EU · CCIN2P3 · France'
        ['mirrors.abroad.22']='EU · Ircam · France'
        ['mirrors.abroad.23']='EU · Crans · France'
        ['mirrors.abroad.24']='EU · CRIHAN · France'
        ['mirrors.abroad.25']='EU · xTom · Netherlands'
        ['mirrors.abroad.26']='EU · Linux Kernel · Netherlands'
        ['mirrors.abroad.27']='EU · xTom · Estonia'
        ['mirrors.abroad.28']='EU · netsite · Denmark'
        ['mirrors.abroad.29']='EU · Dotsrc · Denmark'
        ['mirrors.abroad.30']='EU · Academic Computer Club · Sweden'
        ['mirrors.abroad.31']='EU · Lysator · Sweden'
        ['mirrors.abroad.32']='EU · Yandex · Russia'
        ['mirrors.abroad.33']='EU · Belgian Research Network · Belgium'
        ['mirrors.abroad.34']='EU · Masaryk University Faculty of Informatics · Czech'
        ['mirrors.abroad.35']='EU · Czech Technical University Student Club (Silicon Hill) · Czech'
        ['mirrors.abroad.36']='EU · Vodafone · Czech'
        ['mirrors.abroad.37']='NA · Linux Kernel · United States'
        ['mirrors.abroad.38']='NA · MIT · United States'
        ['mirrors.abroad.39']='NA · Princeton University Department of Mathematics · United States'
        ['mirrors.abroad.40']='NA · Oregon State University Open Source Lab · United States'
        ['mirrors.abroad.41']='NA · Fremont Cabal Internet Exchange(FCIX) · United States'
        ['mirrors.abroad.42']='NA · xTom · United States'
        ['mirrors.abroad.43']='NA · Steadfast · United States'
        ['mirrors.abroad.44']='NA · University of British Columbia · Canada'
        ['mirrors.abroad.45']='NA · GoCodeIT · Canada'
        ['mirrors.abroad.46']='NA · Switch · Canada'
        ['mirrors.abroad.47']='SA · PoP-SC · Brazil'
        ['mirrors.abroad.48']='SA · State University of Ponta Grossa · Brazil'
        ['mirrors.abroad.49']='OC · Fremont Cabal Internet Exchange(FCIX) · Australia'
        ['mirrors.abroad.50']='OC · AARNet · Australia'
        ['mirrors.abroad.51']='OC · DataMossa · Australia'
        ['mirrors.abroad.52']='OC · Amaze · Australia'
        ['mirrors.abroad.53']='OC · xTom · Australia'
        ['mirrors.abroad.54']='OC · Free Software Mirror Group · New Zealand'
        ['mirrors.abroad.55']='AF · Liquid Telecom · Kenya'
        ['mirrors.abroad.56']='AF · Dimension Data · South Africa'
        ['mirrors.abroad.57']='Official Source'
    )
}

##############################################################################

init_msg_pack
handle_command_options "$@"
main
