From 9af4e8f4ff135429b679efc597cb7ab8079520a5 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 28 Aug 2026 05:20:34 +0200 Subject: [PATCH] feat(playlists): sentence-style two-way sync preview (only changed sides) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The confirm dialog spelled out both sides even when one was "+0 / −0", which read as engineer shorthand. Build the message from one sentence per side that actually changes and add a short "stays unchanged" line for the untouched side; the zero side is no longer shown as a bare +0/−0 to decode. Assembled in the component from granular i18n clauses (add/remove per side, reorder, rename) with count plurals; HU+EN. Replaces the single pushPlanMerge template. --- frontend/src/components/Playlists.tsx | 46 ++++++++++++++------- frontend/src/i18n/locales/en/playlists.json | 16 ++++++- frontend/src/i18n/locales/hu/playlists.json | 16 ++++++- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/Playlists.tsx b/frontend/src/components/Playlists.tsx index d69c07ce..977e2926 100644 --- a/frontend/src/components/Playlists.tsx +++ b/frontend/src/components/Playlists.tsx @@ -384,21 +384,37 @@ export default function Playlists() { }); return; } - const message = - plan.action === "create" - ? t("playlists.pushPlanCreate", { - count: plan.add_yt, - units: plan.units_estimate, - left: plan.remaining_today, - }) - : t("playlists.pushPlanMerge", { - addYt: plan.add_yt, - addLocal: plan.add_local, - removeYt: plan.remove_yt, - removeLocal: plan.remove_local, - units: plan.units_estimate, - left: plan.remaining_today, - }); + let message: string; + if (plan.action === "create") { + message = t("playlists.pushPlanCreate", { + count: plan.add_yt, + units: plan.units_estimate, + left: plan.remaining_today, + }); + } else { + // One sentence per side that actually changes; the untouched side gets a short "stays + // unchanged" reassurance instead of a "+0 / −0" the reader has to decode. + const here: string[] = []; + if (plan.add_local) here.push(t("playlists.mergeAddLocal", { count: plan.add_local })); + if (plan.remove_local) + here.push(t("playlists.mergeRemoveLocal", { count: plan.remove_local })); + const yt: string[] = []; + if (plan.add_yt) yt.push(t("playlists.mergeAddYt", { count: plan.add_yt })); + if (plan.remove_yt) yt.push(t("playlists.mergeRemoveYt", { count: plan.remove_yt })); + if (plan.to_reorder) yt.push(t("playlists.mergeReorder", { count: plan.to_reorder })); + if (plan.rename) yt.push(t("playlists.mergeRename")); + const sentences = [...here, ...yt]; + if (here.length && !yt.length) sentences.push(t("playlists.mergeYtUnchanged")); + if (yt.length && !here.length) sentences.push(t("playlists.mergeLocalUnchanged")); + const quota = + plan.units_estimate > 0 + ? t("playlists.mergeQuota", { + units: plan.units_estimate, + left: plan.remaining_today, + }) + : t("playlists.mergeQuotaFree"); + message = sentences.join(" ") + quota; + } const ok = await confirm({ title: t("playlists.pushTitle"), message, diff --git a/frontend/src/i18n/locales/en/playlists.json b/frontend/src/i18n/locales/en/playlists.json index 475a5f5f..c9ae9858 100644 --- a/frontend/src/i18n/locales/en/playlists.json +++ b/frontend/src/i18n/locales/en/playlists.json @@ -35,7 +35,21 @@ "pushTitle": "Sync to YouTube", "pushConfirm": "Sync", "pushPlanCreate": "Create a new YouTube playlist with {{count}} videos? (~{{units}} quota units; {{left}} left today.)", - "pushPlanMerge": "Two-way sync — on YouTube: +{{addYt}} / −{{removeYt}}; here: +{{addLocal}} / −{{removeLocal}}. (~{{units}} quota units; {{left}} left today.)", + "mergeAddLocal_one": "{{count}} video added to Siftlode from YouTube.", + "mergeAddLocal_other": "{{count}} videos added to Siftlode from YouTube.", + "mergeRemoveLocal_one": "{{count}} video removed from Siftlode.", + "mergeRemoveLocal_other": "{{count}} videos removed from Siftlode.", + "mergeAddYt_one": "{{count}} video added to YouTube.", + "mergeAddYt_other": "{{count}} videos added to YouTube.", + "mergeRemoveYt_one": "{{count}} video removed from YouTube.", + "mergeRemoveYt_other": "{{count}} videos removed from YouTube.", + "mergeReorder_one": "{{count}} video reordered on YouTube.", + "mergeReorder_other": "{{count}} videos reordered on YouTube.", + "mergeRename": "The playlist will be renamed on YouTube.", + "mergeYtUnchanged": "Your YouTube playlist stays unchanged.", + "mergeLocalUnchanged": "Your Siftlode playlist stays unchanged.", + "mergeQuota": " (~{{units}} quota units; {{left}} left today.)", + "mergeQuotaFree": " (~0 quota units.)", "pushNoQuota": "Not enough YouTube quota left today ({{left}}) for this sync (~{{units}} needed). Try again tomorrow.", "pushUpToDate": "“{{name}}” is already in sync with YouTube.", "pushDone": "“{{name}}” synced to YouTube ✓", diff --git a/frontend/src/i18n/locales/hu/playlists.json b/frontend/src/i18n/locales/hu/playlists.json index 720301ba..cddb6c19 100644 --- a/frontend/src/i18n/locales/hu/playlists.json +++ b/frontend/src/i18n/locales/hu/playlists.json @@ -35,7 +35,21 @@ "pushTitle": "Szinkron YouTube-ra", "pushConfirm": "Szinkron", "pushPlanCreate": "Létrehozol egy új YouTube-listát {{count}} videóval? (~{{units}} kvótaegység; ma még {{left}} maradt.)", - "pushPlanMerge": "Kétirányú szinkron — YouTube-on: +{{addYt}} / −{{removeYt}}; itt: +{{addLocal}} / −{{removeLocal}}. (~{{units}} kvótaegység; ma még {{left}} maradt.)", + "mergeAddLocal_one": "{{count}} videó bekerül a Siftlode-listádba a YouTube-ról.", + "mergeAddLocal_other": "{{count}} videó bekerül a Siftlode-listádba a YouTube-ról.", + "mergeRemoveLocal_one": "{{count}} videó törlődik a Siftlode-listádból.", + "mergeRemoveLocal_other": "{{count}} videó törlődik a Siftlode-listádból.", + "mergeAddYt_one": "{{count}} videó felkerül a YouTube-ra.", + "mergeAddYt_other": "{{count}} videó felkerül a YouTube-ra.", + "mergeRemoveYt_one": "{{count}} videó törlődik a YouTube-ról.", + "mergeRemoveYt_other": "{{count}} videó törlődik a YouTube-ról.", + "mergeReorder_one": "{{count}} videó átrendeződik a YouTube-on.", + "mergeReorder_other": "{{count}} videó átrendeződik a YouTube-on.", + "mergeRename": "A lista átnevezésre kerül a YouTube-on.", + "mergeYtUnchanged": "A YouTube-listád nem változik.", + "mergeLocalUnchanged": "A Siftlode-listád nem változik.", + "mergeQuota": " (~{{units}} kvótaegység; ma még {{left}} maradt.)", + "mergeQuotaFree": " (~0 kvótaegység.)", "pushNoQuota": "Nincs elég YouTube-kvóta mára ({{left}}) ehhez a szinkronhoz (~{{units}} kell). Próbáld újra holnap.", "pushUpToDate": "A(z) „{{name}}” már szinkronban van a YouTube-bal.", "pushDone": "„{{name}}” szinkronizálva a YouTube-ra ✓",