diff --git a/lib/core/extension/ssh_client.dart b/lib/core/extension/ssh_client.dart index 43e16ef8..09bc2bff 100644 --- a/lib/core/extension/ssh_client.dart +++ b/lib/core/extension/ssh_client.dart @@ -7,13 +7,9 @@ import 'package:flutter/widgets.dart'; import 'package:server_box/data/helper/ssh_decoder.dart'; import 'package:server_box/data/model/server/system.dart'; -import 'package:server_box/data/res/misc.dart'; - typedef OnStdout = void Function(String data, SSHSession session); typedef OnStdin = void Function(SSHSession session); -typedef PwdRequestFunc = Future Function(String? user); - extension SSHClientX on SSHClient { /// Create a persistent PowerShell session for Windows commands Future<(SSHSession, String)> execPowerShell( @@ -47,7 +43,7 @@ extension SSHClientX on SSHClient { session.stderr.listen( (e) { onStderr?.call(e.string, session); - if (stderr) result.add(e); + // Don't add stderr to result, only stdout }, onDone: stderrDone.complete, onError: stderrDone.completeError, @@ -112,6 +108,19 @@ extension SSHClientX on SSHClient { return (session, result.takeBytes().string); } + /// Executes a command with password error detection. + /// + /// This method is used for executing commands where password has already been + /// handled beforehand (e.g., via base64 pipe in container commands). + /// It captures stderr via [onStderr] callback to detect sudo password errors + /// (e.g., "Sorry, try again.", "incorrect password attempt", or + /// "a password is required"), while excluding stderr from the returned + /// output via [stderr: false]. + /// + /// Returns exitCode: + /// - 0: success + /// - 1: general error + /// - 2: sudo password error Future<(int?, String)> execWithPwd( String script, { String? entry, @@ -120,7 +129,8 @@ extension SSHClientX on SSHClient { OnStdout? onStderr, required String id, }) async { - var isRequestingPwd = false; + var hasPasswordError = false; + final (session, output) = await exec( (sess) { sess.stdin.add('$script\n'.uint8List); @@ -128,25 +138,20 @@ extension SSHClientX on SSHClient { }, onStderr: (data, session) async { onStderr?.call(data, session); - if (isRequestingPwd) return; - - if (data.contains('[sudo] password for ')) { - isRequestingPwd = true; - final user = Miscs.pwdRequestWithUserReg.firstMatch(data)?.group(1); - final ctx = context ?? WidgetsBinding.instance.focusManager.primaryFocus?.context; - if (ctx == null) return; - final pwd = ctx.mounted ? await ctx.showPwdDialog(title: user, id: id) : null; - if (pwd == null || pwd.isEmpty) { - session.stdin.close(); - } else { - session.stdin.add('$pwd\n'.uint8List); - } - isRequestingPwd = false; + if (data.contains('Sorry, try again.') || + data.contains('incorrect password attempt') || + data.contains('a password is required')) { + hasPasswordError = true; } }, onStdout: onStdout, entry: entry, + stderr: false, ); + + if (hasPasswordError) { + return (2, output); + } return (session.exitCode, output); } diff --git a/lib/data/model/app/error.dart b/lib/data/model/app/error.dart index d3d64ca8..ad6ecf75 100644 --- a/lib/data/model/app/error.dart +++ b/lib/data/model/app/error.dart @@ -27,6 +27,8 @@ enum ContainerErrType { parseImages, parseStats, podmanDetected, + sudoPasswordRequired, + sudoPasswordIncorrect, } class ContainerErr extends Err { diff --git a/lib/data/provider/container.dart b/lib/data/provider/container.dart index afaaaeda..0a1a9ecf 100644 --- a/lib/data/provider/container.dart +++ b/lib/data/provider/container.dart @@ -37,6 +37,7 @@ abstract class ContainerState with _$ContainerState { @riverpod class ContainerNotifier extends _$ContainerNotifier { var sudoCompleter = Completer(); + String? _cachedPassword; @override ContainerState build(SSHClient? client, String userName, String hostId, BuildContext context) { @@ -49,6 +50,18 @@ class ContainerNotifier extends _$ContainerNotifier { return initialState; } + Future _getSudoPassword() async { + if (_cachedPassword != null) return _cachedPassword; + + if (!context.mounted) return null; + final pwd = await context.showPwdDialog(title: userName, id: hostId); + + if (pwd != null && pwd.isNotEmpty) { + _cachedPassword = pwd; + } + return pwd; + } + Future setType(ContainerType type) async { state = state.copyWith(type: type, error: null, runLog: null, items: null, images: null, version: null); Stores.container.setType(type, hostId); @@ -84,14 +97,39 @@ class ContainerNotifier extends _$ContainerNotifier { state = state.copyWith(isBusy: false); return; } + + String? password; + if (sudo) { + password = await _getSudoPassword(); + if (password == null) { + state = state.copyWith( + isBusy: false, + error: ContainerErr( + type: ContainerErrType.sudoPasswordRequired, + message: l10n.containerSudoPasswordRequired, + ), + ); + return; + } + } + final includeStats = Stores.setting.containerParseStat.fetch(); - final cmd = _wrap(ContainerCmdType.execAll(state.type, sudo: sudo, includeStats: includeStats)); + final cmd = _wrap(ContainerCmdType.execAll(state.type, sudo: sudo, includeStats: includeStats, password: password)); int? code; String raw = ''; - final errs = []; + var isPodmanEmulation = false; if (client != null) { - (code, raw) = await client!.execWithPwd(cmd, context: context, id: hostId); + (code, raw) = await client!.execWithPwd( + cmd, + context: context, + id: hostId, + onStderr: (data, _) { + if (data.contains(_podmanEmulationMsg)) { + isPodmanEmulation = true; + } + }, + ); } else { state = state.copyWith( isBusy: false, @@ -106,13 +144,23 @@ class ContainerNotifier extends _$ContainerNotifier { if (!context.mounted) return; /// Code 127 means command not found - if (code == 127 || raw.contains(_dockerNotFound) || errs.join().contains(_dockerNotFound)) { + if (code == 127 || raw.contains(_dockerNotFound)) { state = state.copyWith(error: ContainerErr(type: ContainerErrType.notInstalled)); return; } + /// Sudo password error (exitCode = 2) + if (code == 2) { + _cachedPassword = null; + state = state.copyWith(error: ContainerErr( + type: ContainerErrType.sudoPasswordIncorrect, + message: l10n.containerSudoPasswordIncorrect, + )); + return; + } + /// Pre-parse Podman detection - if (raw.contains(_podmanEmulationMsg)) { + if (isPodmanEmulation) { state = state.copyWith( error: ContainerErr( type: ContainerErrType.podmanDetected, @@ -122,15 +170,8 @@ class ContainerNotifier extends _$ContainerNotifier { return; } - /// Filter out sudo password prompt from output - if (errs.any((e) => e.contains('[sudo] password'))) { - raw = raw.split('\n').where((line) => !line.contains('[sudo] password')).join('\n'); - } - /// Detect Podman not installed when using Podman mode - if (state.type == ContainerType.podman && - (errs.any((e) => e.contains('podman: not found')) || - raw.contains('podman: not found'))) { + if (state.type == ContainerType.podman && raw.contains('podman: not found')) { state = state.copyWith(error: ContainerErr(type: ContainerErrType.notInstalled)); return; } @@ -276,21 +317,43 @@ class ContainerNotifier extends _$ContainerNotifier { ContainerType.podman => 'podman $cmd', }; + final needSudo = await sudoCompleter.future; + String? password; + if (needSudo) { + password = await _getSudoPassword(); + if (password == null) { + return ContainerErr( + type: ContainerErrType.sudoPasswordRequired, + message: l10n.containerSudoPasswordRequired, + ); + } + } + + if (needSudo) { + cmd = _buildSudoCmd(cmd, password!); + } + state = state.copyWith(runLog: ''); - final errs = []; - final (code, _) = await client?.execWithPwd( - _wrap((await sudoCompleter.future) ? 'sudo -S $cmd' : cmd), + final (code, _) = await client!.execWithPwd( + _wrap(cmd), context: context, onStdout: (data, _) { state = state.copyWith(runLog: '${state.runLog}$data'); }, - onStderr: (data, _) => errs.add(data), id: hostId, - ) ?? (null, null); + ); + state = state.copyWith(runLog: null); + if (code == 2) { + _cachedPassword = null; + return ContainerErr( + type: ContainerErrType.sudoPasswordIncorrect, + message: l10n.containerSudoPasswordIncorrect, + ); + } if (code != 0) { - return ContainerErr(type: ContainerErrType.unknown, message: errs.join('\n').trim()); + return ContainerErr(type: ContainerErrType.unknown, message: 'Command execution failed'); } if (autoRefresh) await refresh(); return null; @@ -310,6 +373,11 @@ class ContainerNotifier extends _$ContainerNotifier { const _jsonFmt = '--format "{{json .}}"'; +String _buildSudoCmd(String baseCmd, String password) { + final pwdBase64 = base64Encode(utf8.encode(password)); + return 'echo "$pwdBase64" | base64 -d | sudo -S $baseCmd'; +} + enum ContainerCmdType { version, ps, @@ -319,30 +387,41 @@ enum ContainerCmdType { // and don't require splitting output with ScriptConstants.separator ; - String exec(ContainerType type, {bool sudo = false, bool includeStats = false}) { - final prefix = sudo ? 'sudo -S ${type.name}' : type.name; - return switch (this) { - ContainerCmdType.version => '$prefix version $_jsonFmt', + String exec(ContainerType type, {bool includeStats = false}) { + final baseCmd = switch (this) { + ContainerCmdType.version => '${type.name} version $_jsonFmt', ContainerCmdType.ps => switch (type) { /// TODO: Rollback to json format when performance recovers. /// Use [_jsonFmt] in Docker will cause the operation to slow down. ContainerType.docker => - '$prefix ps -a --format "table {{printf \\"' + '${type.name} ps -a --format "table {{printf \\"' '%-15.15s ' '%-30.30s ' '${"%-50.50s " * 2}\\"' ' .ID .Status .Names .Image}}"', - ContainerType.podman => '$prefix ps -a $_jsonFmt', + ContainerType.podman => '${type.name} ps -a $_jsonFmt', }, - ContainerCmdType.stats => includeStats ? '$prefix stats --no-stream $_jsonFmt' : 'echo PASS', - ContainerCmdType.images => '$prefix image ls $_jsonFmt', + ContainerCmdType.stats => includeStats ? '${type.name} stats --no-stream $_jsonFmt' : 'echo PASS', + ContainerCmdType.images => '${type.name} image ls $_jsonFmt', }; + + return baseCmd; } - static String execAll(ContainerType type, {bool sudo = false, bool includeStats = false}) { - return ContainerCmdType.values - .map((e) => e.exec(type, sudo: sudo, includeStats: includeStats)) + static String execAll(ContainerType type, {bool sudo = false, bool includeStats = false, String? password}) { + final commands = ContainerCmdType.values + .map((e) => e.exec(type, includeStats: includeStats)) .join('\necho ${ScriptConstants.separator}\n'); + + final wrappedCommands = 'sh -c \'${commands.replaceAll("'", "'\\''")}\''; + + if (sudo && password != null) { + return _buildSudoCmd(wrappedCommands, password); + } + if (sudo) { + return 'sudo -S $wrappedCommands'; + } + return wrappedCommands; } /// Find out the required segment from [segments] diff --git a/lib/data/res/github_id.dart b/lib/data/res/github_id.dart index f3e4fbc0..817c49f5 100644 --- a/lib/data/res/github_id.dart +++ b/lib/data/res/github_id.dart @@ -138,7 +138,9 @@ abstract final class GithubIds { 'itmagpro', 'atikattar1104', 'coldboy404', - 'puskyer' + 'puskyer', + 'wanababy', + 'toarujs' }; } diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 50125318..7ddfc7c6 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -467,6 +467,18 @@ abstract class AppLocalizations { /// **'For example: In the app, the user is set to aaa, but Docker is installed under the root user. In this case, you need to enable this option.'** String get containerTrySudoTip; + /// No description provided for @containerSudoPasswordRequired. + /// + /// In en, this message translates to: + /// **'Sudo password is required to access Docker. Please enter your password.'** + String get containerSudoPasswordRequired; + + /// No description provided for @containerSudoPasswordIncorrect. + /// + /// In en, this message translates to: + /// **'Sudo password is incorrect or not allowed. Please try again.'** + String get containerSudoPasswordIncorrect; + /// No description provided for @convert. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 1c4d96f2..d63ffcdf 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -203,6 +203,14 @@ class AppLocalizationsDe extends AppLocalizations { String get containerTrySudoTip => 'Zum Beispiel: In der App ist der Benutzer auf aaa eingestellt, aber Docker ist unter dem Root-Benutzer installiert. In diesem Fall müssen Sie diese Option aktivieren'; + @override + String get containerSudoPasswordRequired => + 'Ein sudo-Passwort ist erforderlich, um auf Docker zuzugreifen. Bitte geben Sie Ihr Passwort ein.'; + + @override + String get containerSudoPasswordIncorrect => + 'Das sudo-Passwort ist falsch oder nicht erlaubt. Bitte versuchen Sie es erneut.'; + @override String get convert => 'Konvertieren'; diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index f80c7b61..72d7badf 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -202,6 +202,14 @@ class AppLocalizationsEn extends AppLocalizations { String get containerTrySudoTip => 'For example: In the app, the user is set to aaa, but Docker is installed under the root user. In this case, you need to enable this option.'; + @override + String get containerSudoPasswordRequired => + 'Sudo password is required to access Docker. Please enter your password.'; + + @override + String get containerSudoPasswordIncorrect => + 'Sudo password is incorrect or not allowed. Please try again.'; + @override String get convert => 'Convert'; diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index 858a19fa..b0a2eea4 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -203,6 +203,14 @@ class AppLocalizationsEs extends AppLocalizations { String get containerTrySudoTip => 'Por ejemplo: si configuras el usuario dentro de la app como aaa, pero Docker está instalado bajo el usuario root, entonces necesitarás habilitar esta opción'; + @override + String get containerSudoPasswordRequired => + 'Se requiere contraseña de sudo para acceder a Docker. Por favor ingrese su contraseña.'; + + @override + String get containerSudoPasswordIncorrect => + 'La contraseña de sudo es incorrecta o no está permitida. Por favor intente de nuevo.'; + @override String get convert => 'Convertir'; diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index 56b64712..65007a62 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -203,6 +203,14 @@ class AppLocalizationsFr extends AppLocalizations { String get containerTrySudoTip => 'Par exemple : Dans l\'application, l\'utilisateur est défini comme aaa, mais Docker est installé sous l\'utilisateur root. Dans ce cas, vous devez activer cette option.'; + @override + String get containerSudoPasswordRequired => + 'Un mot de passe sudo est requis pour accéder à Docker. Veuillez entrer votre mot de passe.'; + + @override + String get containerSudoPasswordIncorrect => + 'Le mot de passe sudo est incorrect ou non autorisé. Veuillez réessayer.'; + @override String get convert => 'Convertir'; diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index a89612f1..d6709bf2 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -201,6 +201,14 @@ class AppLocalizationsId extends AppLocalizations { String get containerTrySudoTip => 'Contohnya: Di dalam aplikasi, pengguna diatur sebagai aaa, tetapi Docker diinstal di bawah pengguna root. Dalam kasus ini, Anda perlu mengaktifkan opsi ini.'; + @override + String get containerSudoPasswordRequired => + 'Kata sandi sudo diperlukan untuk mengakses Docker. Silakan masukkan kata sandi Anda.'; + + @override + String get containerSudoPasswordIncorrect => + 'Kata sandi sudo salah atau tidak diizinkan. Silakan coba lagi.'; + @override String get convert => 'Mengubah'; diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index 1c85a9a9..df6d4124 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -194,6 +194,14 @@ class AppLocalizationsJa extends AppLocalizations { String get containerTrySudoTip => '例:アプリ内でユーザーをaaaに設定しているが、Dockerがrootユーザーでインストールされている場合、このオプションを有効にする必要があります'; + @override + String get containerSudoPasswordRequired => + 'Dockerにアクセスするにはsudoパスワードが必要です。パスワードを入力してください。'; + + @override + String get containerSudoPasswordIncorrect => + 'sudoパスワードが正しくないか、許可されていません。再試行してください。'; + @override String get convert => '変換'; diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index 0fa70a98..69496554 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -202,6 +202,14 @@ class AppLocalizationsNl extends AppLocalizations { String get containerTrySudoTip => 'Bijvoorbeeld: in de app is de gebruiker ingesteld op aaa, maar Docker is geïnstalleerd onder de rootgebruiker. In dit geval moet u deze optie inschakelen.'; + @override + String get containerSudoPasswordRequired => + 'Een sudo-wachtwoord is vereist om toegang te krijgen tot Docker. Voer uw wachtwoord in.'; + + @override + String get containerSudoPasswordIncorrect => + 'Het sudo-wachtwoord is onjuist of niet toegestaan. Probeer het opnieuw.'; + @override String get convert => 'Converteren'; diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index 97e43e35..b752c6ae 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -201,6 +201,14 @@ class AppLocalizationsPt extends AppLocalizations { String get containerTrySudoTip => 'Por exemplo: se o usuário for definido como aaa dentro do app, mas o Docker estiver instalado sob o usuário root, esta opção precisará ser ativada'; + @override + String get containerSudoPasswordRequired => + 'É necessária uma senha sudo para acessar o Docker. Por favor, insira sua senha.'; + + @override + String get containerSudoPasswordIncorrect => + 'A senha sudo está incorreta ou não é permitida. Por favor, tente novamente.'; + @override String get convert => 'Converter'; diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index 04d1c5f8..719755d3 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -202,6 +202,14 @@ class AppLocalizationsRu extends AppLocalizations { String get containerTrySudoTip => 'Например: если пользователь в приложении установлен как aaa, но Docker установлен под пользователем root, тогда нужно включить эту опцию'; + @override + String get containerSudoPasswordRequired => + 'Для доступа к Docker требуется пароль sudo. Пожалуйста, введите ваш пароль.'; + + @override + String get containerSudoPasswordIncorrect => + 'Пароль sudo неверен или не разрешён. Пожалуйста, попробуйте снова.'; + @override String get convert => 'Конвертировать'; diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index c03da488..eaaccdcc 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -201,6 +201,14 @@ class AppLocalizationsTr extends AppLocalizations { String get containerTrySudoTip => 'Örneğin: Uygulamada kullanıcı aaa olarak ayarlanmış, ancak Docker root kullanıcısı altında kurulmuş. Bu durumda bu seçeneği etkinleştirmeniz gerekir.'; + @override + String get containerSudoPasswordRequired => + 'Docker\'e erişmek için sudo şifresi gereklidir. Lütfen şifrenizi girin.'; + + @override + String get containerSudoPasswordIncorrect => + 'Sudo şifresi yanlış veya izin verilmiyor. Lütfen tekrar deneyin.'; + @override String get convert => 'Dönüştür'; diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index 85047a15..15f0f2bc 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -202,6 +202,14 @@ class AppLocalizationsUk extends AppLocalizations { String get containerTrySudoTip => 'Наприклад: У застосунку користувач це aaa, але Docker встановлений під користувачем root. У цьому випадку вам потрібно активувати цю опцію.'; + @override + String get containerSudoPasswordRequired => + 'Для доступу до Docker потрібен пароль sudo. Будь ласка, введіть ваш пароль.'; + + @override + String get containerSudoPasswordIncorrect => + 'Пароль sudo неправильний або не дозволений. Будь ласка, спробуйте ще раз.'; + @override String get convert => 'Конвертувати'; diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index e3805d2a..5faba05b 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -192,6 +192,12 @@ class AppLocalizationsZh extends AppLocalizations { String get containerTrySudoTip => '例如:在应用内将用户设置为 aaa,但是 Docker 安装在root用户下,这时就需要启用此选项'; + @override + String get containerSudoPasswordRequired => '需要 sudo 密码才能访问 Docker。请输入您的密码。'; + + @override + String get containerSudoPasswordIncorrect => 'sudo 密码错误或无权限。请重试。'; + @override String get convert => '转换'; @@ -1182,6 +1188,12 @@ class AppLocalizationsZhTw extends AppLocalizationsZh { String get containerTrySudoTip => '例如:App 內設定使用者為 aaa,但是 Docker 安裝在 root 使用者,這時就需要開啟此選項'; + @override + String get containerSudoPasswordRequired => '需要 sudo 密碼才能存取 Docker。請輸入您的密碼。'; + + @override + String get containerSudoPasswordIncorrect => 'sudo 密碼錯誤或無權限。請重試。'; + @override String get convert => '轉換'; diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index ac45c9b3..92941f1a 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -58,6 +58,8 @@ "connectionStatsDesc": "Server-Verbindungserfolgsrate und Verlauf anzeigen", "container": "Container", "containerTrySudoTip": "Zum Beispiel: In der App ist der Benutzer auf aaa eingestellt, aber Docker ist unter dem Root-Benutzer installiert. In diesem Fall müssen Sie diese Option aktivieren", + "containerSudoPasswordRequired": "Ein sudo-Passwort ist erforderlich, um auf Docker zuzugreifen. Bitte geben Sie Ihr Passwort ein.", + "containerSudoPasswordIncorrect": "Das sudo-Passwort ist falsch oder nicht erlaubt. Bitte versuchen Sie es erneut.", "convert": "Konvertieren", "copyPath": "Pfad kopieren", "cpuViewAsProgressTip": "Zeigen Sie die Auslastung jedes CPUs in einem Fortschrittsbalken-Stil an (alter Stil)", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0a40e91b..2e6c5e79 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -47,7 +47,7 @@ "clearServerStatsTitle": "Clear {serverName} Statistics", "clearThisServerStats": "Clear This Server Statistics", "compactDatabase": "Compact Database", - "compactDatabaseContent": "Database size: {size}\n\nThis will rebuild the whole database to reduce file size.", + "compactDatabaseContent": "Database size: {size}\n\nThis will reorganize the database to reduce file size. No data will be deleted.", "confirm": "Confirm", "closeAfterSave": "Save and close", "cmd": "Command", @@ -58,6 +58,8 @@ "connectionStatsDesc": "View server connection success rate and history", "container": "Container", "containerTrySudoTip": "For example: In the app, the user is set to aaa, but Docker is installed under the root user. In this case, you need to enable this option.", + "containerSudoPasswordRequired": "Sudo password is required to access Docker. Please enter your password.", + "containerSudoPasswordIncorrect": "Sudo password is incorrect or not allowed. Please try again.", "convert": "Convert", "copyPath": "Copy path", "cpuViewAsProgressTip": "Display the usage of each CPU in a progress bar style (old style)", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 536c5b17..3033b158 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Ver la tasa de éxito de conexión del servidor e historial", "container": "Contenedor", "containerTrySudoTip": "Por ejemplo: si configuras el usuario dentro de la app como aaa, pero Docker está instalado bajo el usuario root, entonces necesitarás habilitar esta opción", + "containerSudoPasswordRequired": "Se requiere contraseña de sudo para acceder a Docker. Por favor ingrese su contraseña.", + "containerSudoPasswordIncorrect": "La contraseña de sudo es incorrecta o no está permitida. Por favor intente de nuevo.", "convert": "Convertir", "copyPath": "Copiar ruta", "cpuViewAsProgressTip": "Muestre la tasa de uso de cada CPU en estilo de barra de progreso (estilo antiguo)", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 402ab2d6..eda94e15 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Voir le taux de réussite de connexion du serveur et l'historique", "container": "Conteneur", "containerTrySudoTip": "Par exemple : Dans l'application, l'utilisateur est défini comme aaa, mais Docker est installé sous l'utilisateur root. Dans ce cas, vous devez activer cette option.", + "containerSudoPasswordRequired": "Un mot de passe sudo est requis pour accéder à Docker. Veuillez entrer votre mot de passe.", + "containerSudoPasswordIncorrect": "Le mot de passe sudo est incorrect ou non autorisé. Veuillez réessayer.", "convert": "Convertir", "copyPath": "Copier le chemin", "cpuViewAsProgressTip": "Afficher le taux d'utilisation de chaque CPU sous forme de barre de progression (ancien style)", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index f6e776d4..bc22570b 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Lihat tingkat keberhasilan koneksi server dan riwayat", "container": "Wadah", "containerTrySudoTip": "Contohnya: Di dalam aplikasi, pengguna diatur sebagai aaa, tetapi Docker diinstal di bawah pengguna root. Dalam kasus ini, Anda perlu mengaktifkan opsi ini.", + "containerSudoPasswordRequired": "Kata sandi sudo diperlukan untuk mengakses Docker. Silakan masukkan kata sandi Anda.", + "containerSudoPasswordIncorrect": "Kata sandi sudo salah atau tidak diizinkan. Silakan coba lagi.", "convert": "Mengubah", "copyPath": "Path Copy", "cpuViewAsProgressTip": "Tampilkan tingkat penggunaan setiap CPU dalam gaya bilah kemajuan (gaya lama)", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 4c90947e..cf4d527a 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "サーバー接続成功率と履歴を表示", "container": "コンテナ", "containerTrySudoTip": "例:アプリ内でユーザーをaaaに設定しているが、Dockerがrootユーザーでインストールされている場合、このオプションを有効にする必要があります", + "containerSudoPasswordRequired": "Dockerにアクセスするにはsudoパスワードが必要です。パスワードを入力してください。", + "containerSudoPasswordIncorrect": "sudoパスワードが正しくないか、許可されていません。再試行してください。", "convert": "変換", "copyPath": "パスをコピー", "cpuViewAsProgressTip": "各CPUの使用率をプログレスバースタイルで表示する(旧スタイル)", diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index a579fc06..fc0111fb 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Bekijk server verbindingssucces ratio en geschiedenis", "container": "Container", "containerTrySudoTip": "Bijvoorbeeld: in de app is de gebruiker ingesteld op aaa, maar Docker is geïnstalleerd onder de rootgebruiker. In dit geval moet u deze optie inschakelen.", + "containerSudoPasswordRequired": "Een sudo-wachtwoord is vereist om toegang te krijgen tot Docker. Voer uw wachtwoord in.", + "containerSudoPasswordIncorrect": "Het sudo-wachtwoord is onjuist of niet toegestaan. Probeer het opnieuw.", "convert": "Converteren", "copyPath": "Pad kopiëren", "cpuViewAsProgressTip": "Toon het gebruik van elke CPU in een voortgangsbalkstijl (oude stijl)", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index a2589770..8517b32e 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Ver taxa de sucesso de conexão do servidor e histórico", "container": "Contêiner", "containerTrySudoTip": "Por exemplo: se o usuário for definido como aaa dentro do app, mas o Docker estiver instalado sob o usuário root, esta opção precisará ser ativada", + "containerSudoPasswordRequired": "É necessária uma senha sudo para acessar o Docker. Por favor, insira sua senha.", + "containerSudoPasswordIncorrect": "A senha sudo está incorreta ou não é permitida. Por favor, tente novamente.", "convert": "Converter", "copyPath": "Copiar caminho", "cpuViewAsProgressTip": "Exiba a taxa de uso de cada CPU em estilo de barra de progresso (estilo antigo)", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index ee3268ae..5c52e0a4 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -58,6 +58,8 @@ "connectionStatsDesc": "Просмотр коэффициента успешности подключения к серверу и истории", "container": "Контейнер", "containerTrySudoTip": "Например: если пользователь в приложении установлен как aaa, но Docker установлен под пользователем root, тогда нужно включить эту опцию", + "containerSudoPasswordRequired": "Для доступа к Docker требуется пароль sudo. Пожалуйста, введите ваш пароль.", + "containerSudoPasswordIncorrect": "Пароль sudo неверен или не разрешён. Пожалуйста, попробуйте снова.", "convert": "Конвертировать", "copyPath": "Копировать путь", "cpuViewAsProgressTip": "Отобразите уровень использования каждого процессора в виде индикатора выполнения (старый стиль)", diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index ece319b7..ce3b1c94 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Sunucu bağlantı başarı oranını ve geçmişi görüntüle", "container": "Konteyner", "containerTrySudoTip": "Örneğin: Uygulamada kullanıcı aaa olarak ayarlanmış, ancak Docker root kullanıcısı altında kurulmuş. Bu durumda bu seçeneği etkinleştirmeniz gerekir.", + "containerSudoPasswordRequired": "Docker'e erişmek için sudo şifresi gereklidir. Lütfen şifrenizi girin.", + "containerSudoPasswordIncorrect": "Sudo şifresi yanlış veya izin verilmiyor. Lütfen tekrar deneyin.", "convert": "Dönüştür", "copyPath": "Yolu kopyala", "cpuViewAsProgressTip": "Her CPU'nun kullanımını ilerleme çubuğu tarzında göster (eski tarz)", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 244dced6..4049af95 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "Переглянути коефіцієнт успішності підключення до сервера та історію", "container": "Контейнер", "containerTrySudoTip": "Наприклад: У застосунку користувач це aaa, але Docker встановлений під користувачем root. У цьому випадку вам потрібно активувати цю опцію.", + "containerSudoPasswordRequired": "Для доступу до Docker потрібен пароль sudo. Будь ласка, введіть ваш пароль.", + "containerSudoPasswordIncorrect": "Пароль sudo неправильний або не дозволений. Будь ласка, спробуйте ще раз.", "convert": "Конвертувати", "copyPath": "Скопіювати шлях", "cpuViewAsProgressTip": "Відобразити використання кожного процесора у вигляді стовпчикової діаграми (старий стиль)", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 24ea01c6..fa5ef2f5 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -58,6 +58,8 @@ "connectionStatsDesc": "查看服务器连接成功率和历史记录", "container": "容器", "containerTrySudoTip": "例如:在应用内将用户设置为 aaa,但是 Docker 安装在root用户下,这时就需要启用此选项", + "containerSudoPasswordRequired": "需要 sudo 密码才能访问 Docker。请输入您的密码。", + "containerSudoPasswordIncorrect": "sudo 密码错误或无权限。请重试。", "convert": "转换", "copyPath": "复制路径", "cpuViewAsProgressTip": "以进度条样式显示每个 CPU 的使用率(旧版样式)", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 31f82c27..479ae29c 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -57,6 +57,8 @@ "connectionStatsDesc": "檢視伺服器連線成功率和歷史記錄", "container": "容器", "containerTrySudoTip": "例如:App 內設定使用者為 aaa,但是 Docker 安裝在 root 使用者,這時就需要開啟此選項", + "containerSudoPasswordRequired": "需要 sudo 密碼才能存取 Docker。請輸入您的密碼。", + "containerSudoPasswordIncorrect": "sudo 密碼錯誤或無權限。請重試。", "convert": "轉換", "copyPath": "複製路徑", "cpuViewAsProgressTip": "以進度條樣式顯示每個CPU的使用率(舊版樣式)",