feat(playlists): sentence-style two-way sync preview (only changed sides)

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.
This commit is contained in:
2026-08-28 05:20:34 +02:00
parent a0b725d779
commit 9af4e8f4ff
3 changed files with 61 additions and 17 deletions
+31 -15
View File
@@ -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,
+15 -1
View File
@@ -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 ✓",
+15 -1
View File
@@ -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 ✓",