Signed client-side session cookies had no server-side kill switch: logout + password reset couldn't evict a stolen/copied cookie (valid until expiry). Add User.session_epoch (migration 0053), record it in the cookie at every login, and reject in current_user any cookie whose recorded epoch is behind the account's current one. - Bump the epoch on: password reset (kills ALL sessions — a reset is a compromise response), password change + a new 'Log out other sessions' action (both re-stamp the CURRENT cookie so the caller stays signed in, evicting only the others). - Per-account epoch map in the session so one account's revocation doesn't evict the other signed-in accounts in the same browser wallet. - Missing epoch (pre-SA4 cookie) is treated as 0, so the first bump revokes grandfathered sessions too. - New POST /auth/logout-others + a Settings → Account 'Active sessions' button (trilingual).
146 lines
7.4 KiB
JSON
146 lines
7.4 KiB
JSON
{
|
|
"title": "Settings",
|
|
"tabs": {
|
|
"appearance": "Appearance",
|
|
"notifications": "Notifications",
|
|
"account": "Account"
|
|
},
|
|
"plexSync": {
|
|
"title": "Plex watch sync",
|
|
"intro": "Sync your watched / in-progress status between Plex and Siftlode. Turning this on imports your existing Plex history now (Plex wins); ongoing two-way sync follows.",
|
|
"toggle": "Two-way sync with Plex",
|
|
"toggleHint": "Uses this server's Plex account. Turning it on imports your Plex watch history once.",
|
|
"importNow": "Import from Plex now",
|
|
"imported": "Imported from Plex: {{watched}} watched, {{in_progress}} in progress.",
|
|
"lastSync": "Last import: {{when}}",
|
|
"never": "Not imported yet",
|
|
"importing": "Importing your Plex watch history…"
|
|
},
|
|
"save": {
|
|
"unsaved": "Unsaved changes",
|
|
"saving": "Saving…",
|
|
"saved": "Settings saved",
|
|
"failed": "Couldn't save",
|
|
"save": "Save",
|
|
"discard": "Discard"
|
|
},
|
|
"unsaved": {
|
|
"title": "Unsaved changes",
|
|
"message": "You have unsaved settings. Discard them and leave?",
|
|
"discard": "Discard changes"
|
|
},
|
|
"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."
|
|
},
|
|
"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",
|
|
"dangerZone": "Danger zone",
|
|
"deleteHint": "Permanently delete your account and all your data — subscriptions, tags, watch history, playlists and settings. This can't be undone.",
|
|
"deleteAccount": "Delete my account",
|
|
"deleteTitle": "Delete your account?",
|
|
"deleteConfirm": "This permanently erases your account and all your data (subscriptions, tags, watch/saved/hidden state, playlists, settings). It can't be undone.",
|
|
"deleteConfirmButton": "Delete everything",
|
|
"demoNotice": "This is the shared demo account. It has no YouTube connection — you can browse the whole shared library, filter and sort it, build playlists and try things out, but nothing touches a real YouTube account.",
|
|
"signInMethods": "Sign-in methods",
|
|
"googleLink": {
|
|
"title": "Google account",
|
|
"connected": "Connected",
|
|
"connectedHint": "Your Google account is linked. You can sign in with Google and grant YouTube access below.",
|
|
"connectHint": "Link a Google account to sign in with Google and to enable YouTube access for your feed.",
|
|
"connect": "Connect Google",
|
|
"linked": "Google account linked.",
|
|
"conflict": "That Google account is already linked to a different Siftlode account.",
|
|
"mismatch": "That's a different Google account than the one already linked here. Sign in with the linked one.",
|
|
"error": "Couldn't link the Google account. Please try again."
|
|
},
|
|
"password": {
|
|
"title": "Password",
|
|
"setHint": "A password is set. You can sign in with your email and password.",
|
|
"unsetHint": "No password yet — set one to sign in with your email instead of (or as well as) Google.",
|
|
"set": "Set password",
|
|
"change": "Change password",
|
|
"current": "Current password",
|
|
"new": "New password (min. 10 characters)",
|
|
"save": "Save password",
|
|
"saving": "Saving…",
|
|
"saved": "Password updated for {{email}}.",
|
|
"failed": "Couldn't update the password."
|
|
},
|
|
"sessions": {
|
|
"title": "Active sessions",
|
|
"hint": "Sign this account out everywhere else — handy if you left it signed in on a shared or lost device. You stay signed in here.",
|
|
"action": "Log out other sessions",
|
|
"confirmTitle": "Log out other sessions?",
|
|
"confirmBody": "This signs you out of every other browser and device. You'll stay signed in here.",
|
|
"done": "Signed out of all other sessions.",
|
|
"failed": "Couldn't sign out other sessions. Please try again."
|
|
}
|
|
},
|
|
"demo": {
|
|
"title": "Demo access",
|
|
"intro": "Emails here can enter the shared demo account straight from the login page — no Google sign-in. They just type the address into the field and are let in after a moment.",
|
|
"addPlaceholder": "Whitelist an email for demo…",
|
|
"add": "Add",
|
|
"added": "{{email}} added to the demo whitelist",
|
|
"addFailed": "Couldn't add that email",
|
|
"removeFailed": "Couldn't remove that email",
|
|
"remove": "Remove",
|
|
"removeHint": "Remove this email from the demo whitelist.",
|
|
"addedBy": "added by {{who}}",
|
|
"reset": "Reset demo state",
|
|
"resetHint": "Wipe the demo account's playlists, hidden/watched/saved videos and settings, then re-seed a few sample playlists.",
|
|
"resetTitle": "Reset demo state?",
|
|
"resetConfirm": "This clears everything the demo account has accumulated — playlists, hidden/watched/saved videos and settings — and re-seeds a few sample playlists. It can't be undone.",
|
|
"resetDone": "Demo reset — {{count}} sample playlist(s) seeded",
|
|
"resetFailed": "Couldn't reset the demo account"
|
|
},
|
|
"invites": {
|
|
"title": "Access requests",
|
|
"noPending": "No pending requests.",
|
|
"addPlaceholder": "Add an email directly…",
|
|
"add": "Add",
|
|
"decided": "{{count}} decided",
|
|
"approved": "Approved {{email}} — they can sign in now",
|
|
"approveFailed": "Approve failed",
|
|
"denyFailed": "Deny failed",
|
|
"addedToWhitelist": "{{email}} 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"
|
|
}
|
|
}
|