feat(design): the Copy rule — names, verbs, dates and units (E28 S3g)

The owner's picks (all the recommended options) become DESIGN.md's Copy
chapter and a sweep of both languages (742 values, no key drift):

- One name per feature: the rail's label, which may shorten it (Alerts
  -> Notifications, Fiókok -> Felhasználók, Download Center -> Downloads,
  Channel manager -> Channels); one "sync from YouTube" verb.
- Four verbs, four meanings (Delete / Remove / Dismiss / Discard, and
  their Hungarian pairs), checked against what each action does.
- Sentence case, the app's places capitalised; "…" on a command that
  asks first (a popover or form title got its own key without it);
  one-sentence messages without a stop or a tick, "Couldn't …"; curly
  quotes.
- Dates only from lib/format.ts, in the UI language: formatDateTime
  names the month and stops at the minute (it printed "9/27/2026,
  8:30:14 AM"), formatDateTimeSec for the log, the audit and the
  recorder; ui/RelativeTime pairs "3d ago" with the exact minute on
  hover. local/date-format refuses toLocale*String and Intl date
  formatters elsewhere.
- Units from Intl, short (formatUnit): runtimes, totals, the Scheduler
  countdown, the stats range, the player's tap skip. "2h 13m" was
  English in both languages.
- src/i18n/copyRule.test.ts holds the machine-readable part (quotes,
  "...", ticks, bare unit letters).
This commit is contained in:
2026-09-27 05:46:17 +02:00
parent 2c65edfffd
commit bd6aaa1e43
100 changed files with 1193 additions and 964 deletions
+137 -137
View File
@@ -1,57 +1,57 @@
{
"title": "Couldn't complete that",
"generic": "The server couldn't carry out that action. Please try again.",
"demo": "This isn't available in the demo account.",
"demo": "This isn't available in the demo account",
"validation": "Some of the values you entered weren't valid. Please check them and try again.",
"tooLarge": "That was too large for the server to accept.",
"tooLarge": "That was too large for the server to accept",
"server": "Server error",
"syncCooldown": "You've synced a few times just now — give it a few minutes. The YouTube budget is shared with everyone on this instance, and background syncing keeps running either way.",
"notFound": {
"generic": "This item doesn't exist any more.",
"view": "This view doesn't exist any more.",
"tag": "This tag doesn't exist any more.",
"pin": "This isn't on your comfort shelf any more.",
"playlist": "This playlist doesn't exist any more.",
"move_target": "The playlist you're moving it to doesn't exist any more.",
"video": "This video isn't in the library any more.",
"invite": "This invite doesn't exist any more.",
"user": "This user doesn't exist any more.",
"channel": "This channel isn't in the library any more.",
"subscription": "You're not subscribed to this channel any more.",
"recipient": "There's no user with that email address.",
"job": "This job doesn't exist any more.",
"download": "This download doesn't exist any more.",
"debug_session": "This debug session doesn't exist any more.",
"screenshot": "This screenshot doesn't exist any more.",
"link": "This share link doesn't exist any more.",
"notification": "This notification doesn't exist any more.",
"account": "That account no longer exists.",
"collection": "This collection doesn't exist any more.",
"config_key": "There's no such setting.",
"file": "The file is no longer available.",
"filmstrip": "There's no filmstrip for this.",
"host": "This host isn't on the list any more.",
"image": "There's no image for this.",
"item": "This item doesn't exist any more.",
"library": "This library doesn't exist any more.",
"plex_item": "This Plex item isn't in the library any more.",
"poster": "There's no poster for this.",
"profile": "This download profile doesn't exist any more.",
"season": "This season isn't in the library any more.",
"show": "This show isn't in the library any more."
"generic": "This item doesn't exist any more",
"view": "This view doesn't exist any more",
"tag": "This tag doesn't exist any more",
"pin": "This isn't on your comfort shelf any more",
"playlist": "This playlist doesn't exist any more",
"move_target": "The playlist you're moving it to doesn't exist any more",
"video": "This video isn't in the library any more",
"invite": "This invite doesn't exist any more",
"user": "This user doesn't exist any more",
"channel": "This channel isn't in the library any more",
"subscription": "You're not subscribed to this channel any more",
"recipient": "There's no user with that email address",
"job": "This job doesn't exist any more",
"download": "This download doesn't exist any more",
"debug_session": "This debug session doesn't exist any more",
"screenshot": "This screenshot doesn't exist any more",
"link": "This share link doesn't exist any more",
"notification": "This notification doesn't exist any more",
"account": "That account no longer exists",
"collection": "This collection doesn't exist any more",
"config_key": "There's no such setting",
"file": "The file is no longer available",
"filmstrip": "There's no filmstrip for this",
"host": "This host isn't on the list any more",
"image": "There's no image for this",
"item": "This item doesn't exist any more",
"library": "This library doesn't exist any more",
"plex_item": "This Plex item isn't in the library any more",
"poster": "There's no poster for this",
"profile": "This download profile doesn't exist any more",
"season": "This season isn't in the library any more",
"show": "This show isn't in the library any more"
},
"admin": {
"not_allowed": "Their address isn't approved any more. Add it to the whitelist first.",
"not_applicable": "This account has nothing left to confirm.",
"not_approved": "Only an approved address can be invited.",
"not_applicable": "This account has nothing left to confirm",
"not_approved": "Only an approved address can be invited",
"email_off": "No mail was sent: this instance has no working SMTP setup. Check Configuration → Email / SMTP.",
"send_failed": "The mail couldn't be sent: the mail server was unreachable, or it refused the login or the message. Check Configuration → Email / SMTP, then try again.",
"demo": "The demo account is shared, so it can't get a set-password link."
"demo": "The demo account is shared, so it can't get a set-password link"
},
"reason": {
"unreachable": "The server couldn't be reached. Try again.",
"forbidden": "You don't have permission to do that.",
"rateLimited": "Too many requests at once — wait a moment and try again.",
"forbidden": "You don't have permission to do that",
"rateLimited": "Too many requests at once — wait a moment and try again",
"unexpected": "Something went wrong in the app. Try again."
},
"writeFailed": {
@@ -70,12 +70,12 @@
"delete": "Couldn't delete the download",
"redownload": "Couldn't start the download again"
},
"removeShared": "Couldn't remove the shared item",
"removeShared": "Couldn't remove the shared download",
"downloadDetails": "Couldn't save the download's details",
"enqueueDownload": "Couldn't queue the download",
"downloadProfileSave": "Couldn't save the download profile",
"downloadProfileDelete": "Couldn't delete the download profile",
"downloadCookiesRemove": "Couldn't remove those cookies",
"downloadCookiesRemove": "Couldn't delete those cookies",
"notifRead": "Couldn't mark the notification as read",
"notifReadAll": "Couldn't mark the notifications as read",
"notifDismiss": "Couldn't dismiss the notification",
@@ -106,8 +106,8 @@
"body": "Couldn't reach the server — it may be restarting. This notice will clear once the connection is back."
},
"boundary": {
"title": "Something went wrong.",
"subtitle": "The app hit an unexpected error.",
"title": "Something went wrong",
"subtitle": "The app hit an unexpected error",
"reload": "Reload",
"notifTitle": "Something broke",
"notifMessage": "Unexpected error"
@@ -118,111 +118,111 @@
"unavailable": "YouTube didn't accept that just now. Please try again."
},
"refused": {
"account_no_access": "That account no longer has access.",
"account_signed_out": "That account was signed out elsewhere — sign in again.",
"account_suspended": "That account is suspended.",
"address_has_account": "That address already has an account — delete the account instead.",
"admin_first": "Create the admin account first.",
"admin_only": "Only an admin can do that.",
"already_set_up": "This instance is already set up.",
"already_subscribed": "You're already subscribed to this channel.",
"channel_blocked": "You've blocked this channel.",
"collection_fields_required": "A title and an item are both needed.",
"collection_pending_sync": "The collection changed in Plex, but refreshing the local copy failed — it catches up on the next sync.",
"collection_read_only": "This collection is read-only.",
"collection_smart": "Smart and auto-list collections can't be made editable.",
"demo_activate": "The demo account can't be activated.",
"demo_delete": "The demo account can't be deleted.",
"demo_role": "The demo account's role can't be changed.",
"demo_suspend": "The demo account can't be suspended.",
"download_not_ready": "This download isn't ready.",
"edit_unfinished": "Only a finished download can be edited.",
"filters_not_object": "The view's filters are malformed.",
"account_no_access": "That account no longer has access",
"account_signed_out": "That account was signed out elsewhere — sign in again",
"account_suspended": "That account is suspended",
"address_has_account": "That address already has an account — delete the account instead",
"admin_first": "Create the admin account first",
"admin_only": "Only an admin can do that",
"already_set_up": "This instance is already set up",
"already_subscribed": "You're already subscribed to this channel",
"channel_blocked": "You've blocked this channel",
"collection_fields_required": "A title and an item are both needed",
"collection_pending_sync": "The collection changed in Plex, but refreshing the local copy failed — it catches up on the next sync",
"collection_read_only": "This collection is read-only",
"collection_smart": "Smart and auto-list collections can't be made editable",
"demo_activate": "The demo account can't be activated",
"demo_delete": "The demo account can't be deleted",
"demo_role": "The demo account's role can't be changed",
"demo_suspend": "The demo account can't be suspended",
"download_not_ready": "This download isn't ready",
"edit_unfinished": "Only a finished download can be edited",
"filters_not_object": "The view's filters are malformed",
"google_no_userinfo": "Google didn't send back the account details. Try signing in again.",
"image_fetch_failed": "The image couldn't be loaded.",
"image_host": "Images from that address aren't loaded.",
"invalid_cursor": "The list position is out of date — reload the page.",
"invalid_email": "Enter a valid email address.",
"invalid_interval": "The interval must be between {{min}} and {{max}} minutes.",
"invalid_link": "That doesn't look like a valid video link.",
"invalid_role": "Unknown role.",
"invalid_status": "Unknown status.",
"invite_decided": "This request has already been decided.",
"job_running": "This job is already running.",
"last_active_admin": "This is the only active admin — make someone else admin first.",
"last_admin": "This is the only admin — make someone else admin first.",
"link_gone": "This link is no longer available.",
"link_required": "A video link or id is required.",
"media_missing": "The media file isn't on disk.",
"message_empty": "The message is empty.",
"message_rate": "Too many messages — slow down a moment.",
"message_self": "You can't message yourself.",
"message_too_long": "The message is too long.",
"messaging_set_up": "Secure messaging is already set up.",
"name_required": "A name is required.",
"no_encryption_key": "Set TOKEN_ENCRYPTION_KEY to store secrets in the database.",
"no_messaging": "That member hasn't set up messaging yet.",
"no_profile": "There's no download profile to use.",
"no_subscription_id": "There's no YouTube subscription on record yet — sync your subscriptions first.",
"not_authenticated": "You're not signed in.",
"not_ready": "Not available yet.",
"not_redownloadable": "This item can't be downloaded again.",
"not_your_account": "That's not an account you've signed into here.",
"only_admin_delete": "You're the only admin — make someone else admin before deleting your account.",
"own_delete": "Delete your own account in Settings → Account.",
"own_role": "You can't change your own role.",
"own_suspend": "You can't suspend your own account.",
"plex_disabled": "The Plex module is turned off.",
"plex_not_configured": "Plex isn't set up — add the server address and token in Configuration → Plex.",
"image_fetch_failed": "Couldn't load the image",
"image_host": "Images from that address aren't loaded",
"invalid_cursor": "The list position is out of date — reload the page",
"invalid_email": "Enter a valid email address",
"invalid_interval": "The interval must be between {{min}} and {{max}} minutes",
"invalid_link": "That doesn't look like a valid video link",
"invalid_role": "Unknown role",
"invalid_status": "Unknown status",
"invite_decided": "This request has already been decided",
"job_running": "This job is already running",
"last_active_admin": "This is the only active admin — make someone else admin first",
"last_admin": "This is the only admin — make someone else admin first",
"link_gone": "This link is no longer available",
"link_required": "A video link or id is required",
"media_missing": "The media file isn't on disk",
"message_empty": "The message is empty",
"message_rate": "Too many messages — slow down a moment",
"message_self": "You can't message yourself",
"message_too_long": "The message is too long",
"messaging_set_up": "Secure messaging is already set up",
"name_required": "A name is required",
"no_encryption_key": "Set TOKEN_ENCRYPTION_KEY to store secrets in the database",
"no_messaging": "That member hasn't set up messaging yet",
"no_profile": "There's no download profile to use",
"no_subscription_id": "There's no YouTube subscription on record yet — sync your subscriptions first",
"not_authenticated": "You're not signed in",
"not_ready": "Not available yet",
"not_redownloadable": "This download can't be downloaded again",
"not_your_account": "That's not an account you've signed into here",
"only_admin_delete": "You're the only admin — make someone else admin before deleting your account",
"own_delete": "Delete your own account in Settings → Account",
"own_role": "You can't change your own role",
"own_suspend": "You can't suspend your own account",
"plex_disabled": "The Plex module is turned off",
"plex_not_configured": "Plex isn't set up — add the server address and token in Configuration → Plex",
"plex_unreachable": "The Plex server didn't answer. Check the address and the token, then try again.",
"plex_write_failed": "Plex didn't accept the change. Try again.",
"profile_name_required": "A profile name is required.",
"quota_reserve": "Today's shared YouTube budget is down to its reserve — try again later.",
"profile_name_required": "A profile name is required",
"quota_reserve": "Today's shared YouTube budget is down to its reserve — try again later",
"quota_spent": "Today's shared YouTube budget is spent. Try again tomorrow.",
"recipient_required": "A recipient email is required.",
"registration_closed": "Registration is currently closed.",
"recipient_required": "A recipient email is required",
"registration_closed": "Registration is currently closed",
"search_daily_limit": "You've reached today's YouTube search limit ({{limit}}). Try again tomorrow.",
"search_term_required": "Enter something to search for.",
"session_gone": "The playback session ended.",
"setting_not_a_choice": "Pick one of: {{choices}}.",
"setting_not_a_number": "Enter a whole number.",
"setting_too_high": "The value can be at most {{max}}.",
"setting_too_low": "The value must be at least {{min}}.",
"setup_token": "The setup token is missing or wrong.",
"share_own": "That's already your download.",
"share_password_required": "This link needs a password.",
"share_unfinished": "Only a finished download can be shared.",
"smtp_failed": "The email couldn't be sent — check the SMTP settings.",
"smtp_off": "SMTP isn't configured.",
"spec_not_object": "The profile's settings are malformed.",
"still_downloading": "This download is still in progress.",
"tag_name_taken": "You already have a tag named “{{name}}”.",
"title_required": "A title is required.",
"search_term_required": "Enter something to search for",
"session_gone": "The playback session ended",
"setting_not_a_choice": "Pick one of: {{choices}}",
"setting_not_a_number": "Enter a whole number",
"setting_too_high": "The value can be at most {{max}}",
"setting_too_low": "The value must be at least {{min}}",
"setup_token": "The setup token is missing or wrong",
"share_own": "That's already your download",
"share_password_required": "This link needs a password",
"share_unfinished": "Only a finished download can be shared",
"smtp_failed": "The email couldn't be sent — check the SMTP settings",
"smtp_off": "SMTP isn't configured",
"spec_not_object": "The profile's settings are malformed",
"still_downloading": "This download is still in progress",
"tag_name_taken": "You already have a tag named “{{name}}”",
"title_required": "A title is required",
"too_many_attempts": "Too many attempts. Try again shortly.",
"unknown_setting": "Unknown setting: {{key}}",
"value_missing": "A value is required.",
"video_gone": "This video is no longer available.",
"view_name_taken": "You already have a view named “{{name}}”.",
"watch_sync_off": "Turn on Plex watch-sync first.",
"weak_password": "The password must be at least {{min}} characters.",
"wrong_password": "The current password is incorrect.",
"wrong_share_password": "Wrong password.",
"youtube_editing_off": "Turn on YouTube editing in Settings first.",
"youtube_read_scope": "Connect your YouTube account (read access) first.",
"value_missing": "A value is required",
"video_gone": "This video is no longer available",
"view_name_taken": "You already have a view named “{{name}}”",
"watch_sync_off": "Turn on Plex watch-sync first",
"weak_password": "The password must be at least {{min}} characters",
"wrong_password": "The current password is incorrect",
"wrong_share_password": "Wrong password",
"youtube_editing_off": "Turn on YouTube editing in Settings first",
"youtube_read_scope": "Connect your YouTube account (read access) first",
"debug_session_conflict": "A recording just started in another tab. Reload the page.",
"issue_already_sent": "This report was already sent; its draft can no longer change.",
"issue_title_required": "Give the report a title before you send it.",
"screenshot_already_sent": "The report was already sent; this session takes no more images.",
"screenshot_too_many": "A session can have at most {{limit}} screenshots.",
"screenshot_empty": "The image is empty.",
"screenshot_not_an_image": "This is not a PNG, JPEG or WebP image.",
"issue_already_sent": "This report was already sent; its draft can no longer change",
"issue_title_required": "Give the report a title before you send it",
"screenshot_already_sent": "The report was already sent; this session takes no more images",
"screenshot_too_many": "A session can have at most {{limit}} screenshots",
"screenshot_empty": "The image is empty",
"screenshot_not_an_image": "This is not a PNG, JPEG or WebP image",
"issues_not_configured": "No issue tracker is set up. Download the report as a zip instead.",
"issues_unreachable": "The issue tracker isn't answering. Try again later.",
"issues_bad_token": "The issue tracker refused the token, or it lacks the permission.",
"issues_repo_not_found": "The configured issue repository was not found.",
"issues_bad_token": "The issue tracker refused the token, or it lacks the permission",
"issues_repo_not_found": "The configured issue repository was not found",
"issues_public_repo": "The issue repository is public. Because of the log, reports go only to a private one.",
"issues_disabled": "The issue repository has its issue tracker switched off.",
"issues_rejected": "The issue tracker refused the request.",
"issues_disabled": "The issue repository has its issue tracker switched off",
"issues_rejected": "The issue tracker refused the request",
"issues_rate_limited": "You can send at most {{limit}} reports an hour. Try again later.",
"debug_session_rate_limited": "You can start at most {{limit}} new recordings an hour. Try again later.",
"export_rate_limited": "Too many downloads in a row. Wait a minute.",