Add multilingual dialog to step one (#6)

This commit is contained in:
ochenstarik-ui 2026-07-15 14:04:48 +07:00 committed by GitHub
parent 78da5ae6e7
commit a1ef247af1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 376 additions and 56 deletions

View file

@ -51,7 +51,7 @@ sudo ./ochenstarik-server-install.sh
| Файл | Назначение | Можно запускать отдельно |
| --- | --- | --- |
| `ochenstarik-server-install.sh` | Единый пошаговый мастер: установить, пропустить или завершить на каждом этапе | Да; рекомендуемый способ полной настройки |
| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да |
| `ochenstarik-server-1.sh` | Предлагает выбрать язык диалога, часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да |
| `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да |
| `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 |
| `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 |
@ -115,7 +115,9 @@ bash -n ./ИМЯ-СКРИПТА.sh
Файл: `ochenstarik-server-1.sh`.
Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется.
Самый первый запрос выбирает язык диалога установщика: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Этот выбор меняет вопросы и сообщения скрипта, но не системную локаль сервера.
Затем показывается нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется.
После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления.

View file

@ -14,28 +14,28 @@ readonly LEGACY_LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent
readonly LEGACY_LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list"
readonly PROGRAM_PACKAGES="${CONFIG_DIR}/step1-installed-packages.list"
readonly -a PROGRAM_GROUP_TITLES=(
"Терминал и повседневная работа"
"Диагностика сети"
"Мониторинг системы и дисков"
"Безопасность и обслуживание Ubuntu"
"Разработка и автоматизация"
"Архивы, таблицы и локальные данные"
"Документы, PDF и OCR"
"Изображения, аудио и видео"
"Резервное копирование и синхронизация"
declare -a PROGRAM_GROUP_TITLES=(
"Terminal and everyday tools"
"Network diagnostics"
"System and disk monitoring"
"Ubuntu security and maintenance"
"Development and automation"
"Archives, tables, and local data"
"Documents, PDF, and OCR"
"Images, audio, and video"
"Backup and synchronization"
)
readonly -a PROGRAM_GROUP_DESCRIPTIONS=(
"git, wget, curl, jq/yq, ripgrep/fd/fzf, lnav, редакторы, mc, tmux, htop/btop, ncdu, rsync"
"DNS/ping/MTR/traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat"
"iotop, sysstat, SMART, датчики, atop, nethogs, vnStat и инструменты процессов"
"auditd, AppArmor tools, Lynis, needrestart, автоматические обновления, debsums, ACL/xattr"
"компилятор, ShellCheck, Python pip/venv и pipx"
"7-Zip/ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis"
declare -a PROGRAM_GROUP_DESCRIPTIONS=(
"git, wget, curl, jq/yq, ripgrep/fd/fzf, lnav, editors, mc, tmux, htop/btop, ncdu, rsync"
"DNS/ping/MTR/traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool, socat"
"iotop, sysstat, SMART, sensors, atop, nethogs, vnStat, process tools"
"auditd, AppArmor tools, Lynis, needrestart, automatic updates, debsums, ACL/xattr"
"compiler, ShellCheck, Python pip/venv, pipx"
"7-Zip/ZIP/RAR, SQLite, csvkit, PostgreSQL and Redis clients"
"Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword/catdoc"
"ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo"
"restic, BorgBackup и rclone"
"ImageMagick, ExifTool, WebP, FFmpeg, MediaInfo"
"restic, BorgBackup, rclone"
)
readonly -a PROGRAM_GROUP_PACKAGES=(
@ -51,6 +51,315 @@ readonly -a PROGRAM_GROUP_PACKAGES=(
)
APT_UPDATED=no
UI_LANG=en
declare -A UI_TEXT=()
load_ui_text() {
UI_TEXT=(
[input_interrupted]="Input was interrupted"
[invalid_number]="Enter a number from %s to %s"
[no_options]="No options are available for: %s"
[select_option]="Enter the option number: "
[select_region]="Select a region:"
[select_timezone]="Select a timezone in %s:"
[timezone_heading]="Select the server timezone (current: %s):"
[other_timezone]="Other timezone (select by region)"
[unknown_timezone]="Unknown timezone: %s"
[timezone_set]="Timezone set to %s"
[terminal_heading]="Select the language for new terminal sessions (current: %s):"
[terminal_keep]="Keep the current terminal language"
[terminal_other]="Other locale from the full list"
[terminal_prompt]="Language number [1 — keep current]: "
[terminal_unchanged]="Terminal language was not changed"
[terminal_full_list]="Select a locale from the full list:"
[locale_package_missing]="Language package is unavailable and will be skipped: %s"
[locale_selected]="Locale %s selected. It will apply at the next login"
[program_heading]="Select program groups to install:"
[program_none]="Do not install additional programs"
[program_example]="Enter numbers separated by spaces, for example: 1 2 4 9."
[program_prompt]="Selection [Enter — install all groups]: "
[program_skipped]="Installation of additional programs was skipped"
[program_bad_number]="Invalid group number: %s"
[program_out_of_range]="Group number is out of range: %s"
[program_selected]="Selected group: %s"
[package_missing]="Package is unavailable in the configured repositories and will be skipped: %s"
[no_packages]="None of the selected packages were found"
[programs_installed]="Selected programs have been installed"
[apt_update]="Updating the package list"
[root_required]="Run this script as root"
[required_missing]="Required command not found: %s"
[swap_configuring]="Configuring %s swap file"
[setup_complete]="Server memory setup is complete"
[next_script]="Now run ochenstarik-server-2.sh as root."
)
case "$UI_LANG" in
ru) UI_TEXT+=(
[input_interrupted]="Ввод прерван" [invalid_number]="Введите номер от %s до %s"
[select_option]="Введите номер варианта: " [select_region]="Выберите регион:"
[select_timezone]="Выберите часовой пояс в регионе %s:"
[timezone_heading]="Выберите часовой пояс сервера (сейчас: %s):"
[other_timezone]="Другой часовой пояс (выбор по региону)"
[unknown_timezone]="Неизвестный часовой пояс: %s" [timezone_set]="Установлен часовой пояс %s"
[terminal_heading]="Выберите язык новых терминальных сессий (сейчас: %s):"
[terminal_keep]="Не менять язык терминала" [terminal_other]="Другая локаль из полного списка"
[terminal_prompt]="Номер языка [1 — не менять]: " [terminal_unchanged]="Язык терминала не изменён"
[terminal_full_list]="Выберите локаль из полного списка:"
[locale_package_missing]="Языковой пакет недоступен и будет пропущен: %s"
[locale_selected]="Выбрана локаль %s. Она применится при следующем входе"
[program_heading]="Выберите наборы программ для установки:"
[program_none]="Не устанавливать дополнительные программы"
[program_example]="Введите номера через пробел, например: 1 2 4 9."
[program_prompt]="Выбор [Enter — установить все наборы]: "
[program_skipped]="Установка дополнительных программ пропущена"
[program_bad_number]="Некорректный номер набора: %s"
[program_out_of_range]="Номер набора вне диапазона: %s"
[program_selected]="Выбран набор: %s"
[package_missing]="Пакет отсутствует в подключённых репозиториях и будет пропущен: %s"
[no_packages]="Ни один выбранный пакет не найден"
[programs_installed]="Выбранные программы установлены" [apt_update]="Обновление списка пакетов"
[root_required]="Запустите этот скрипт от root"
[required_missing]="Не найдена обязательная команда: %s"
[swap_configuring]="Настройка файла подкачки размером %s"
[setup_complete]="Настройка памяти сервера завершена"
[next_script]="Теперь запустите ochenstarik-server-2.sh от root."
) ;;
es) UI_TEXT+=(
[input_interrupted]="Entrada interrumpida" [invalid_number]="Introduzca un número del %s al %s"
[select_option]="Introduzca el número de opción: " [select_region]="Seleccione una región:"
[select_timezone]="Seleccione una zona horaria en %s:"
[timezone_heading]="Seleccione la zona horaria del servidor (actual: %s):"
[other_timezone]="Otra zona horaria (seleccionar por región)" [timezone_set]="Zona horaria configurada: %s"
[terminal_heading]="Seleccione el idioma para nuevas sesiones de terminal (actual: %s):"
[terminal_keep]="Mantener el idioma actual" [terminal_other]="Otra configuración regional de la lista completa"
[terminal_prompt]="Número de idioma [1 — mantener]: " [terminal_unchanged]="El idioma del terminal no se cambió"
[terminal_full_list]="Seleccione una configuración regional de la lista completa:"
[program_heading]="Seleccione los grupos de programas que desea instalar:"
[program_none]="No instalar programas adicionales"
[program_example]="Introduzca números separados por espacios, por ejemplo: 1 2 4 9."
[program_prompt]="Selección [Enter — instalar todos]: " [program_skipped]="Se omitió la instalación de programas adicionales"
[program_bad_number]="Número de grupo no válido: %s" [program_out_of_range]="Número de grupo fuera de rango: %s"
[program_selected]="Grupo seleccionado: %s" [programs_installed]="Los programas seleccionados se instalaron"
[apt_update]="Actualizando la lista de paquetes" [root_required]="Ejecute este script como root"
[swap_configuring]="Configurando un archivo swap de %s" [setup_complete]="Configuración de memoria completada"
[next_script]="Ahora ejecute ochenstarik-server-2.sh como root."
) ;;
de) UI_TEXT+=(
[input_interrupted]="Eingabe wurde unterbrochen" [invalid_number]="Geben Sie eine Zahl von %s bis %s ein"
[select_option]="Optionsnummer eingeben: " [select_region]="Region auswählen:"
[select_timezone]="Zeitzone in %s auswählen:"
[timezone_heading]="Server-Zeitzone auswählen (aktuell: %s):"
[other_timezone]="Andere Zeitzone (nach Region auswählen)" [timezone_set]="Zeitzone auf %s gesetzt"
[terminal_heading]="Sprache für neue Terminalsitzungen auswählen (aktuell: %s):"
[terminal_keep]="Aktuelle Terminalsprache beibehalten" [terminal_other]="Andere Locale aus der vollständigen Liste"
[terminal_prompt]="Sprachnummer [1 — beibehalten]: " [terminal_unchanged]="Terminalsprache wurde nicht geändert"
[terminal_full_list]="Locale aus der vollständigen Liste auswählen:"
[program_heading]="Zu installierende Programmgruppen auswählen:"
[program_none]="Keine zusätzlichen Programme installieren"
[program_example]="Nummern durch Leerzeichen getrennt eingeben, zum Beispiel: 1 2 4 9."
[program_prompt]="Auswahl [Enter — alle installieren]: " [program_skipped]="Installation zusätzlicher Programme übersprungen"
[program_bad_number]="Ungültige Gruppennummer: %s" [program_out_of_range]="Gruppennummer außerhalb des Bereichs: %s"
[program_selected]="Ausgewählte Gruppe: %s" [programs_installed]="Ausgewählte Programme wurden installiert"
[apt_update]="Paketliste wird aktualisiert" [root_required]="Führen Sie dieses Skript als root aus"
[swap_configuring]="%s-Swap-Datei wird eingerichtet" [setup_complete]="Speichereinrichtung abgeschlossen"
[next_script]="Führen Sie jetzt ochenstarik-server-2.sh als root aus."
) ;;
fr) UI_TEXT+=(
[input_interrupted]="Saisie interrompue" [invalid_number]="Saisissez un nombre de %s à %s"
[select_option]="Saisissez le numéro de loption : " [select_region]="Sélectionnez une région :"
[select_timezone]="Sélectionnez un fuseau horaire dans %s :"
[timezone_heading]="Sélectionnez le fuseau horaire du serveur (actuel : %s) :"
[other_timezone]="Autre fuseau horaire (sélection par région)" [timezone_set]="Fuseau horaire défini sur %s"
[terminal_heading]="Sélectionnez la langue des nouvelles sessions (actuelle : %s) :"
[terminal_keep]="Conserver la langue actuelle" [terminal_other]="Autre locale de la liste complète"
[terminal_prompt]="Numéro de langue [1 — conserver] : " [terminal_unchanged]="La langue du terminal na pas été modifiée"
[terminal_full_list]="Sélectionnez une locale dans la liste complète :"
[program_heading]="Sélectionnez les groupes de programmes à installer :"
[program_none]="Ne pas installer de programmes supplémentaires"
[program_example]="Saisissez les numéros séparés par des espaces, par exemple : 1 2 4 9."
[program_prompt]="Sélection [Entrée — tout installer] : " [program_skipped]="Installation des programmes supplémentaires ignorée"
[program_bad_number]="Numéro de groupe incorrect : %s" [program_out_of_range]="Numéro de groupe hors plage : %s"
[program_selected]="Groupe sélectionné : %s" [programs_installed]="Les programmes sélectionnés ont été installés"
[apt_update]="Mise à jour de la liste des paquets" [root_required]="Exécutez ce script en tant que root"
[swap_configuring]="Configuration dun fichier swap de %s" [setup_complete]="Configuration de la mémoire terminée"
[next_script]="Exécutez maintenant ochenstarik-server-2.sh en tant que root."
) ;;
pt) UI_TEXT+=(
[input_interrupted]="Entrada interrompida" [invalid_number]="Digite um número de %s a %s"
[select_option]="Digite o número da opção: " [select_region]="Selecione uma região:"
[select_timezone]="Selecione um fuso horário em %s:"
[timezone_heading]="Selecione o fuso horário do servidor (atual: %s):"
[other_timezone]="Outro fuso horário (selecionar por região)" [timezone_set]="Fuso horário definido como %s"
[terminal_heading]="Selecione o idioma das novas sessões de terminal (atual: %s):"
[terminal_keep]="Manter o idioma atual" [terminal_other]="Outra localidade da lista completa"
[terminal_prompt]="Número do idioma [1 — manter]: " [terminal_unchanged]="O idioma do terminal não foi alterado"
[terminal_full_list]="Selecione uma localidade na lista completa:"
[program_heading]="Selecione os grupos de programas para instalar:"
[program_none]="Não instalar programas adicionais"
[program_example]="Digite números separados por espaços, por exemplo: 1 2 4 9."
[program_prompt]="Seleção [Enter — instalar todos]: " [program_skipped]="Instalação de programas adicionais ignorada"
[program_bad_number]="Número de grupo inválido: %s" [program_out_of_range]="Número de grupo fora do intervalo: %s"
[program_selected]="Grupo selecionado: %s" [programs_installed]="Os programas selecionados foram instalados"
[apt_update]="Atualizando a lista de pacotes" [root_required]="Execute este script como root"
[swap_configuring]="Configurando arquivo swap de %s" [setup_complete]="Configuração de memória concluída"
[next_script]="Agora execute ochenstarik-server-2.sh como root."
) ;;
zh) UI_TEXT+=(
[input_interrupted]="输入已中断" [invalid_number]="请输入 %s 到 %s 之间的数字"
[select_option]="请输入选项编号:" [select_region]="请选择地区:" [select_timezone]="请选择 %s 的时区:"
[timezone_heading]="请选择服务器时区(当前:%s" [other_timezone]="其他时区(按地区选择)" [timezone_set]="时区已设置为 %s"
[terminal_heading]="请选择新终端会话的语言(当前:%s" [terminal_keep]="保留当前终端语言"
[terminal_other]="从完整列表选择其他区域设置" [terminal_prompt]="语言编号 [1 — 保留当前设置]"
[terminal_unchanged]="终端语言未更改" [terminal_full_list]="请从完整列表选择区域设置:"
[program_heading]="请选择要安装的程序组:" [program_none]="不安装其他程序"
[program_example]="请输入用空格分隔的编号例如1 2 4 9。" [program_prompt]="选择 [Enter — 全部安装]"
[program_skipped]="已跳过其他程序的安装" [program_bad_number]="无效的程序组编号:%s"
[program_out_of_range]="程序组编号超出范围:%s" [program_selected]="已选择程序组:%s"
[programs_installed]="已安装所选程序" [apt_update]="正在更新软件包列表" [root_required]="请以 root 身份运行此脚本"
[swap_configuring]="正在配置 %s 交换文件" [setup_complete]="服务器内存设置完成"
[next_script]="现在请以 root 身份运行 ochenstarik-server-2.sh。"
) ;;
ja) UI_TEXT+=(
[input_interrupted]="入力が中断されました" [invalid_number]="%s から %s の番号を入力してください"
[select_option]="オプション番号を入力してください: " [select_region]="地域を選択してください:"
[select_timezone]="%s のタイムゾーンを選択してください:"
[timezone_heading]="サーバーのタイムゾーンを選択してください(現在: %s:"
[other_timezone]="その他のタイムゾーン(地域から選択)" [timezone_set]="タイムゾーンを %s に設定しました"
[terminal_heading]="新しいターミナルセッションの言語を選択してください(現在: %s:"
[terminal_keep]="現在の言語を維持" [terminal_other]="完全なリストから別のロケールを選択"
[terminal_prompt]="言語番号 [1 — 変更しない]: " [terminal_unchanged]="ターミナルの言語は変更されませんでした"
[terminal_full_list]="完全なリストからロケールを選択してください:"
[program_heading]="インストールするプログラムグループを選択してください:"
[program_none]="追加プログラムをインストールしない"
[program_example]="番号を空白で区切って入力してください。例: 1 2 4 9。"
[program_prompt]="選択 [Enter — すべてインストール]: " [program_skipped]="追加プログラムのインストールをスキップしました"
[program_bad_number]="無効なグループ番号: %s" [program_out_of_range]="グループ番号が範囲外です: %s"
[program_selected]="選択したグループ: %s" [programs_installed]="選択したプログラムをインストールしました"
[apt_update]="パッケージリストを更新しています" [root_required]="このスクリプトを root で実行してください"
[swap_configuring]="%s のスワップファイルを設定しています" [setup_complete]="メモリ設定が完了しました"
[next_script]="次に ochenstarik-server-2.sh を root で実行してください。"
) ;;
ar) UI_TEXT+=(
[input_interrupted]="تم إيقاف الإدخال" [invalid_number]="أدخل رقماً من %s إلى %s"
[select_option]="أدخل رقم الخيار: " [select_region]="اختر المنطقة:" [select_timezone]="اختر منطقة زمنية في %s:"
[timezone_heading]="اختر المنطقة الزمنية للخادم (الحالية: %s):" [other_timezone]="منطقة زمنية أخرى (حسب المنطقة)"
[timezone_set]="تم ضبط المنطقة الزمنية على %s" [terminal_heading]="اختر لغة جلسات الطرفية الجديدة (الحالية: %s):"
[terminal_keep]="الاحتفاظ باللغة الحالية" [terminal_other]="إعداد محلي آخر من القائمة الكاملة"
[terminal_prompt]="رقم اللغة [1 — الاحتفاظ]: " [terminal_unchanged]="لم تتغير لغة الطرفية"
[terminal_full_list]="اختر إعداداً محلياً من القائمة الكاملة:"
[program_heading]="اختر مجموعات البرامج المراد تثبيتها:" [program_none]="عدم تثبيت برامج إضافية"
[program_example]="أدخل الأرقام مفصولة بمسافات، مثال: 1 2 4 9." [program_prompt]="الاختيار [Enter — تثبيت الكل]: "
[program_skipped]="تم تخطي تثبيت البرامج الإضافية" [program_bad_number]="رقم مجموعة غير صالح: %s"
[program_out_of_range]="رقم المجموعة خارج النطاق: %s" [program_selected]="المجموعة المحددة: %s"
[programs_installed]="تم تثبيت البرامج المحددة" [apt_update]="جارٍ تحديث قائمة الحزم"
[root_required]="شغّل هذا البرنامج النصي بصلاحية root" [swap_configuring]="جارٍ إعداد ملف swap بحجم %s"
[setup_complete]="اكتمل إعداد ذاكرة الخادم" [next_script]="شغّل الآن ochenstarik-server-2.sh بصلاحية root."
) ;;
hi) UI_TEXT+=(
[input_interrupted]="इनपुट बाधित हुआ" [invalid_number]="%s से %s तक की संख्या दर्ज करें"
[select_option]="विकल्प संख्या दर्ज करें: " [select_region]="क्षेत्र चुनें:" [select_timezone]="%s में समय क्षेत्र चुनें:"
[timezone_heading]="सर्वर का समय क्षेत्र चुनें (वर्तमान: %s):" [other_timezone]="अन्य समय क्षेत्र (क्षेत्र के अनुसार)"
[timezone_set]="समय क्षेत्र %s पर सेट किया गया" [terminal_heading]="नई टर्मिनल सत्रों की भाषा चुनें (वर्तमान: %s):"
[terminal_keep]="वर्तमान भाषा बनाए रखें" [terminal_other]="पूरी सूची से अन्य लोकेल"
[terminal_prompt]="भाषा संख्या [1 — वर्तमान रखें]: " [terminal_unchanged]="टर्मिनल भाषा नहीं बदली गई"
[terminal_full_list]="पूरी सूची से लोकेल चुनें:"
[program_heading]="इंस्टॉल करने के लिए प्रोग्राम समूह चुनें:" [program_none]="अतिरिक्त प्रोग्राम इंस्टॉल न करें"
[program_example]="खाली स्थान से अलग संख्याएँ दर्ज करें, उदाहरण: 1 2 4 9।" [program_prompt]="चयन [Enter — सभी इंस्टॉल करें]: "
[program_skipped]="अतिरिक्त प्रोग्राम की स्थापना छोड़ दी गई" [program_bad_number]="अमान्य समूह संख्या: %s"
[program_out_of_range]="समूह संख्या सीमा से बाहर है: %s" [program_selected]="चयनित समूह: %s"
[programs_installed]="चयनित प्रोग्राम इंस्टॉल हो गए" [apt_update]="पैकेज सूची अपडेट की जा रही है"
[root_required]="इस स्क्रिप्ट को root के रूप में चलाएँ" [swap_configuring]="%s swap फ़ाइल कॉन्फ़िगर की जा रही है"
[setup_complete]="सर्वर मेमोरी सेटअप पूरा हुआ" [next_script]="अब ochenstarik-server-2.sh को root के रूप में चलाएँ।"
) ;;
esac
}
msg() {
local key="$1"
shift
printf "${UI_TEXT[$key]}" "$@"
}
localize_program_catalog() {
case "$UI_LANG" in
ru) PROGRAM_GROUP_TITLES=(
"Терминал и повседневная работа" "Диагностика сети" "Мониторинг системы и дисков"
"Безопасность и обслуживание Ubuntu" "Разработка и автоматизация"
"Архивы, таблицы и локальные данные" "Документы, PDF и OCR"
"Изображения, аудио и видео" "Резервное копирование и синхронизация"
) ;;
es) PROGRAM_GROUP_TITLES=(
"Terminal y herramientas cotidianas" "Diagnóstico de red" "Supervisión del sistema y discos"
"Seguridad y mantenimiento de Ubuntu" "Desarrollo y automatización"
"Archivos, tablas y datos locales" "Documentos, PDF y OCR"
"Imágenes, audio y vídeo" "Copias de seguridad y sincronización"
) ;;
de) PROGRAM_GROUP_TITLES=(
"Terminal und Alltagswerkzeuge" "Netzwerkdiagnose" "System- und Datenträgerüberwachung"
"Ubuntu-Sicherheit und Wartung" "Entwicklung und Automatisierung"
"Archive, Tabellen und lokale Daten" "Dokumente, PDF und OCR"
"Bilder, Audio und Video" "Sicherung und Synchronisierung"
) ;;
fr) PROGRAM_GROUP_TITLES=(
"Terminal et outils quotidiens" "Diagnostic réseau" "Surveillance du système et des disques"
"Sécurité et maintenance dUbuntu" "Développement et automatisation"
"Archives, tableaux et données locales" "Documents, PDF et OCR"
"Images, audio et vidéo" "Sauvegarde et synchronisation"
) ;;
pt) PROGRAM_GROUP_TITLES=(
"Terminal e ferramentas diárias" "Diagnóstico de rede" "Monitoramento do sistema e discos"
"Segurança e manutenção do Ubuntu" "Desenvolvimento e automação"
"Arquivos, tabelas e dados locais" "Documentos, PDF e OCR"
"Imagens, áudio e vídeo" "Backup e sincronização"
) ;;
zh) PROGRAM_GROUP_TITLES=(
"终端与日常工具" "网络诊断" "系统与磁盘监控" "Ubuntu 安全与维护" "开发与自动化"
"压缩包、表格与本地数据" "文档、PDF 与 OCR" "图像、音频与视频" "备份与同步"
) ;;
ja) PROGRAM_GROUP_TITLES=(
"ターミナルと日常ツール" "ネットワーク診断" "システムとディスクの監視"
"Ubuntu のセキュリティと保守" "開発と自動化" "アーカイブ、表、ローカルデータ"
"文書、PDF、OCR" "画像、音声、動画" "バックアップと同期"
) ;;
ar) PROGRAM_GROUP_TITLES=(
"الطرفية والأدوات اليومية" "تشخيص الشبكة" "مراقبة النظام والأقراص"
"أمان Ubuntu وصيانته" "التطوير والأتمتة" "الأرشيفات والجداول والبيانات المحلية"
"المستندات وPDF وOCR" "الصور والصوت والفيديو" "النسخ الاحتياطي والمزامنة"
) ;;
hi) PROGRAM_GROUP_TITLES=(
"टर्मिनल और दैनिक उपकरण" "नेटवर्क निदान" "सिस्टम और डिस्क निगरानी"
"Ubuntu सुरक्षा और रखरखाव" "विकास और स्वचालन" "अभिलेख, तालिकाएँ और स्थानीय डेटा"
"दस्तावेज़, PDF और OCR" "चित्र, ऑडियो और वीडियो" "बैकअप और सिंक्रोनाइज़ेशन"
) ;;
esac
}
choose_ui_language() {
local answer
printf '\nSelect the installer language / Выберите язык установщика:\n'
printf '%s\n' \
' 1) English' ' 2) Русский' ' 3) Español' ' 4) Deutsch' \
' 5) Français' ' 6) Português' ' 7) 中文' ' 8) 日本語' \
' 9) العربية' ' 10) हिन्दी'
while :; do
read -rp 'Language [1 — English]: ' answer || { printf '[x] Input was interrupted\n' >&2; exit 1; }
answer="${answer:-1}"
case "$answer" in
1) UI_LANG=en ;; 2) UI_LANG=ru ;; 3) UI_LANG=es ;; 4) UI_LANG=de ;;
5) UI_LANG=fr ;; 6) UI_LANG=pt ;; 7) UI_LANG=zh ;; 8) UI_LANG=ja ;;
9) UI_LANG=ar ;; 10) UI_LANG=hi ;;
*) printf '[!] Enter a number from 1 to 10.\n' >&2; continue ;;
esac
break
done
load_ui_text
localize_program_catalog
}
# English is active until the user answers the first prompt. This also keeps
# sourced helper functions usable by the automated tests.
load_ui_text
log() { printf '[+] %s\n' "$*"; }
warn() { printf '[!] %s\n' "$*" >&2; }
@ -64,14 +373,14 @@ ensure_package_tools() {
local command_name
for command_name in apt-get apt-cache dpkg-query; do
command -v "$command_name" >/dev/null 2>&1 \
|| die "Required command not found: $command_name"
|| die "$(msg required_missing "$command_name")"
done
}
ensure_apt_updated() {
[[ "$APT_UPDATED" == no ]] || return 0
ensure_package_tools
log "Обновление списка пакетов"
log "$(msg apt_update)"
apt-get update
APT_UPDATED=yes
}
@ -131,7 +440,7 @@ choose_terminal_language() {
local answer selected_locale selected_language primary_language package_name index
local current_locale="${LANG:-не задана}"
local -a titles=(
"Не менять язык терминала"
"$(msg terminal_keep)"
"Русский — ru_RU.UTF-8"
"English (US) — en_US.UTF-8"
"Deutsch — de_DE.UTF-8"
@ -145,7 +454,7 @@ choose_terminal_language() {
"简体中文 — zh_CN.UTF-8"
"日本語 — ja_JP.UTF-8"
"한국어 — ko_KR.UTF-8"
"Другая локаль из полного списка"
"$(msg terminal_other)"
)
local -a locales=(
"" ru_RU.UTF-8 en_US.UTF-8 de_DE.UTF-8 fr_FR.UTF-8 es_ES.UTF-8
@ -166,17 +475,17 @@ choose_terminal_language() {
newly_installed=() extra_packages=()
while :; do
printf '\nВыберите язык новых терминальных сессий (сейчас: %s):\n' "$current_locale"
printf '\n%s\n' "$(msg terminal_heading "$current_locale")"
for index in "${!titles[@]}"; do
printf ' %d) %s\n' "$((index + 1))" "${titles[index]}"
done
read -rp 'Номер языка [1 — не менять]: ' answer || die "Ввод прерван"
read -rp "$(msg terminal_prompt)" answer || die "$(msg input_interrupted)"
answer="${answer:-1}"
[[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#titles[@]})) && break
warn "Введите номер от 1 до ${#titles[@]}"
warn "$(msg invalid_number 1 "${#titles[@]}")"
done
((answer == 1)) && { log "Язык терминала не изменён"; return 0; }
((answer == 1)) && { log "$(msg terminal_unchanged)"; return 0; }
ensure_apt_updated
save_original_locale
@ -184,7 +493,7 @@ choose_terminal_language() {
package_is_installed locales || newly_installed+=(locales)
apt-get install -y locales
mapfile -t supported_locales < <(awk '{print $1}' /usr/share/i18n/SUPPORTED | sort -u)
choose_numbered_option "Выберите локаль из полного списка:" selected_locale \
choose_numbered_option "$(msg terminal_full_list)" selected_locale \
"${supported_locales[@]}"
primary_language="${selected_locale%%_*}"
selected_language="${selected_locale%%.*}:${primary_language}"
@ -202,7 +511,7 @@ choose_terminal_language() {
available_packages+=("$package_name")
package_is_installed "$package_name" || newly_installed+=("$package_name")
else
warn "Языковой пакет недоступен и будет пропущен: $package_name"
warn "$(msg locale_package_missing "$package_name")"
fi
done
@ -213,7 +522,7 @@ choose_terminal_language() {
LC_MESSAGES="$selected_locale"
record_new_packages "$LOCALE_PACKAGES" "${newly_installed[@]}"
log "Выбрана локаль ${selected_locale}. Она применится при следующем входе"
log "$(msg locale_selected "$selected_locale")"
}
choose_and_install_programs() {
@ -221,14 +530,14 @@ choose_and_install_programs() {
local -a selected_groups=() group_packages=() requested=() available=() newly_installed=()
local -A selected=() seen=()
printf '\nВыберите наборы программ для установки:\n'
printf '\n%s\n' "$(msg program_heading)"
for group_index in "${!PROGRAM_GROUP_TITLES[@]}"; do
printf ' %d) %s\n %s\n' "$((group_index + 1))" \
"${PROGRAM_GROUP_TITLES[group_index]}" "${PROGRAM_GROUP_DESCRIPTIONS[group_index]}"
done
printf ' 0) Не устанавливать дополнительные программы\n'
printf '\nВведите номера через пробел, например: 1 2 4 9.\n'
read -rp 'Выбор [Enter — установить все наборы]: ' answer || die "Ввод прерван"
printf ' 0) %s\n' "$(msg program_none)"
printf '\n%s\n' "$(msg program_example)"
read -rp "$(msg program_prompt)" answer || die "$(msg input_interrupted)"
answer="${answer//,/ }"
if [[ -z "${answer//[[:space:]]/}" ]]; then
@ -236,14 +545,14 @@ choose_and_install_programs() {
selected_groups+=("$group_index")
done
elif [[ "$answer" =~ ^[[:space:]]*0[[:space:]]*$ ]]; then
log "Установка дополнительных программ пропущена"
log "$(msg program_skipped)"
return 0
else
for token in $answer; do
[[ "$token" =~ ^[0-9]+$ ]] \
|| die "Некорректный номер набора: $token"
|| die "$(msg program_bad_number "$token")"
((token >= 1 && token <= ${#PROGRAM_GROUP_TITLES[@]})) \
|| die "Номер набора вне диапазона: $token"
|| die "$(msg program_out_of_range "$token")"
group_index="$((token - 1))"
[[ -n "${selected[$group_index]:-}" ]] || selected_groups+=("$group_index")
selected[$group_index]=yes
@ -252,7 +561,7 @@ choose_and_install_programs() {
ensure_apt_updated
for group_index in "${selected_groups[@]}"; do
log "Выбран набор: ${PROGRAM_GROUP_TITLES[group_index]}"
log "$(msg program_selected "${PROGRAM_GROUP_TITLES[group_index]}")"
read -r -a group_packages <<< "${PROGRAM_GROUP_PACKAGES[group_index]}"
for package_name in "${group_packages[@]}"; do
[[ -n "${seen[$package_name]:-}" ]] && continue
@ -266,15 +575,15 @@ choose_and_install_programs() {
available+=("$package_name")
package_is_installed "$package_name" || newly_installed+=("$package_name")
else
warn "Пакет отсутствует в подключённых репозиториях и пропущен: $package_name"
warn "$(msg package_missing "$package_name")"
fi
done
((${#available[@]} > 0)) || die "Ни один выбранный пакет не найден"
((${#available[@]} > 0)) || die "$(msg no_packages)"
export DEBIAN_FRONTEND=noninteractive
apt-get install -y "${available[@]}"
record_new_packages "$PROGRAM_PACKAGES" "${newly_installed[@]}"
log "Выбранные программы установлены"
log "$(msg programs_installed)"
}
choose_numbered_option() {
@ -282,18 +591,18 @@ choose_numbered_option() {
shift 2
local -a options=("$@")
((${#options[@]} > 0)) || die "No options are available for: ${prompt}"
((${#options[@]} > 0)) || die "$(msg no_options "$prompt")"
while :; do
printf '\n%s\n' "$prompt"
for index in "${!options[@]}"; do
printf ' %d) %s\n' "$((index + 1))" "${options[index]}"
done
read -rp "Enter the option number: " answer || die "Input was interrupted"
read -rp "$(msg select_option)" answer || die "$(msg input_interrupted)"
if [[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#options[@]})); then
printf -v "$result_variable" '%s' "${options[answer - 1]}"
return 0
fi
warn "Enter a number from 1 to ${#options[@]}"
warn "$(msg invalid_number 1 "${#options[@]}")"
done
}
@ -302,15 +611,15 @@ choose_timezone_from_full_list() {
local -a regions region_timezones
mapfile -t regions < <(timedatectl list-timezones | awk -F/ 'NF > 1 && !seen[$1]++ { print $1 }')
choose_numbered_option "Select a region:" region "${regions[@]}"
choose_numbered_option "$(msg select_region)" region "${regions[@]}"
mapfile -t region_timezones < <(timedatectl list-timezones | awk -F/ -v selected_region="$region" '$1 == selected_region')
choose_numbered_option "Select a timezone in ${region}:" selected_timezone "${region_timezones[@]}"
choose_numbered_option "$(msg select_timezone "$region")" selected_timezone "${region_timezones[@]}"
printf -v "$result_variable" '%s' "$selected_timezone"
}
choose_timezone() {
local current_timezone timezone candidate
local full_list_option="Other timezone (select by region)"
local full_list_option="$(msg other_timezone)"
local -a timezone_options=()
current_timezone="$(timedatectl show --property=Timezone --value 2>/dev/null || true)"
@ -337,26 +646,27 @@ choose_timezone() {
done
timezone_options+=("$full_list_option")
choose_numbered_option "Select the server timezone (current: ${current_timezone}):" timezone "${timezone_options[@]}"
choose_numbered_option "$(msg timezone_heading "$current_timezone")" timezone "${timezone_options[@]}"
if [[ "$timezone" == "$full_list_option" ]]; then
choose_timezone_from_full_list timezone
fi
timedatectl list-timezones | grep -Fqx -- "$timezone" || die "Unknown timezone: $timezone"
timedatectl list-timezones | grep -Fqx -- "$timezone" || die "$(msg unknown_timezone "$timezone")"
timedatectl set-timezone "$timezone"
log "Timezone set to ${timezone}"
log "$(msg timezone_set "$timezone")"
}
[[ "$EUID" -eq 0 ]] || die "Run this script as root"
choose_ui_language
for command_name in awk blkid fallocate grep mkswap swapon sysctl timedatectl; do
command -v "$command_name" >/dev/null 2>&1 || die "Required command not found: $command_name"
command -v "$command_name" >/dev/null 2>&1 || die "$(msg required_missing "$command_name")"
done
choose_timezone
choose_terminal_language
choose_and_install_programs
log "Configuring ${SWAPSIZE} swap file"
log "$(msg swap_configuring "$SWAPSIZE")"
if [[ ! -e "$SWAPFILE" ]]; then
fallocate -l "$SWAPSIZE" "$SWAPFILE"
chmod 600 "$SWAPFILE"
@ -385,7 +695,7 @@ EOF
chmod 644 "$SYSCTL_FILE"
sysctl -p "$SYSCTL_FILE" >/dev/null
log "Server memory setup is complete"
log "$(msg setup_complete)"
printf 'Timezone: %s\n' "$(timedatectl show --property=Timezone --value)"
swapon --show
printf '\nNow run ochenstarik-server-2.sh as root.\n'
printf '\n%s\n' "$(msg next_script)"

View file

@ -32,6 +32,14 @@ contains_package() {
return 1
}
declare -a EXPECTED_UI_LANGUAGES=(en ru es de fr pt zh ja ar hi)
for index in "${!EXPECTED_UI_LANGUAGES[@]}"; do
choose_ui_language <<< "$((index + 1))" >/dev/null
[[ "$UI_LANG" == "${EXPECTED_UI_LANGUAGES[index]}" ]]
done
choose_ui_language <<< '' >/dev/null
[[ "$UI_LANG" == en ]]
choose_and_install_programs <<< '1 3' >/dev/null
contains_package git
contains_package iotop