feat(i18n): translate remaining components (HU/EN/DE)

Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
This commit is contained in:
npeter83 2026-06-15 00:47:04 +02:00
parent b38ae92d8d
commit 9c61bd898d
45 changed files with 1522 additions and 355 deletions

View file

@ -0,0 +1,17 @@
{
"watchedUnmark": "Watched — click to unmark",
"markWatched": "Mark watched",
"savedRemove": "Saved — click to remove",
"saveForLater": "Save for later",
"unhide": "Unhide",
"hide": "Hide",
"onlyThisChannel": "Only this channel",
"thisChannel": "This channel",
"continueTitle": "Continue where you left off",
"continue": "Continue",
"restartTitle": "Play from the beginning",
"restart": "Restart",
"play": "Play",
"stream": "stream",
"views": "views"
}

View file

@ -0,0 +1,69 @@
{
"intro": "Set a channel's <0>priority</0> to push its videos up when you sort by “Channel priority”, attach your own <1>tags</1> to filter the feed, or <2>hide</2> a channel to drop it from the feed without unsubscribing.",
"filterPlaceholder": "Filter channels…",
"syncSubscriptions": "Sync subscriptions",
"syncSubscriptionsHint": "Re-import your subscription list from YouTube — adds channels you've newly followed and drops ones you've unfollowed. The videos themselves keep syncing automatically in the background; this does not re-fetch them.",
"backfillEverything": "Backfill everything",
"backfillEverythingHint": "Request full back-catalog backfill for every channel you're subscribed to. Older videos and search become complete as the shared daily quota allows — this can take a while.",
"filters": {
"all": "All",
"needsFull": "Needs full history",
"fullySynced": "Fully synced",
"hidden": "Hidden"
},
"tags": {
"yourTags": "Your tags",
"yourTagsHint": "Your personal labels. Attach them to channels below, then filter the feed by tag from the sidebar. (Separate from the automatic language/topic tags.)",
"newTag": "new tag",
"createTag": "Create tag"
},
"loading": "Loading channels…",
"empty": "No channels.",
"stats": {
"channels": "Channels",
"channelsHint": "Channels you're subscribed to.",
"recentSynced": "Recent synced",
"recentSyncedHint": "Channels whose recent uploads are in the local DB and show in your feed.",
"fullHistory": "Full history",
"fullHistoryHint": "Channels whose entire back-catalog is fetched, out of the ones you've requested full history for.",
"left": "left",
"leftHint": "Rough estimate to finish full-history backfill of {{count}} pending channel(s), based on the shared daily quota.",
"myVideos": "My videos",
"myVideosHint": "Total videos available across your channels.",
"quotaLeft": "Quota left",
"quotaLeftHint": "Shared YouTube API budget left today (resets midnight US Pacific)."
},
"row": {
"stored": "{{formatted}} stored",
"subs": "{{formatted}} subs",
"priorityHint": "Your ranking for this channel. Sort the feed by “Channel priority” to bring higher-priority channels to the top.",
"raisePriority": "Raise priority",
"lowerPriority": "Lower priority",
"recent": "recent",
"recentSyncedHint": "Recent uploads synced.",
"recentNotSyncedHint": "Recent uploads not fetched yet.",
"full": "full",
"fullHint": "Full history fetched.",
"fullHistoryQueued": "full history queued",
"queuedRequestedHint": "Full history requested — this channel's whole back-catalog will backfill as the shared quota allows. Click to cancel your request.",
"queuedByOtherHint": "Another subscriber already requested this channel's full history, so its whole back-catalog is on its way to everyone — nothing to do here.",
"getFullHistory": "get full history",
"getFullHistoryHint": "Only recent uploads so far. Click to request this channel's full back-catalog (older videos + complete search).",
"hiddenHint": "Hidden — this channel's videos are kept out of your feed. Click to show them again.",
"hideHint": "Hide this channel's videos from your feed. It stays subscribed; this doesn't unsubscribe on YouTube.",
"unhide": "Unhide",
"hideFromFeed": "Hide from feed",
"unsubscribeHint": "Unsubscribe from this channel on YouTube (changes your real account). Read-only mode hides this — use Hide instead.",
"unsubscribeOnYoutube": "Unsubscribe on YouTube",
"thisChannel": "This channel"
},
"notify": {
"synced": "Synced {{count}} subscriptions",
"syncFailed": "Subscription sync failed",
"unsubscribed": "Unsubscribed on YouTube",
"unsubscribeFailed": "Unsubscribe failed",
"fullHistoryRequested": "Full history requested for {{count}} channels",
"fullHistoryFailed": "Couldn't request full history"
},
"confirmUnsubscribe": "Unsubscribe from \"{{name}}\" on YouTube? This changes your real YouTube account. To just remove it from your feed, hide it instead."
}

View file

@ -0,0 +1,9 @@
{
"boundary": {
"title": "Something went wrong.",
"subtitle": "The app hit an unexpected error.",
"reload": "Reload",
"notifTitle": "Something broke",
"notifMessage": "Unexpected error"
}
}

View file

@ -0,0 +1,17 @@
{
"loading": "Loading feed…",
"loadError": "Couldn't load the feed.",
"emptyTitle": "Your feed is empty",
"emptyBody": "Connect your YouTube account to import your subscriptions and build your feed.",
"setUp": "Set up my feed",
"noMatches": "No videos match these filters.",
"videoCount_one": "{{formattedCount}} video",
"videoCount_other": "{{formattedCount}} videos",
"loadingMore": "Loading more…",
"hiddenNamed": "Hidden “{{title}}”",
"hidden": "Video hidden",
"undo": "Undo",
"markedWatchedNamed": "Marked watched “{{title}}”",
"markedWatched": "Marked watched",
"unwatch": "Unwatch"
}

View file

@ -0,0 +1,19 @@
{
"title": "Notifications",
"clearAll": "Clear all",
"clear": "Clear",
"empty": "No notifications yet.",
"actionNeeded": "Action needed",
"dismiss": "Dismiss",
"findInFeed": "Find in feed",
"unhide": "Unhide",
"unwatch": "Unwatch",
"unhidden": "Unhidden “{{title}}”",
"unwatched": "Unwatched “{{title}}”",
"time": {
"justNow": "just now",
"minutes": "{{count}}m ago",
"hours": "{{count}}h ago",
"days": "{{count}}d ago"
}
}

View file

@ -0,0 +1,29 @@
{
"close": "Close",
"consent": "Google will show a <0>\"Google hasn't verified this app\"</0> screen — that's expected, because Siftlode hasn't gone through Google's full review. It's safe to continue: click <1>Advanced</1> → <2>Go to Siftlode</2>. You can revoke access anytime from your <3>Google Account</3>.",
"read": {
"title": "Connect your YouTube subscriptions",
"body": "You're signed in. To build your feed, Siftlode needs <0>read-only</0> access to the channels you're subscribed to on YouTube. It never posts, deletes, or changes anything with this.",
"connect": "Connect (read-only)",
"skip": "Skip for now"
},
"importing": {
"title": "Building your feed…",
"body": "Importing your YouTube subscriptions. Channels already in Siftlode show up instantly; any new ones fill in automatically in the background."
},
"write": {
"title": "You're connected",
"importFailed": "Your YouTube account is connected, but importing subscriptions failed — you can retry from Settings → Sync. ",
"feedReady": "Your feed is ready{{channels}}. Optionally, let Siftlode ",
"feedReadyChannels": " ({{count}} channels)",
"rationale": "<0>unsubscribe</0> from channels on YouTube for you — this needs an extra write permission. Skip it to stay read-only; you can always enable it later in Settings.",
"retryImport": "Retry import",
"enableUnsubscribe": "Enable unsubscribe",
"keepReadOnly": "No thanks — keep it read-only"
},
"done": {
"title": "All set",
"body": "Read and unsubscribe access are both granted. You can review or revoke either one anytime in Settings → Account, or from your Google Account.",
"done": "Done"
}
}

View file

@ -0,0 +1,17 @@
{
"loading": "Loading…",
"backToOriginal": "Back to the original video",
"back": "Back",
"close": "Close",
"closeEsc": "Close (Esc)",
"description": "Description",
"noDescription": "No description.",
"channel": "Channel",
"views": "{{formattedCount}} views",
"stream": "stream",
"watched": "Watched",
"markWatched": "Mark watched",
"watchedUnmark": "Watched — click to unmark",
"jumpToTime": "Jump to this time",
"playInApp": "Play in the in-app player"
}

View file

@ -0,0 +1,101 @@
{
"title": "Settings",
"tabs": {
"appearance": "Appearance",
"notifications": "Notifications",
"sync": "Sync",
"account": "Account"
},
"appearance": {
"colorScheme": "Color scheme",
"display": "Display",
"darkMode": "Dark mode",
"listView": "List view",
"listViewHint": "Show the feed as a compact list instead of a grid of cards.",
"performanceMode": "Performance mode",
"performanceModeHint": "Turns off the translucent glass blur and soft shadows for snappier rendering on slower machines.",
"showHints": "Show hints",
"showHintsHint": "These little hover explanations. Turn them off once you know your way around.",
"textSize": "Text size"
},
"notifications": {
"title": "Notifications",
"sound": "Sound on alerts",
"soundHint": "Play a short tone when a notification needs your attention (e.g. errors, prompts).",
"autoDismiss": "Auto-dismiss toasts",
"autoDismissHint": "When off, pop-up toasts stay until you close them. When on, they fade after the set time.",
"dismissAfter": "Dismiss after",
"sendTest": "Send a test notification",
"testTitle": "Test notification",
"testMessage": "This is what a notification looks like."
},
"sync": {
"myStatus": "My sync status",
"channels": "Channels",
"channelsHint": "How many channels you're subscribed to.",
"recentSynced": "Recent synced",
"recentSyncedHint": "Channels whose recent uploads (~last year) are already in the local database, so they show in your feed.",
"fullHistory": "Full history",
"fullHistoryHint": "Channels whose entire back-catalog has been fetched, out of the ones you've requested full history for (opt in per channel on the Channels page).",
"fullHistoryEta": "Full history ETA",
"fullHistoryEtaHint": "Rough estimate to finish full-history backfill of {{count}} pending channel(s), based on the shared daily quota.",
"myVideos": "My videos",
"myVideosHint": "Total videos available to you across your subscribed channels.",
"quotaLeft": "Quota left today",
"quotaLeftHint": "Shared YouTube API budget remaining today (resets midnight US Pacific). Backfill yields to it.",
"loading": "Loading…",
"apiUsage": "Your API usage",
"today": "Today",
"todayHint": "YouTube API units your own actions used today (resets midnight US Pacific). Background sync isn't counted here.",
"last7d": "Last 7 days",
"last30d": "Last 30 days",
"allTime": "All time",
"byAction": "By action (30d)",
"noUsage": "No usage yet.",
"actions": "Actions",
"syncSubscriptions": "Sync subscriptions",
"syncSubscriptionsHint": "Re-import your YouTube subscriptions and pull in any newly-followed channels.",
"backfillEverything": "Backfill everything",
"backfillEverythingHint": "Request full back-catalog backfill for every channel you're subscribed to. Older videos + complete search fill in as the shared daily quota allows.",
"synced": "Synced {{count}} subscriptions",
"syncFailed": "Sync failed",
"fullHistoryRequested": "Full history requested for {{count}} channels",
"fullHistoryFailed": "Couldn't request full history",
"admin": "Admin",
"adminPauseHint": "Pause or resume the background sync for the whole app (all users).",
"resumeBackgroundSync": "Resume background sync",
"pauseBackgroundSync": "Pause background sync"
},
"account": {
"title": "Account",
"youtubeAccess": "YouTube access",
"youtubeAccessIntro": "Sign-in only shares your name and email. YouTube access is granted separately, below — each is optional and revocable anytime in your Google Account.",
"readTitle": "Read subscriptions (your feed)",
"readGrantedHint": "Granted. Siftlode reads the channels you follow to build your feed. It never changes your YouTube account with this.",
"readEnableHint": "Read-only access to your subscriptions — required to build your feed. Siftlode can't modify anything with it.",
"writeTitle": "Unsubscribe (write)",
"writeGrantedHint": "Granted. You can unsubscribe from channels on YouTube from inside Siftlode. It only writes when you ask it to.",
"writeEnableHint": "Lets Siftlode unsubscribe from channels on YouTube for you. Optional — skip it to stay read-only.",
"granted": "Granted",
"enable": "Enable",
"enableHint": "Redirects to Google to grant the permission. Google shows an 'unverified app' notice — that's expected; click Advanced → Go to Siftlode.",
"walkMeThrough": "Walk me through setup"
},
"invites": {
"title": "Access requests",
"noPending": "No pending requests.",
"addPlaceholder": "Add an email directly…",
"add": "Add",
"decided": "{{count}} decided",
"approved": "Approved — they can sign in now",
"approveFailed": "Approve failed",
"denyFailed": "Deny failed",
"addedToWhitelist": "Added to the whitelist",
"addFailed": "Couldn't add that email",
"requested": "requested {{time}}",
"approveHint": "Approve — whitelist this email and email them they're in.",
"approve": "Approve",
"denyHint": "Deny this request.",
"deny": "Deny"
}
}

View file

@ -0,0 +1,65 @@
{
"filters": "Filters",
"activeCount": "{{count}} active",
"clearAll": "Clear all",
"resetDefaults": "Reset to defaults",
"customize": "Customize sidebar",
"done": "Done",
"editHint": "Drag to reorder · eye to show/hide",
"channel": "Channel",
"loading": "Loading…",
"thisChannel": "This channel",
"channelCount": "{{count}} channel",
"channelCount_other": "{{count}} channels",
"dragToReorder": "Drag to reorder",
"showWidget": "Show widget",
"hideWidget": "Hide widget",
"expand": "Expand",
"collapse": "Collapse",
"any": "Any",
"all": "All",
"tagModeTooltip": "Match any vs all selected tags",
"custom": "Custom",
"from": "From",
"to": "To",
"clearDates": "clear dates",
"widget": {
"show": "Show",
"sort": "Sort",
"date": "Upload date",
"content": "Content type",
"language": "Language",
"topic": "Topic"
},
"show": {
"unwatched": "Unwatched",
"in_progress": "In progress",
"all": "All",
"watched": "Watched",
"saved": "Saved",
"hidden": "Hidden"
},
"sort": {
"newest": "Newest",
"oldest": "Oldest",
"views": "Most viewed",
"duration_desc": "Longest",
"duration_asc": "Shortest",
"title": "Name (AZ)",
"subscribers": "Channel subscribers",
"priority": "Channel priority",
"shuffle": "Surprise me"
},
"content": {
"normal": "Normal",
"shorts": "Shorts",
"live": "Live / Upcoming"
},
"datePreset": {
"24h": "24h",
"1week": "1 week",
"1month": "1 month",
"6months": "6 months",
"1year": "1 year"
}
}

View file

@ -0,0 +1,13 @@
{
"title": "API quota usage",
"rangeDays": "{{count}}d",
"introBefore": "YouTube Data API units attributed by who triggered the spend. ",
"introSystem": "System",
"introAfter": " is shared background work (scheduled backfill, enrichment, resync) that isn't tied to one person.",
"loading": "Loading…",
"noData": "No data.",
"dailyTotal": "Daily total ({{count}}d · {{units}} units)",
"noUsageInRange": "No usage in this range.",
"dailyTooltip": "{{day}}: {{units}} units",
"system": "System (background)"
}

View file

@ -0,0 +1,10 @@
{
"justNow": "just now",
"secondsAgo": "{{count}}s ago",
"minutesAgo": "{{count}} min ago",
"hoursAgo": "{{count}}h ago",
"daysAgo": "{{count}}d ago",
"weeksAgo": "{{count}} wk ago",
"monthsAgo": "{{count}} mo ago",
"yearsAgo": "{{count}} yr ago"
}