screentinker/frontend/js/i18n/en.js
ScreenTinker 8e7a093150 i18n: extract all strings, add 6 language translations, restructure i18n module
Session 1 of 2 of the i18n rollout.

- Split i18n module into per-language files under frontend/js/i18n/ so a
  translator can edit one language without touching the others.
- Add Portuguese (pt) and seed Hindi (hi). Hindi is intentionally a skeleton
  -- 0 keys, full English fallback -- because we have an active Indian user
  and would rather ship "no Hindi" than ship machine-quality Hindi that
  could read as unprofessional or get formality/gender register wrong.
- 183 keys, 100% parity across en/es/fr/de/pt; native review still
  recommended before publicizing as "fully supported".
- Add t(key, vars) variable substitution and tn(keyBase, n, vars) plural
  helper for _one/_other key pairs.
- setLanguage() now triggers a CustomEvent + HashChangeEvent so the
  existing hash router naturally re-renders the current view, plus a
  subscriber pattern for nav labels rendered once outside the router.
- Wire t() into 3 high-traffic views end-to-end: dashboard, login,
  content-library. Sidebar nav labels in app.js update on language change.
- The remaining 16 views still ship with hardcoded English; they will be
  wired in session 2. The t() lookup is robust against unwired views, so
  the dashboard works in 5 languages while clicking into e.g. Schedule
  still shows English. No regressions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 19:25:22 -05:00

209 lines
9.6 KiB
JavaScript

// English translations. This file is the source of truth for keys —
// every other locale should mirror its keys (or fall back to en).
export default {
// Nav (sidebar)
'nav.displays': 'Displays',
'nav.content': 'Content',
'nav.playlists': 'Playlists',
'nav.layouts': 'Layouts',
'nav.widgets': 'Widgets',
'nav.schedule': 'Schedule',
'nav.walls': 'Video Walls',
'nav.reports': 'Reports',
'nav.kiosk': 'Kiosk',
'nav.designer': 'Designer',
'nav.activity': 'Activity',
'nav.teams': 'Teams',
'nav.help': 'Help',
'nav.settings': 'Settings',
'nav.subscription': 'Subscription',
'nav.admin': 'Admin',
// Common (shared across views)
'common.save': 'Save',
'common.cancel': 'Cancel',
'common.delete': 'Delete',
'common.edit': 'Edit',
'common.done': 'Done',
'common.loading': 'Loading...',
'common.connected': 'Connected',
'common.disconnected': 'Disconnected',
'common.never': 'Never',
'common.just_now': 'Just now',
'common.minutes_ago': '{n}m ago',
'common.hours_ago': '{n}h ago',
'common.days_ago': '{n}d ago',
'common.unknown': 'Unknown',
// Auth (login view)
'auth.sign_in': 'Sign In',
'auth.sign_out': 'Sign out',
'auth.create_account': 'Create Account',
'auth.create_admin_account': 'Create Admin Account',
'auth.email': 'Email',
'auth.password': 'Password',
'auth.name': 'Name',
'auth.placeholder_email': 'you@example.com',
'auth.placeholder_password': '••••••••',
'auth.placeholder_name': 'Your name',
'auth.placeholder_register_password': 'At least 6 characters',
'auth.subtitle_setup': 'Create your admin account to get started',
'auth.subtitle_signin': 'Sign in to manage your displays',
'auth.trial_notice': 'New accounts get a 14-day free Pro trial',
'auth.divider_or': 'OR',
'auth.signin_google': 'Sign in with Google',
'auth.signin_microsoft': 'Sign in with Microsoft',
'auth.back_to_signin': 'Back to Sign In',
'auth.support_access': 'Support Access',
'auth.support_token_placeholder': 'Paste support token',
'auth.support_authenticate': 'Authenticate with Support Token',
'auth.terms': 'Terms of Service',
'auth.privacy': 'Privacy Policy',
'auth.error_email_password_required': 'Email and password required',
'auth.error_password_min_6': 'Password must be at least 6 characters',
'auth.error_login_failed': 'Login failed',
'auth.error_registration_failed': 'Registration failed',
'auth.error_paste_support_token': 'Paste a support token',
'auth.error_support_failed': 'Support login failed',
'auth.error_google_failed': 'Google sign-in failed',
'auth.error_microsoft_failed': 'Microsoft sign-in failed',
// Dashboard
'dashboard.title': 'Displays',
'dashboard.subtitle': 'Manage your remote displays',
'dashboard.help_tip': 'Your paired display devices. Green = online, red = offline. Click a device to manage its playlist, view telemetry, or use remote control.',
'dashboard.add': 'Add Display',
'dashboard.create_group': '+ Group',
'dashboard.search': 'Search displays...',
'dashboard.all_status': 'All Status',
'dashboard.online': 'Online',
'dashboard.offline': 'Offline',
'dashboard.awaiting_pairing': 'Awaiting Pairing',
'dashboard.no_preview': 'No preview available',
'dashboard.total_displays': 'Total Displays',
'dashboard.ungrouped': 'Ungrouped',
'dashboard.no_displays': 'No displays yet',
'dashboard.no_displays_desc': 'Install the ScreenTinker app on your TV and pair it using the button above.',
'dashboard.failed_to_load': 'Failed to load displays',
'dashboard.unknown_playlist': 'Unknown playlist',
'dashboard.mixed_playlists': 'Mixed playlists',
'dashboard.playlist_label': 'Playlist: {name}',
'dashboard.devices_count_one': '{n} device',
'dashboard.devices_count_other': '{n} devices',
'dashboard.online_count': '{n} online',
'dashboard.set_playlist_placeholder': 'Set Playlist...',
'dashboard.send_command_placeholder': 'Send Command...',
'dashboard.manage': 'Manage',
'dashboard.manage_tooltip': 'Add/remove devices',
'dashboard.delete_group_tooltip': 'Delete group',
'dashboard.no_devices_in_group': 'No devices in this group. Click Manage to add some.',
'dashboard.manage_group_subtitle': 'Check devices to add them to this group',
'dashboard.draft_suffix': '(draft)',
// Group commands
'dashboard.cmd.screen_on': 'Screen On',
'dashboard.cmd.screen_off': 'Screen Off',
'dashboard.cmd.restart_app': 'Restart App',
'dashboard.cmd.check_update': 'Check Update',
'dashboard.cmd.reboot': 'Reboot',
'dashboard.cmd.shutdown': 'Shutdown',
// Dashboard prompts/confirms
'dashboard.prompt_group_name': 'Group name:',
'dashboard.error_pairing_code': 'Enter a valid 6-digit pairing code',
'dashboard.confirm_add_to_group': '{name} is already in: {groups}\n\nAdd it to "{target}" too?',
'dashboard.confirm_assign_playlist': 'Assign playlist "{playlist}" to all devices in "{group}"?',
'dashboard.confirm_destructive_command': '{cmd} all {n} devices in "{group}"?\n\nThis cannot be undone.',
'dashboard.confirm_delete_group': 'Delete this group? Devices will not be affected.',
// Dashboard toasts
'dashboard.toast.display_paired': 'Display paired successfully!',
'dashboard.toast.group_created': 'Group created',
'dashboard.toast.group_deleted': 'Group deleted',
'dashboard.toast.already_in_group': '{name} is already in {group}',
'dashboard.toast.moved_device': 'Moved {name} to {group}',
'dashboard.toast.removed_device_one': 'Removed {name} from 1 group',
'dashboard.toast.removed_device_other': 'Removed {name} from {n} groups',
'dashboard.toast.playlist_assigned_one': 'Playlist assigned to 1 device',
'dashboard.toast.playlist_assigned_other': 'Playlist assigned to {n} devices',
'dashboard.toast.command_sent': '{cmd} sent to {sent}/{total} devices',
'dashboard.toast.command_sent_with_offline': '{cmd} sent to {sent}/{total} devices ({offline} offline)',
// Content library
'content.title': 'Content Library',
'content.subtitle': 'Upload and manage your media files',
'content.help_tip': 'Upload videos and images here. Select multiple files for bulk upload. Use Remote URL to stream from external sources. Click a thumbnail to preview.',
'content.drop': 'Drop files here or click to upload',
'content.upload_hint': 'Supports MP4, WebM, AVI, MKV, JPEG, PNG, GIF, WebP',
'content.upload_progress': 'Uploading...',
'content.upload_progress_named': 'Uploading {name}...',
'content.upload_progress_named_pct': 'Uploading {name}... {pct}%',
// Remote URL panel
'content.remote_url': 'Remote URL',
'content.remote_desc': 'Stream directly from a URL. Saves local bandwidth.',
'content.remote_url_placeholder': 'https://example.com/video.mp4',
'content.remote_name_placeholder': 'Display name (optional)',
'content.remote_add_btn': 'Add Remote URL',
// YouTube panel
'content.youtube': 'YouTube',
'content.youtube_desc': 'Embed a YouTube video on your displays.',
'content.youtube_url_placeholder': 'https://youtube.com/watch?v=...',
'content.youtube_name_placeholder': 'Display name (optional)',
'content.youtube_add_btn': 'Add YouTube Video',
// Search / folders
'content.search_placeholder': 'Search content...',
'content.new_folder_btn': '+ New Folder',
'content.breadcrumb_root': 'All Content',
'content.rename_btn': 'Rename',
'content.delete_folder_btn': 'Delete folder',
'content.prompt_folder_name': 'Folder name:',
'content.prompt_rename_folder': 'Rename folder:',
'content.confirm_delete_folder': 'Delete this folder? Content inside moves back to the root level. Subfolders will also be deleted.',
// Empty states
'content.empty_folder_title': 'This folder is empty',
'content.empty_folder_desc': 'Drag content here, or use the Move action.',
'content.no_content': 'No content yet',
'content.no_content_desc': 'Upload videos and images to get started.',
'content.failed_to_load': 'Failed to load content',
// Item type labels
'content.type_youtube': 'YouTube',
'content.type_remote': 'Remote URL',
'content.type_remote_short': 'Remote',
'content.type_video': 'Video',
'content.type_image': 'Image',
// Item action buttons
'content.btn_edit': 'Edit',
'content.btn_delete': 'Delete',
'content.btn_confirm_delete': 'Confirm Delete?',
'content.btn_deleting': 'Deleting...',
// Edit modal
'content.edit_modal_title': 'Edit Content',
'content.label_filename': 'Filename / Display Name',
'content.label_remote_url_field': 'Remote URL',
'content.label_mime_type': 'MIME Type',
'content.label_folder': 'Folder',
'content.label_replace_file': 'Replace File',
'content.replace_file_hint': 'Leave empty to keep current file',
'content.folder_root_option': '— Root —',
'content.save_changes': 'Save Changes',
// MIME options
'content.mime.video_mp4': 'Video (MP4)',
'content.mime.video_webm': 'Video (WebM)',
'content.mime.image_jpeg': 'Image (JPEG)',
'content.mime.image_png': 'Image (PNG)',
'content.mime.image_gif': 'Image (GIF)',
'content.mime.image_webp': 'Image (WebP)',
// Content errors / toasts
'content.error_enter_url': 'Enter a URL',
'content.error_enter_youtube_url': 'Enter a YouTube URL',
'content.error_update_failed': 'Update failed',
'content.toast.remote_added': 'Remote content added',
'content.toast.youtube_added': 'YouTube video added',
'content.toast.deleted': 'Content deleted',
'content.toast.updated': 'Content updated',
'content.toast.uploaded_named': '{name} uploaded successfully',
'content.toast.upload_failed_named': 'Failed to upload {name}: {error}',
'content.toast.folder_created_named': 'Folder "{name}" created',
'content.toast.folder_renamed': 'Folder renamed',
'content.toast.folder_deleted': 'Folder deleted',
'content.toast.moved': 'Moved',
'content.toast.moved_to_root': 'Moved to root',
};