diff --git a/assets/l10n/en.json b/assets/l10n/en.json index f67d78e..9edcb03 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -813,6 +813,7 @@ "마지막으로: {0}": "To the last: {0}", "만든 그림 · 저장: {0}": "Made images · saved in: {0}", "만들 자막 언어 (파일명_언어코드.srt)": "Subtitle languages to make (filename_languagecode.srt)", + "만들거나 쓰지 못했습니다": "Could not create or write", "만들기": "Make", "만들다 만 파일 {0}개": "{0} incomplete files", "만들다 만 파일 {0}개를 지울까요?": "Delete {0} incomplete files?", @@ -1332,6 +1333,7 @@ "올린 그림은 지워집니다. 다시 보려면 해상도 올리기를 다시 해야 합니다.": "The upscaled image will be deleted. To see it again you will need to upscale again.", "올린 이 {0}": "Uploaded by {0}", "옮겼습니다.": "moved.", + "옮기거나 이름을 바꾸지 못했습니다": "Could not move or rename", "옮기기": "Move", "옮기는 중": "Moving", "옮길": "move", @@ -1794,6 +1796,7 @@ "지우면 휴지통으로": "Delete to the Recycle Bin", "지우지 못한 파일: {0}": "Couldn't delete: {0}", "지우지 못함: {0}": "Could not delete: {0}", + "지우지 못했습니다 (권한이 없거나 사용 중)": "Could not delete (no permission or in use)", "지우지 못했습니다: {0}": "Could not delete: {0}", "지우지 않고 맞춤 · 대상에만 있는 {0}개를 지울지 확인 필요": "Synced without deleting · confirm whether to delete {0} items only in the target", "지우지 않기 (지우기 끄기)": "Don't delete (turn off delete)", @@ -1934,7 +1937,9 @@ "파일 폴더": "File folder", "파일 확인 중…": "Checking files…", "파일만 골랐을 때": "When only files are selected", + "파일을 복사하지 못했습니다": "Could not copy the file", "파일을 분석할 수 없습니다: {0}": "Cannot analyze the file: {0}", + "파일을 읽지 못했습니다": "Could not read the file", "파일이 깨졌습니다": "The file is damaged", "파일이 깨졌습니다 · [다시 받기] 를 눌러 주세요": "The file is damaged · tap [Download again]", "파일이 보이지 않습니다: \"모든 파일에 대한 접근\" 권한이 없습니다": "Files are not shown: \"All files access\" is not allowed", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 3f5242d..69eca3d 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -813,6 +813,7 @@ "마지막으로: {0}": "最後へ: {0}", "만든 그림 · 저장: {0}": "作成した画像・保存先: {0}", "만들 자막 언어 (파일명_언어코드.srt)": "作成する字幕の言語(ファイル名_言語コード.srt)", + "만들거나 쓰지 못했습니다": "作成または書き込みできませんでした", "만들기": "作成", "만들다 만 파일 {0}개": "未完成のファイル {0} 個", "만들다 만 파일 {0}개를 지울까요?": "未完成のファイル {0} 個を削除しますか?", @@ -1332,6 +1333,7 @@ "올린 그림은 지워집니다. 다시 보려면 해상도 올리기를 다시 해야 합니다.": "高解像度化した画像は削除されます。もう一度見るには高解像度化をやり直す必要があります。", "올린 이 {0}": "投稿者 {0}", "옮겼습니다.": "移動しました。", + "옮기거나 이름을 바꾸지 못했습니다": "移動または名前の変更ができませんでした", "옮기기": "移動", "옮기는 중": "移動中", "옮길": "移す", @@ -1794,6 +1796,7 @@ "지우면 휴지통으로": "削除するとごみ箱へ", "지우지 못한 파일: {0}": "削除できなかったファイル: {0}", "지우지 못함: {0}": "削除できません: {0}", + "지우지 못했습니다 (권한이 없거나 사용 중)": "削除できませんでした (権限がないか使用中)", "지우지 못했습니다: {0}": "削除できませんでした: {0}", "지우지 않고 맞춤 · 대상에만 있는 {0}개를 지울지 확인 필요": "削除せずに同期・対象にだけある {0} 個を削除するか確認が必要", "지우지 않기 (지우기 끄기)": "削除しない (削除をオフ)", @@ -1934,7 +1937,9 @@ "파일 폴더": "ファイル フォルダー", "파일 확인 중…": "ファイルを確認中…", "파일만 골랐을 때": "ファイルだけを選んだとき", + "파일을 복사하지 못했습니다": "ファイルをコピーできませんでした", "파일을 분석할 수 없습니다: {0}": "ファイルを分析できません: {0}", + "파일을 읽지 못했습니다": "ファイルを読み取れませんでした", "파일이 깨졌습니다": "ファイルが壊れています", "파일이 깨졌습니다 · [다시 받기] 를 눌러 주세요": "ファイルが壊れています・[再ダウンロード] を押してください", "파일이 보이지 않습니다: \"모든 파일에 대한 접근\" 권한이 없습니다": "ファイルが表示されません: 「すべてのファイルへのアクセス」権限がありません", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 62c399f..173d99d 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -813,6 +813,7 @@ "마지막으로: {0}": "到最后: {0}", "만든 그림 · 저장: {0}": "生成的图片 · 保存位置: {0}", "만들 자막 언어 (파일명_언어코드.srt)": "要生成的字幕语言(文件名_语言代码.srt)", + "만들거나 쓰지 못했습니다": "无法创建或写入", "만들기": "生成", "만들다 만 파일 {0}개": "{0} 个未完成的文件", "만들다 만 파일 {0}개를 지울까요?": "删除 {0} 个未完成的文件吗?", @@ -1332,6 +1333,7 @@ "올린 그림은 지워집니다. 다시 보려면 해상도 올리기를 다시 해야 합니다.": "提升后的图片将被删除。要再次查看需要重新提升分辨率。", "올린 이 {0}": "上传者 {0}", "옮겼습니다.": "已移动。", + "옮기거나 이름을 바꾸지 못했습니다": "无法移动或重命名", "옮기기": "移动", "옮기는 중": "正在移动", "옮길": "移动", @@ -1794,6 +1796,7 @@ "지우면 휴지통으로": "删除时移到回收站", "지우지 못한 파일: {0}": "无法删除的文件: {0}", "지우지 못함: {0}": "无法删除: {0}", + "지우지 못했습니다 (권한이 없거나 사용 중)": "无法删除(没有权限或正在使用)", "지우지 못했습니다: {0}": "无法删除: {0}", "지우지 않고 맞춤 · 대상에만 있는 {0}개를 지울지 확인 필요": "已同步但未删除 · 需确认是否删除仅在目标中的 {0} 个项目", "지우지 않기 (지우기 끄기)": "不删除 (关闭删除)", @@ -1934,7 +1937,9 @@ "파일 폴더": "文件夹", "파일 확인 중…": "正在检查文件…", "파일만 골랐을 때": "只选择文件时", + "파일을 복사하지 못했습니다": "无法复制文件", "파일을 분석할 수 없습니다: {0}": "无法分析文件: {0}", + "파일을 읽지 못했습니다": "无法读取文件", "파일이 깨졌습니다": "文件已损坏", "파일이 깨졌습니다 · [다시 받기] 를 눌러 주세요": "文件已损坏 · 请点按 [重新下载]", "파일이 보이지 않습니다: \"모든 파일에 대한 접근\" 권한이 없습니다": "文件不可见: 没有“所有文件访问”权限", diff --git a/lib/app/copy_center.dart b/lib/app/copy_center.dart index 80a45cd..1d6b322 100644 --- a/lib/app/copy_center.dart +++ b/lib/app/copy_center.dart @@ -142,7 +142,7 @@ class CopyCenter extends ChangeNotifier { if (job.leftInSource > 0) trf('{0}개는 원본에 남음 (건너뜀)', [job.leftInSource]), if (job.pruned > 0) trf('빈 폴더 {0}개 지움', [job.pruned]), ].join(' · ') - : userErrorText(e), // 237: 예외 이름 없이 (원래 글은 작업 기록에) + : userErrorText(e, sources: t.sources), // 237 · 245: 예외 이름 없이 · 화면 언어로 (원래 글은 작업 기록에) lastFiles: job.doneFiles, lastLeft: e == null ? job.leftInSource : 0, lastDetail: e == null || e is FileOpCancelled ? '' : '$e', diff --git a/lib/app/transfer_job.dart b/lib/app/transfer_job.dart index 5fe2d72..872ca52 100644 --- a/lib/app/transfer_job.dart +++ b/lib/app/transfer_job.dart @@ -14,17 +14,39 @@ import '../l10n/tr.dart'; /// 복사 · 이동 한 번 (파일 탐색기). 방법 (현재 방식 · rsync · robocopy) 과 상관없이 같은 진행 상태를 알린다: /// - 위쪽: 고른 항목 (폴더) 중 몇 번째인지 · 전체 진행률 /// - 아래쪽: 지금 항목 (폴더) 의 파일 중 몇 개를 했는지 -/// 215: rsync 가 실패했을 때 보일 글 - 원본을 못 지웠으면 쉬운 말 한 줄, 그리고 오류 · 권한 줄을 끝쪽부터 (전송한 파일 이름이 아니라). -/// 예전에는 출력의 앞쪽 8줄이라 파일 이름만 보이고 "Permission denied" · "rsync error … (code 23)" 가 빠지기 쉬웠다 /// 237: 사용자에게 보이는 실패 글 - "ProcessException:" 같은 예외 이름 · "Command:" 줄은 빼고 내용만. -/// 원래 글 ('$e') 은 작업 기록에 그대로 남긴다 -String userErrorText(Object e) => switch (e) { +/// 원래 글 ('$e') 은 작업 기록에 그대로 남긴다. +/// 245: 앱이 직접 할 때의 파일 오류 (Dart 의 "Cannot delete file" 등 영어) 는 화면 언어로. [sources] 안의 파일을 못 지웠으면 +/// rsync 때 (215) 와 같은 글 - 이동에서 원본만 남은 것 +String userErrorText(Object e, {List sources = const []}) => switch (e) { ProcessException(:final message) when message.isNotEmpty => message, - FileSystemException(:final message, :final path, :final osError) => - [message, if (path != null && path.isNotEmpty) path, if (osError != null && osError.message.isNotEmpty) osError.message].join(' · '), + FileSystemException(:final message, :final path, :final osError) => [ + _fileErrorText(message, path, sources), + if (path != null && path.isNotEmpty) path, + if (osError != null && osError.message.isNotEmpty) osError.message, + ].join(' · '), _ => '$e'.replaceFirst(RegExp(r'^\w*(Exception|Error)(\([^)]*\))?: '), ''), }; +String _fileErrorText(String message, String? path, List sources) { + final m = message.toLowerCase(); + if (m.startsWith('cannot delete') || m.startsWith('deletion failed')) { + final inSource = path != null && sources.any((s) => isSameOrInside(path, s)); + return inSource + ? tr('원본 파일을 지우지 못했습니다 (권한이 없거나 사용 중). 대상에는 복사됐고 원본은 남아 있습니다.') + : tr('지우지 못했습니다 (권한이 없거나 사용 중)'); + } + if (m.startsWith('cannot copy')) return tr('파일을 복사하지 못했습니다'); + if (m.startsWith('cannot rename')) return tr('옮기거나 이름을 바꾸지 못했습니다'); + if (m.startsWith('cannot open') || m.startsWith('cannot read')) return tr('파일을 읽지 못했습니다'); + if (m.startsWith('cannot create') || m.startsWith('creation failed') || m.startsWith('cannot write')) { + return tr('만들거나 쓰지 못했습니다'); + } + return message; +} + +/// 215: rsync 가 실패했을 때 보일 글 - 원본을 못 지웠으면 쉬운 말 한 줄, 그리고 오류 · 권한 줄을 끝쪽부터 (전송한 파일 이름이 아니라). +/// 예전에는 출력의 앞쪽 8줄이라 파일 이름만 보이고 "Permission denied" · "rsync error … (code 23)" 가 빠지기 쉬웠다 String rsyncFailureText(int code, List errLines, List allLines) { final all = [...errLines, ...allLines]; final removeFailed = all.any((l) { diff --git a/test/dismiss_test.dart b/test/dismiss_test.dart index 5ca3a2e..3e94662 100644 --- a/test/dismiss_test.dart +++ b/test/dismiss_test.dart @@ -274,6 +274,11 @@ void main() { expect(userErrorText(const ProcessException('rsync', ['-a'], '원본 파일을 지우지 못했습니다', 23)), '원본 파일을 지우지 못했습니다'); expect(userErrorText(const FileSystemException('지울 수 없음', r'C:\x', OSError('액세스가 거부되었습니다', 5))), r'지울 수 없음 · C:\x · 액세스가 거부되었습니다'); expect(userErrorText(Exception('끊김')), '끊김'); + // 245: 앱이 직접 할 때의 Dart 파일 오류 (영어) 도 rsync 때와 같은 글 - 원본을 못 지웠으면 + expect(userErrorText(const FileSystemException('Cannot delete file', r'C:\s\a.txt', OSError('사용 중', 32)), sources: [r'C:\s']), + r'원본 파일을 지우지 못했습니다 (권한이 없거나 사용 중). 대상에는 복사됐고 원본은 남아 있습니다. · C:\s\a.txt · 사용 중'); + expect(userErrorText(const FileSystemException('Cannot delete file', r'D:\t\x.txt'), sources: [r'C:\s']).startsWith('지우지 못했습니다'), isTrue); + expect(userErrorText(const FileSystemException('Cannot copy file to D:\\t', r'C:\s\a.txt')).startsWith('파일을 복사하지 못했습니다'), isTrue); expect(userErrorText('그냥 글'), '그냥 글'); });