diff --git a/assets/l10n/en.json b/assets/l10n/en.json index dbc0e09..4f2cd50 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -367,6 +367,7 @@ "{0}개 폴더를 lsync (실시간 동기화) 로 옮겼습니다.": "Moved {0} folders to lsync (live sync).", "{0}개 표시함": "{0} marked", "{0}개 풀기": "Extract {0}", + "{0}개 항목": "{0} items", "{0}개 항목을 {1}": "{0} items {1}", "{0}개 항목을 다음 폴더로 {1}\n{2}": "{0} items to this folder — {1}\n{2}", "{0}개 항목을 다음 폴더로 복사할까요?\n{1}": "Copy {0} items to this folder?\n{1}", @@ -376,6 +377,7 @@ "{0}개 항목을 지울까요? 되돌릴 수 없습니다.": "Delete {0} items? This cannot be undone.", "{0}개 항목을 휴지통으로 보낼까요?": "Move {0} items to the Recycle Bin?", "{0}개 항목을 휴지통으로 보냈습니다.": "Moved {0} items to the Recycle Bin.", + "{0}개는 원본에 남음 (건너뜀)": "{0} left in the source (skipped)", "{0}개는 지우지 않았습니다 (취소).": "{0} were not deleted (cancelled).", "{0}개는 휴지통보다 커서 영구 삭제했습니다 (Windows 가 물은 대로).": "{0} were too large for the Recycle Bin and were deleted permanently (as Windows asked).", "{0}개는 휴지통에 들어가지 않아 영구 삭제했습니다 (Windows 가 묻고 고른 대로).": "{0} didn't fit in the Recycle Bin and were deleted permanently (as chosen when Windows asked).", @@ -979,6 +981,7 @@ "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · WebDAV)": "If the destination folder already has the same name (built-in method · WebDAV)", "복사 · 이동할 항목을 고르세요 (누르거나 오른쪽 동그라미로 표시).": "Choose items to copy · move (tap, or mark with the circle on the right).", "복사 끝 ({0}): {1}개 → {2}": "Copy done ({0}): {1} → {2}", + "복사 끝: {0}개 · {1} → {2}": "Copy done: {0} · {1} → {2}", "복사 모니터링": "Copy monitor", "복사 실패 ({0}): {1}": "Copy failed ({0}): {1}", "복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "This is a copy task, so source-removing options in the field (--remove-source-files · /MOV) are not used. To move, turn on [Remove source files] on the Rsync screen", @@ -1478,6 +1481,7 @@ "이동": "Move", "이동 ({0})": "Move ({0})", "이동 (붙여넣기로)": "Move (to paste)", + "이동 끝: {0}개 · {1} → {2}": "Move done: {0} · {1} → {2}", "이동 버튼 (세부 정보 오른쪽 아래, 위로 하나씩)": "Move buttons (bottom right of the details, stacked upward)", "이동 버튼 추가": "Add move button", "이동 버튼의 폴더": "Folder of the move button", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 2dbb471..1fdd1b5 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -367,6 +367,7 @@ "{0}개 폴더를 lsync (실시간 동기화) 로 옮겼습니다.": "{0} 個のフォルダーを lsync (リアルタイム同期) へ移しました。", "{0}개 표시함": "{0} 件をマーク", "{0}개 풀기": "{0} 個を展開", + "{0}개 항목": "{0} 項目", "{0}개 항목을 {1}": "{0} 件の項目を{1}", "{0}개 항목을 다음 폴더로 {1}\n{2}": "{0} 件の項目を次のフォルダーへ{1}\n{2}", "{0}개 항목을 다음 폴더로 복사할까요?\n{1}": "{0} 件の項目を次のフォルダーへコピーしますか?\n{1}", @@ -376,6 +377,7 @@ "{0}개 항목을 지울까요? 되돌릴 수 없습니다.": "{0} 件の項目を削除しますか? 元に戻せません。", "{0}개 항목을 휴지통으로 보낼까요?": "{0} 個の項目をごみ箱に移動しますか?", "{0}개 항목을 휴지통으로 보냈습니다.": "{0} 個の項目をごみ箱に移動しました。", + "{0}개는 원본에 남음 (건너뜀)": "{0} 件は元の場所に残っています (スキップ)", "{0}개는 지우지 않았습니다 (취소).": "{0} 個は削除しませんでした (キャンセル)。", "{0}개는 휴지통보다 커서 영구 삭제했습니다 (Windows 가 물은 대로).": "{0} 個はごみ箱より大きいため完全に削除しました (Windows の確認どおり)。", "{0}개는 휴지통에 들어가지 않아 영구 삭제했습니다 (Windows 가 묻고 고른 대로).": "{0} 件はごみ箱に入らなかったため完全に削除しました (Windows の確認で選んだとおり)。", @@ -979,6 +981,7 @@ "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · WebDAV)": "コピー・移動先のフォルダーに同じ名前がある場合 (現在の方式・WebDAV)", "복사 · 이동할 항목을 고르세요 (누르거나 오른쪽 동그라미로 표시).": "コピー · 移動する項目を選んでください (タップ、または右の丸でマーク)。", "복사 끝 ({0}): {1}개 → {2}": "コピー完了 ({0}): {1} 件 → {2}", + "복사 끝: {0}개 · {1} → {2}": "コピー完了: {0} 件 · {1} → {2}", "복사 모니터링": "コピーのモニタリング", "복사 실패 ({0}): {1}": "コピー失敗 ({0}): {1}", "복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "コピー作業のため、欄の元ファイル削除オプション (--remove-source-files · /MOV) は使いません。移動するには Rsync 画面で [元ファイルを削除] をオンにしてください", @@ -1478,6 +1481,7 @@ "이동": "移動", "이동 ({0})": "移動({0})", "이동 (붙여넣기로)": "移動 (貼り付け用)", + "이동 끝: {0}개 · {1} → {2}": "移動完了: {0} 件 · {1} → {2}", "이동 버튼 (세부 정보 오른쪽 아래, 위로 하나씩)": "移動ボタン(詳細の右下、上に 1つずつ)", "이동 버튼 추가": "移動ボタンを追加", "이동 버튼의 폴더": "移動ボタンのフォルダー", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 32b87df..6dd709c 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -367,6 +367,7 @@ "{0}개 폴더를 lsync (실시간 동기화) 로 옮겼습니다.": "已将 {0} 个文件夹移到 lsync (实时同步)。", "{0}개 표시함": "已标记 {0} 个", "{0}개 풀기": "解压 {0} 个", + "{0}개 항목": "{0} 个项目", "{0}개 항목을 {1}": "{0} 个项目{1}", "{0}개 항목을 다음 폴더로 {1}\n{2}": "将 {0} 个项目到以下文件夹 — {1}\n{2}", "{0}개 항목을 다음 폴더로 복사할까요?\n{1}": "要将 {0} 个项目复制到以下文件夹吗?\n{1}", @@ -376,6 +377,7 @@ "{0}개 항목을 지울까요? 되돌릴 수 없습니다.": "删除 {0} 个项目吗? 此操作无法撤销。", "{0}개 항목을 휴지통으로 보낼까요?": "将 {0} 个项目移到回收站吗?", "{0}개 항목을 휴지통으로 보냈습니다.": "已将 {0} 个项目移到回收站。", + "{0}개는 원본에 남음 (건너뜀)": "{0} 个留在源中(已跳过)", "{0}개는 지우지 않았습니다 (취소).": "{0} 个未删除 (已取消)。", "{0}개는 휴지통보다 커서 영구 삭제했습니다 (Windows 가 물은 대로).": "{0} 个超出回收站容量,已永久删除 (按 Windows 的提示)。", "{0}개는 휴지통에 들어가지 않아 영구 삭제했습니다 (Windows 가 묻고 고른 대로).": "{0} 个项目无法放入回收站,已永久删除 (按 Windows 询问时的选择)。", @@ -979,6 +981,7 @@ "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · WebDAV)": "目标文件夹中已有同名项目时 (当前方式 · WebDAV)", "복사 · 이동할 항목을 고르세요 (누르거나 오른쪽 동그라미로 표시).": "请选择要复制 · 移动的项目 (点按,或用右侧圆圈标记)。", "복사 끝 ({0}): {1}개 → {2}": "复制完成 ({0}): {1} 个 → {2}", + "복사 끝: {0}개 · {1} → {2}": "复制完成:{0} 个 · {1} → {2}", "복사 모니터링": "复制监控", "복사 실패 ({0}): {1}": "复制失败 ({0}): {1}", "복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "这是复制任务,栏中的删除源文件选项(--remove-source-files · /MOV)不会使用。要移动,请在 Rsync 界面打开 [删除源文件]", @@ -1478,6 +1481,7 @@ "이동": "移动", "이동 ({0})": "移动({0})", "이동 (붙여넣기로)": "移动 (用于粘贴)", + "이동 끝: {0}개 · {1} → {2}": "移动完成:{0} 个 · {1} → {2}", "이동 버튼 (세부 정보 오른쪽 아래, 위로 하나씩)": "移动按钮(详细信息右下角,向上依次排列)", "이동 버튼 추가": "添加移动按钮", "이동 버튼의 폴더": "移动按钮的文件夹", diff --git a/lib/app/copy_center.dart b/lib/app/copy_center.dart index 9bf81ca..97cd278 100644 --- a/lib/app/copy_center.dart +++ b/lib/app/copy_center.dart @@ -135,12 +135,18 @@ class CopyCenter extends ChangeNotifier { await update(cur.copyWith( lastRun: DateTime.now().toIso8601String(), lastResult: e == null ? 'done' : e is FileOpCancelled ? 'cancelled' : 'failed', - lastMessage: e == null ? (job.pruned > 0 ? trf('빈 폴더 {0}개 지움', [job.pruned]) : '') : '$e', + // 216: 이동 뒤 원본에 남은 것도 카드에 + lastMessage: e == null + ? [ + if (job.leftInSource > 0) trf('{0}개는 원본에 남음 (건너뜀)', [job.leftInSource]), + if (job.pruned > 0) trf('빈 폴더 {0}개 지움', [job.pruned]), + ].join(' · ') + : '$e', lastFiles: job.allDone, )); } c.note(e == null - ? trf('복사 끝 ({0}): {1}개 → {2}', [method.label, t.sources.length, t.dest]) + ? '${job.doneSummary()} (${tr(method.label)})' : trf('복사 실패 ({0}): {1}', [method.label, e])); notifyListeners(); } diff --git a/lib/app/transfer_job.dart b/lib/app/transfer_job.dart index b553919..b0be387 100644 --- a/lib/app/transfer_job.dart +++ b/lib/app/transfer_job.dart @@ -126,6 +126,18 @@ class TransferJob extends ChangeNotifier { int get allFiles => filesTotal.fold(0, (a, b) => a + b); int get allDone => filesDone.fold(0, (a, b) => a + b); + /// 216: 이동이 끝난 뒤 원본에 남은 파일 수 (건너뛴 것) + int leftInSource = 0; + + /// 212 · 216: 끝 알림 한 줄 - "이동 끝: n개 · 원본 → 대상 · m개는 원본에 남음 (건너뜀)" / "복사 끝: n개 · 원본 → 대상" + String doneSummary() { + final from = sources.length == 1 ? vBasename(sources.first) : trf('{0}개 항목', [sources.length]); + final total = filesTotal.fold(0, (a, b) => a + b); + final n = move ? (total - leftInSource).clamp(0, total) : allDone; + final head = move ? trf('이동 끝: {0}개 · {1} → {2}', [n, from, vBasename(dest)]) : trf('복사 끝: {0}개 · {1} → {2}', [n, from, vBasename(dest)]); + return leftInSource > 0 ? '$head · ${trf('{0}개는 원본에 남음 (건너뜀)', [leftInSource])}' : head; + } + /// 위쪽 진행률: 끝낸 항목 + 지금 항목의 파일 진행 double get overall { if (finished) return 1; @@ -214,6 +226,15 @@ class TransferJob extends ChangeNotifier { if (isDav(s) || FileSystemEntity.isDirectorySync(s)) pruned += await removeEmptyDirs(s, keepRoot: prune == 'keep'); } } + // 216: 옮긴 뒤 원본에 남은 파일을 다시 센다 (옵션 -u · --exclude · 대상이 더 새것 등으로 건너뜀, 맞춘 뒤 바뀜) - + // 예전에는 남은 것이 있어도 "끝" 으로만 보였다 + if (move) { + for (final s in sources) { + try { + if (await vExists(s)) leftInSource += await countFiles(s); + } catch (_) {} + } + } } catch (e) { error = cancelled ? const FileOpCancelled() : e; } finally { diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index 63ef48c..e8c44bc 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -2271,7 +2271,8 @@ class _ExplorerPageState extends State { ? tr('취소했습니다.') : error != null ? trf('끝나지 못했습니다: {0}', [error]) - : trf('rsync 끝: {0}', [jobs.map((j) => '${vBasename(j.sources.first)} → ${vBasename(j.dest)}').join(' · ')])); + // 212 · 216: 이동인지 · 몇 개인지 · 원본에 남은 것 + : jobs.map((j) => j.doneSummary()).join('\n')); await Future.delayed(const Duration(milliseconds: 1200)); if (mounted) setState(() => _jobs = [for (final j in _jobs) if (!jobs.contains(j)) j]); } diff --git a/test/move_settled_test.dart b/test/move_settled_test.dart index 64df5c5..65c89c2 100644 --- a/test/move_settled_test.dart +++ b/test/move_settled_test.dart @@ -40,6 +40,10 @@ void main() { expect(File(p.join(src, 'a.txt')).existsSync(), isFalse, reason: '맞춘 것은 지움'); expect(old.existsSync(), isTrue, reason: '건너뛴 것은 남김 (예전에는 지웠다)'); expect(File(p.join(dst, 'keep.txt')).readAsStringSync(), 'newer target'); + // 216 · 212: 원본에 남은 것을 다시 세어 끝 알림에 + expect(job.leftInSource, 1); + expect(job.doneSummary(), contains('이동 끝: 1개 · src → dst')); + expect(job.doneSummary(), contains('1개는 원본에 남음 (건너뜀)')); }); test('214 c: 맞추는 동안 원본에 새로 생긴 파일은 지우지 않는다', () async {