diff --git a/assets/l10n/en.json b/assets/l10n/en.json index 8dcb56b..bc34fb3 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -281,6 +281,7 @@ "lsync (실시간 동기화)": "lsync (live sync)", "lsync 로 이동": "Move to lsync", "quit 시작": "quit started", + "robocopy 복사 · 이동: 원본 안의 링크 · junction 은 따라가지 않고 건너뜁니다 (/XJ - 예전에는 링크가 가리키는 바깥 폴더의 파일까지 복사 · 이동했습니다). 링크째 복사하려면 robocopy 옵션 칸에 /SJ 를 넣으세요": "robocopy copy · move: links and junctions inside the source are no longer followed and are skipped (/XJ - before, files in the outside folder a link pointed to were copied or moved too). To copy links as links, add /SJ to the robocopy options", "robocopy 옵션": "robocopy options", "robocopy 종료 코드 {0}": "robocopy exit code {0}", "rsync --delete · robocopy /PURGE. 끄면 대상에 더하기 · 바꾸기만": "rsync --delete · robocopy /PURGE. When off, only adds · updates in the target", @@ -994,6 +995,7 @@ "복사 · 이동 {0}개": "{0} copy · move", "복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "{0} copy/move tasks have not finished. Exiting stops them, and the file being copied is removed so no incomplete file is left.", "복사 · 이동 중입니다": "Copying · moving in progress", + "복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "Copy · move: files are skipped when the receiving side is newer (before, the older source overwrote them, and a move also deleted the source). Skipped files stay in the source and their number appears in the finish notice. To change back: Settings > File explorer (or Rsync) > Skip when the receiving side is newer", "복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "Copy · move works with two panes (Pane layout > Two panes).", "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · 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).", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 641f3d6..51465bf 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -281,6 +281,7 @@ "lsync (실시간 동기화)": "lsync (リアルタイム同期)", "lsync 로 이동": "lsync へ移動", "quit 시작": "終了開始", + "robocopy 복사 · 이동: 원본 안의 링크 · junction 은 따라가지 않고 건너뜁니다 (/XJ - 예전에는 링크가 가리키는 바깥 폴더의 파일까지 복사 · 이동했습니다). 링크째 복사하려면 robocopy 옵션 칸에 /SJ 를 넣으세요": "robocopy のコピー · 移動: 元の中のリンク · ジャンクションはたどらずにスキップします (/XJ - 以前はリンク先の外のフォルダーのファイルまでコピー · 移動していました)。リンクのままコピーするには robocopy オプション欄に /SJ を入れてください", "robocopy 옵션": "robocopy オプション", "robocopy 종료 코드 {0}": "robocopy 終了コード {0}", "rsync --delete · robocopy /PURGE. 끄면 대상에 더하기 · 바꾸기만": "rsync --delete · robocopy /PURGE。オフなら対象に追加 · 更新のみ", @@ -994,6 +995,7 @@ "복사 · 이동 {0}개": "コピー・移動 {0} 件", "복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "コピー・移動 {0} 件がまだ終わっていません。終了すると止まり、コピー中のファイルは未完成のまま残らないよう削除します。", "복사 · 이동 중입니다": "コピー・移動中です", + "복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "コピー · 移動: 受け取る側のファイルが新しければスキップします (以前は古い元で上書きし、移動なら元も削除していました)。スキップしたファイルは元に残り、終了の通知にその数が出ます。戻すには: 環境設定 > ファイルエクスプローラー (または Rsync) > 受け取る側が新しければスキップ", "복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "コピー · 移動は 2 ペインで使います (ペインの配置 > 2 ペイン)。", "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · WebDAV)": "コピー・移動先のフォルダーに同じ名前がある場合 (現在の方式・WebDAV)", "복사 · 이동할 항목을 고르세요 (누르거나 오른쪽 동그라미로 표시).": "コピー · 移動する項目を選んでください (タップ、または右の丸でマーク)。", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 36a8462..452b72b 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -281,6 +281,7 @@ "lsync (실시간 동기화)": "lsync (实时同步)", "lsync 로 이동": "移到 lsync", "quit 시작": "开始退出", + "robocopy 복사 · 이동: 원본 안의 링크 · junction 은 따라가지 않고 건너뜁니다 (/XJ - 예전에는 링크가 가리키는 바깥 폴더의 파일까지 복사 · 이동했습니다). 링크째 복사하려면 robocopy 옵션 칸에 /SJ 를 넣으세요": "robocopy 复制 · 移动:源中的链接 · 联接不再跟随,而是跳过(/XJ - 以前会连链接指向的外部文件夹中的文件一起复制 · 移动)。要按链接本身复制,请在 robocopy 选项栏中加入 /SJ", "robocopy 옵션": "robocopy 选项", "robocopy 종료 코드 {0}": "robocopy 退出代码 {0}", "rsync --delete · robocopy /PURGE. 끄면 대상에 더하기 · 바꾸기만": "rsync --delete · robocopy /PURGE。关闭时只在目标中添加 · 更新", @@ -994,6 +995,7 @@ "복사 · 이동 {0}개": "复制 · 移动 {0} 个", "복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "{0} 个复制 · 移动尚未完成。退出会停止它们,并删除正在复制的文件,以免留下不完整的文件。", "복사 · 이동 중입니다": "正在复制 · 移动", + "복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "复制 · 移动:接收端文件较新时跳过(以前会用旧的源文件覆盖,移动时还会删除源)。跳过的文件留在源中,完成通知中会显示数量。恢复:设置 > 文件浏览器(或 Rsync)> 接收端较新时跳过", "복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "复制 · 移动需在双窗格中使用 (窗格布局 > 双窗格)。", "복사 · 이동할 폴더에 같은 이름이 있으면 (현재 방식 · WebDAV)": "目标文件夹中已有同名项目时 (当前方式 · WebDAV)", "복사 · 이동할 항목을 고르세요 (누르거나 오른쪽 동그라미로 표시).": "请选择要复制 · 移动的项目 (点按,或用右侧圆圈标记)。", diff --git a/lib/app/settings.dart b/lib/app/settings.dart index 079b064..f6c48a3 100644 --- a/lib/app/settings.dart +++ b/lib/app/settings.dart @@ -692,6 +692,7 @@ class AppSettings { 'rsyncPaths': rsyncPaths, 'components': components, 'aiImageV1': true, + 'robocopyLinksV1': true, 'navOrder': navOrder, 'swipeNav': swipeNav, 'imageExts': imageExts, @@ -1043,8 +1044,21 @@ class AppSettings { if (j['zipComicV2'] != true && j['zipComic'] != false && j.containsKey('imageExts') && !s.zipComic) 'zipComic', if (j['backgroundV2'] != true && j['runInBackground'] == false && s.runInBackground && Platform.isAndroid) 'background', + // 193 · 229: 예전 판을 쓰던 사람 (설정 파일이 있는데 이 항목이 없음) 에게 복사 · 이동이 받는 쪽의 더 새 파일을 건너뛰게 바뀐 것을 한 번 + if (j.isNotEmpty && !j.containsKey('copySkipNewer')) 'copySkipNewer', + // 193: robocopy 를 쓰던 사람 (Windows) 에게 링크 · junction 을 따라가지 않게 (/XJ) 바뀐 것을 한 번 + if (j['robocopyLinksV1'] != true && Platform.isWindows && _usedRobocopy(j)) 'robocopyLinks', ]; + /// 193: 예전 설정에서 robocopy 를 쓰고 있었는지 (복사 방법 · 기억한 복사 · 실시간 동기화 쌍) + static bool _usedRobocopy(Map j) { + bool robo(Object? x) => x is Map && x['method'] == 'robocopy'; + return j['copyMethodFile'] == 'robocopy' || + j['copyMethodFolder'] == 'robocopy' || + ((j['copyTasks'] as List?) ?? const []).any(robo) || + ((j['liveSyncPairs'] as List?) ?? const []).any(robo); + } + /// 다운로드 기본 위치: 프로그램 폴더 (쓸 수 없으면 사용자 다운로드 폴더) String resolvedDownloadRoot() { if (downloadRoot != null && downloadRoot!.isNotEmpty) return downloadRoot!; diff --git a/lib/ui/migration_notice.dart b/lib/ui/migration_notice.dart index ea73dd8..a5d950f 100644 --- a/lib/ui/migration_notice.dart +++ b/lib/ui/migration_notice.dart @@ -24,6 +24,10 @@ Future showMigrationNotice(BuildContext context, AppController c) async { 'zipComic' => tr('ZIP · CBZ 두 번 누르기: 만화 보기 → 목록. 되돌리기: 환경 설정 > 파일 탐색기 > ZIP · CBZ 를 만화로 보기'), 'background' => tr('백그라운드로 실행: 꺼짐 → 켜짐 (← 나 최근 앱에서 밀어도 작업이 계속됨). 되돌리기: 환경 설정 > Rsync > 백그라운드로 실행'), + 'copySkipNewer' => tr('복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). ' + '건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기'), + 'robocopyLinks' => tr('robocopy 복사 · 이동: 원본 안의 링크 · junction 은 따라가지 않고 건너뜁니다 (/XJ - 예전에는 링크가 가리키는 바깥 폴더의 파일까지 ' + '복사 · 이동했습니다). 링크째 복사하려면 robocopy 옵션 칸에 /SJ 를 넣으세요'), _ => id, }; final open = await showDialog( diff --git a/test/settings_store_test.dart b/test/settings_store_test.dart index 4713f26..9e95a75 100644 --- a/test/settings_store_test.dart +++ b/test/settings_store_test.dart @@ -84,4 +84,16 @@ void main() { // 처음 설치 (빈 설정) 는 알릴 것 없음 expect(AppSettings.fromJson({}).migrated, isEmpty); }); + + test('193: 예전 판에서 올라오면 바뀐 동작 (229 받는 쪽이 더 새것이면 건너뜀 · robocopy 링크) 을 한 번 알린다', () { + final old = AppSettings.fromJson({'copyMethodFolder': 'robocopy', 'explorerV2': true}); + expect(old.migrated, contains('copySkipNewer')); + expect(old.migrated.contains('robocopyLinks'), Platform.isWindows, reason: 'robocopy 는 Windows 만'); + // 저장 뒤 다시 읽으면 알리지 않음 + final again = AppSettings.fromJson(old.toJson()); + expect(again.migrated, isNot(contains('copySkipNewer'))); + expect(again.migrated, isNot(contains('robocopyLinks'))); + // robocopy 를 쓰지 않던 사람에게는 링크 알림 없음 + expect(AppSettings.fromJson({'explorerV2': true}).migrated, isNot(contains('robocopyLinks'))); + }); }