diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/locales/en.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/locales/en.js | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js new file mode 100644 index 0000000..0e4427d --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -0,0 +1,392 @@ +/** + * English — the source catalogue. + * + * Every other locale is a translation of this file and carries the same keys. + * A value may be a string, or an object of Intl.PluralRules categories + * (`one`, `few`, `many`, `other`) when it is rendered with a count. + * + * Product vocabulary stays in English everywhere: `hub`, `node` and `GEK` name + * the CLI (`meshbay-node`), node.toml and the docs, so translating them would + * cut the interface off from everything an operator reads and types. + */ + +export default { + // Nav + 'nav.toggle_menu': 'Toggle menu', + 'nav.light_mode': 'Light mode', + 'nav.dark_mode': 'Dark mode', + 'nav.logout': 'Logout', + 'nav.login': 'Login', + + // Sidebar + 'sidebar.my_groups': 'My Groups', + 'sidebar.no_groups': 'No groups yet', + 'sidebar.discover': 'Discover', + 'sidebar.public_groups': 'Public groups', + + // Login + 'login.title': 'Login', + 'login.username': 'Username', + 'login.password': 'Password', + 'login.submit': 'Login', + 'login.loading': 'Logging in...', + 'login.no_account': 'No account?', + 'login.register_link': 'Register', + + // Register + 'register.title': 'Register', + 'register.username': 'Username', + 'register.email': 'Email', + 'register.password': 'Passphrase (min 12 chars)', + 'register.confirm': 'Confirm password', + 'register.submit': 'Register', + 'register.loading': 'Creating account...', + 'register.has_account': 'Already have an account?', + 'register.login_link': 'Login', + 'register.success_title': 'Account created', + 'register.success_msg': 'You can now log in with your credentials.', + 'register.go_login': 'Go to login', + 'register.err_mismatch': 'Passwords do not match', + 'register.err_min_len': { + one: 'Use at least {n} character', + other: 'Use at least {n} characters', + }, + 'register.err_too_weak': 'Too easy to guess. Your passphrase is what protects ' + + 'your keys where they are stored — a few unrelated words work well.', + 'register.strength': 'Strength: about {bits} bits. This protects the copy of ' + + 'your keys kept on the nodes you join, so it is worth getting right.', + + // Home + 'home.welcome': 'Welcome to MeshBay', + 'home.no_groups': 'You are not a member of any group yet.', + 'home.browse_prefix': 'Browse ', + 'home.browse_link': 'public groups', + 'home.browse_suffix': ' or ask a group admin to invite you.', + 'home.my_groups': 'My Groups', + + // Explore + 'explore.title': 'Public Groups', + 'explore.search': 'Search groups...', + 'explore.loading': 'Loading...', + 'explore.empty': 'No public groups available.', + 'explore.create_group': 'Create group', + 'explore.join': 'Join', + 'explore.member': 'Joined', + + // Group page + 'group.default_name': 'Group', + 'group.tab_files': 'Files', + 'group.tab_chat': 'Chat', + 'group.tab_members': 'Members', + 'group.filter': 'Filter files...', + 'group.col_name': 'Name', + 'group.col_size': 'Size', + 'group.col_type': 'Type', + 'group.col_date': 'Date', + 'group.empty_filter': 'No files match the filter', + 'group.empty_dir': 'This directory is empty', + 'group.download': 'Download', + 'group.play': 'Play', + 'group.offline_title': 'No nodes are currently online for this group.', + 'group.offline_hint': 'Files will appear when a node hosting this group connects.', + 'group.upload': 'Upload', + 'group.mkdir': 'New folder', + 'group.mkdir_prompt': 'Name of the new folder:', + 'group.view': 'View', + 'group.delete': 'Delete', + 'group.delete_confirm': 'Delete {name}?', + 'group.delete_group': 'Delete group', + 'group.delete_group_confirm': 'Permanently delete "{name}" and remove all members? Files on the node are preserved.', + 'group.err_transport': 'Transport module not loaded', + + // Status + 'status.idle': 'Idle', + 'status.discovering': 'Finding nodes...', + 'status.connecting': 'Connecting via WebRTC...', + 'status.fetching': 'Fetching index...', + 'status.files': { + one: '{n} file', + other: '{n} files', + }, + 'status.offline': 'No nodes online', + 'status.error': 'Connection failed', + + // Chat + 'chat.empty': 'No messages yet. Start the conversation!', + 'chat.placeholder': 'Type a message...', + 'chat.send': 'Send', + 'chat.attach': 'Attach file', + + // Video player + 'video.loading': 'Loading {name}...', + 'video.buffering': 'Buffering...', + 'video.close': 'Close (Esc)', + 'preview.pdf_fallback': 'This browser will not display the PDF inline. Download it instead — it was decrypted here either way.', + 'group.upload_indexing': 'indexing…', + 'video.err_transport': 'Transport not connected', + 'video.err_mse': 'Codec not supported for streaming: {codec}', + + // Settings + 'settings.title': 'Settings', + 'settings.coming_soon': 'Coming soon.', + 'settings.downloads': 'Downloads', + 'settings.dl_auto': 'Save automatically', + 'settings.dl_auto_hint': 'Files go straight into the folder you choose below, ' + + 'with no dialog. Downloading twenty files puts twenty files there.', + 'settings.dl_ask': 'Ask every time', + 'settings.dl_ask_hint': 'A Save As dialog for each file — one per file, ' + + 'including when you download a selection.', + 'settings.dl_folder': 'Folder: {name}', + 'settings.dl_no_folder': 'No folder chosen — downloads go wherever your browser ' + + 'puts them', + 'settings.dl_choose': 'Choose folder', + 'settings.dl_change': 'Change', + 'settings.dl_forget': 'Forget', + 'settings.dl_path_note': 'A web page cannot be given a path, so there is nothing ' + + 'to type here: your browser grants access to the folder you pick, and MeshBay ' + + 'only ever writes inside it. You may be asked to confirm once per session.', + 'settings.dl_unsupported': 'This browser cannot write into a folder of your ' + + 'choosing (no File System Access API), so downloads go to its own download ' + + 'folder. Chrome and Edge support choosing one.', + 'settings.profile': 'Profile', + 'settings.username': 'Username', + 'settings.node_pins': 'Node identities', + 'settings.node_pins_hint': "Each node's identity key is remembered the first time you connect. If it changes, the connection is refused — that is expected only when an operator reinstalls a node. Verify with them before clearing.", + 'settings.node_pins_count': { + one: '{n} pinned', + other: '{n} pinned', + }, + 'settings.node_pins_clear': 'Clear pinned identities', + 'settings.danger': 'Delete account', + 'settings.delete_hint': 'Removes your account, your group memberships and your ' + + 'notifications from the hub, and frees your username. It cannot reach what ' + + 'lives on nodes: files you uploaded stay where their operator keeps them, ' + + 'and each node keeps the identity it pinned for you until its operator ' + + 'unpins it. Connection logs are kept for a year, as the law requires.', + 'settings.delete_account': 'Delete my account', + 'settings.delete_confirm': 'Enter your passphrase to confirm. This cannot be undone.', + 'settings.delete_confirm_btn': 'Delete for good', + 'settings.cancel': 'Cancel', + 'settings.appearance': 'Appearance', + 'settings.theme': 'Theme', + 'settings.theme_light': 'Light', + 'settings.theme_dark': 'Dark', + 'settings.theme_system': 'System', + 'settings.language': 'Language', + 'settings.about': 'About', + 'settings.version': 'Version', + 'settings.protocol': 'Protocol', + 'settings.role': 'Role', + 'settings.groups': 'Group notifications', + 'settings.notifications': 'Notifications', + 'settings.node_key': 'Link Node', + 'settings.node_key_desc': 'If you operate a MeshBay node, paste its Ed25519 public key here to link it to your account. You can find this key in your node\'s admin dashboard.', + 'settings.node_key_placeholder': 'Paste node Ed25519 public key (base64)', + 'settings.node_key_submit': 'Link Node', + 'settings.node_key_success': 'Node linked successfully', + 'settings.node_key_current': 'Linked node key', + + // Sidebar + 'sidebar.create_group': 'Create group', + 'sidebar.settings': 'Settings', + 'sidebar.admin': 'Admin', + + // User menu + 'usermenu.language': 'Language', + 'usermenu.settings': 'Settings', + 'usermenu.theme_light': 'Light mode', + 'usermenu.theme_dark': 'Dark mode', + 'usermenu.logout': 'Logout', + + // Admin panel + 'admin.title': 'Administration', + 'admin.tab_stats': 'Stats', + 'admin.tab_users': 'Users', + 'admin.tab_groups': 'Groups', + 'admin.tab_nodes': 'Nodes', + 'admin.tab_logs': 'Logs', + 'admin.col_observed_ip': 'Seen from', + 'admin.col_hint': 'Announced hint', + 'admin.col_last_seen': 'Last announce', + 'admin.node_online': 'online', + 'admin.node_offline': 'offline', + 'admin.no_nodes': 'No nodes registered', + 'admin.nodes_hint': '"Seen from" is the address the announcement arrived from, ' + + 'on a request signed with the node key — that is the one to answer a ' + + 'question with. The hint is what the node believes its own address to be, ' + + 'learned from a STUN server and sent to us; it is useful for reaching the ' + + 'node and is not evidence of anything.', + 'admin.tab_blocklist': 'Blocklist', + + // Admin stats + 'admin.stat_users': 'Users', + 'admin.stat_groups': 'Groups', + 'admin.stat_nodes': 'Nodes', + 'admin.stat_online': 'Online Nodes', + + // Admin users + 'admin.users_search': 'Search users...', + 'admin.col_username': 'Username', + 'admin.col_role': 'Role', + 'admin.col_status': 'Status', + 'admin.col_created': 'Created', + 'admin.col_actions': 'Actions', + 'admin.col_email': 'Email', + 'admin.col_groups': 'Groups', + 'admin.no_users': 'No users found', + 'admin.btn_suspend': 'Suspend', + 'admin.btn_unsuspend': 'Unsuspend', + 'admin.btn_details': 'Details', + 'admin.btn_delete': 'Delete', + 'admin.delete_confirm': 'Delete the account "{user}"? This cannot be undone. ' + + 'Files they uploaded stay on the nodes that host them, and each node keeps ' + + 'the identity it pinned until its operator unpins it. Suspending is the ' + + 'reversible option.', + 'admin.user_detail': 'User details', + 'admin.btn_close': 'Close', + 'admin.self_note': '(you)', + + // Admin groups + 'admin.col_name': 'Name', + 'admin.col_visibility': 'Visibility', + 'admin.col_members': 'Members', + 'admin.no_groups': 'No groups found', + + // Admin logs + 'admin.col_time': 'Time', + 'admin.col_event': 'Event', + 'admin.col_user': 'User', + 'admin.col_ip': 'IP', + 'admin.col_detail': 'Detail', + 'admin.filter_all': 'All events', + 'admin.no_logs': 'No logs found', + 'admin.btn_load_more': 'Load more', + + // Admin blocklist + 'admin.col_hash': 'Hash', + 'admin.col_reason': 'Reason', + 'admin.col_date': 'Date', + 'admin.col_added_by': 'Added by', + 'admin.no_blocked': 'No blocked hashes', + 'admin.add_hash': 'Add hash to blocklist', + 'admin.hash_placeholder': 'blake3 hash (64 hex chars)', + 'admin.reason_placeholder': 'Reason', + 'admin.btn_block': 'Block', + 'admin.btn_unblock': 'Unblock', + + // Notifications + // Create group + 'create_group.title': 'Create Group', + 'create_group.name': 'Group name', + 'create_group.name_placeholder': 'e.g. Family Photos, Project Files...', + 'create_group.visibility': 'Visibility', + 'create_group.private': 'Private', + 'create_group.private_desc': 'Only invited members can see and access files', + 'create_group.public': 'Public', + 'create_group.public_desc': 'Anyone can discover and browse this group', + 'create_group.join_policy': 'Join policy', + 'create_group.invite': 'Invite only', + 'create_group.invite_desc': 'Members must be invited by an admin', + 'create_group.open': 'Open (anyone can join)', + 'create_group.open_desc': 'Anyone can join without approval', + 'create_group.description': 'Description', + 'create_group.description_hint': 'What is this group about? (optional)', + 'create_group.submit': 'Create', + 'create_group.hint': 'A group needs a node to host files. You can create the group now and connect a node later.', + 'create_group.creating': 'Creating...', + + // Members + 'members.col_role': 'Role', + 'members.group_role': 'Group role', + 'members.admin': 'Admin', + 'members.group_admin': 'Group admin', + 'members.owner': 'Owner', + 'members.member': 'Member', + 'members.invite_title': 'Invite member', + 'members.username_placeholder': 'Username', + 'members.invite_btn': 'Invite', + 'members.invite_needs_pairing': 'Invitations are signed with the key this node ' + + 'pinned for your browser, so pair it below before inviting anyone.', + 'members.invite_ask_operator': 'Only the operator of the node hosting this group can ' + + 'invite, from a browser paired with it.', + 'members.remove': 'Remove', + 'members.remove_confirm': 'Remove {user} from this group? Their account, their ' + + 'other groups and the files they uploaded are not affected.', + 'members.remove_hint': 'Removing someone takes away their membership and stops ' + + 'the node serving them the group key. It does not delete their account, and ' + + 'it does not take back the key they already hold — rotate it on the node ' + + '(meshbay-node gek-init) if that matters.', + 'members.pair_title': 'Pair this browser with your node', + 'members.pair_hint': 'Your node only accepts operator actions — invites, file ' + + 'deletion — from a browser it has been paired with. Run ' + + '`meshbay-node operator pair` on the node and type the code here. The code ' + + 'never passes through the hub.', + 'members.pair_btn': 'Pair', + 'members.pair_success': 'This browser is now paired with the node.', + 'members.invite_code_ready': 'Invitation code for {user} — send it to them the way ' + + 'you normally talk. It works once, and it never passes through the hub.', + 'members.invite_code_hint': 'They enter it the first time they open this group. ' + + 'You do not need to be online then.', + 'transfers.title': 'Transfers', + 'transfers.clear': 'Clear finished', + 'transfers.cancel': 'Cancel', + 'transfers.open': 'Open', + 'transfers.done': 'Finished', + 'transfers.cancelled': 'Cancelled', + 'transfers.failed': 'Failed', + 'group.select': 'Select', + 'group.select_done': 'Done', + 'group.actions': 'Actions ({n})', + 'group.download_n': 'Download ({n})', + 'group.download_zip_n': 'Download folders as zip ({n})', + 'group.delete_n': 'Delete ({n})', + 'group.delete_n_confirm': { + one: 'Delete {n} item? {names}', + other: 'Delete {n} items? {names}', + }, + 'group.download_zip': { + one: 'Download as zip ({n} file)', + other: 'Download as zip ({n} files)', + }, + 'group.zip_empty': 'That folder has nothing in it to download.', + 'group.zip_no_stream': 'This browser cannot write a download straight to disk, ' + + 'so {name} ({size}) has to be built in memory first. On a large archive ' + + 'that may fail. Continue?', + 'group.rmdir': 'Delete folder', + 'group.rmdir_confirm': 'Delete the folder "{name}"? It must be empty.', + 'group.rmdir_not_empty': 'That folder is not empty. Delete what is in it first — ' + + 'nothing here removes files you cannot see.', + 'group.desc_edit': 'Edit description', + 'group.desc_add': 'Add a description', + 'group.desc_save': 'Save', + 'group.desc_cancel': 'Cancel', + 'group.desc_placeholder': 'What this group is for — members see it on their home page.', + 'group.join_code_title': 'This node needs to recognise you', + 'group.join_code_hint': 'Ask whoever invited you for the one-time code, and enter ' + + 'it here. After that this browser is recognised and you will not be asked again.', + 'group.join_code_btn': 'Join', + + 'notif.purge': 'Clear all', + 'notif.title': 'Notifications', + 'notif.empty': 'No notifications', + 'notif.mark_all_read': 'Mark all read', + + // Sidebar + 'sidebar.search': 'Search files', + + // Status + + // Search + 'search.title': 'Search Files', + 'search.placeholder': 'Search across all groups...', + 'search.no_results': 'No files match your search.', + 'search.synced': 'synced {ago}', + 'search.cache_note': 'Search reads what each group looked like the last time you ' + + 'opened it. Open a group to bring its files up to date here.', + 'search.col_group': 'Group', + 'search.result_count': { + one: '{n} file found', + other: '{n} files found', + }, + 'search.hint': 'Search file names across all cached group indexes.', +}; |