mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-10-30 23:51:57 +01:00
The version check already worked for an installation running the published
image - CI freezes the version before building, so version_frozen.py ships
inside it - but "Update now" never appeared, because the webhook behind it
was installed from scripts/, a folder such an installation does not have.
And update.sh could not have served it anyway: it does git pull + rebuild.
- both installers accept being piped through bash (curl | sudo bash). They
then download what they need into /opt/mc-webui-{updater,watchdog} and
find the instance by looking for docker-compose.yml. A checkout install
keeps running its scripts from the checkout, unchanged
- updater.py picks the update path per request: git checkout -> update.sh,
bare docker-compose.yml -> the new update-image.sh (pull + recreate).
Resolved per request, so cloning the repo later flips it without a restart
- both update scripts end with MC_UPDATE_RESULT=updated|no_change, which
the webhook parses and /api/updater/status passes on. The UI used to
watch only for the version to change and then claim it timed out after
two minutes - the normal outcome right after a push, while the image for
that commit is still building. It now says so as soon as the run ends,
in a neutral state rather than a red cross
- the marker prints only when stdout is not a terminal, so mcupdate output
stays as it was
Also corrects the claim, in the README and in updater.py's own docstring,
that the webhook listens on localhost. It binds 0.0.0.0 - the container
reaches it over the Docker bridge - and sends no token, so port 5050 is
worth keeping off untrusted networks.
Verified: update-image.sh reports updated then no_change against a real
docker compose project; the standalone installer installs, runs and
uninstalls cleanly under systemd (WSL), with the app reaching it through
host.docker.internal and a triggered run coming back changed=false; the
resulting modal checked in a browser in both en and pl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1507 lines
86 KiB
JSON
1507 lines
86 KiB
JSON
{
|
||
"analyzer.add": "Add analyzer",
|
||
"analyzer.choose": "Choose analyzer",
|
||
"analyzer.clear_default_title": "Clear default",
|
||
"analyzer.confirm.delete": "Delete analyzer \"{name}\"?",
|
||
"analyzer.edit": "Edit analyzer",
|
||
"analyzer.empty": "No analyzers configured. Click \"Add analyzer\" to add one.",
|
||
"analyzer.load_failed": "Failed to load analyzers",
|
||
"analyzer.name_ph": "e.g. MarWoj Analyzer",
|
||
"analyzer.name_required": "Name is required",
|
||
"analyzer.set_default_title": "Mark as default",
|
||
"analyzer.toast.default_error": "Network error updating default",
|
||
"analyzer.toast.default_failed": "Failed to update default",
|
||
"analyzer.toast.delete_error": "Network error deleting analyzer",
|
||
"analyzer.toast.delete_failed": "Failed to delete analyzer",
|
||
"analyzer.toast.none_configured": "No analyzer configured. Add one in Settings → Analyzer.",
|
||
"analyzer.toast.save_error": "Network error saving analyzer",
|
||
"analyzer.toast.save_failed": "Failed to save analyzer",
|
||
"analyzer.toast.update_error": "Network error updating analyzer",
|
||
"analyzer.toast.update_failed": "Failed to update analyzer",
|
||
"analyzer.url": "URL template",
|
||
"analyzer.url_hint": "Must include <code>{packetHash}</code>.",
|
||
"analyzer.url_invalid": "URL must start with http:// or https://",
|
||
"analyzer.url_placeholder_missing": "URL must contain the {placeholder} placeholder",
|
||
"backup.auto_off": "Auto-backup disabled",
|
||
"backup.auto_on": "Auto: daily at {time}, keep {days}d",
|
||
"backup.create": "Create Backup",
|
||
"backup.created": "Backup created: {filename}",
|
||
"backup.creating": "Creating...",
|
||
"backup.current_size": "Current size: …",
|
||
"backup.download_title": "Download",
|
||
"backup.empty": "No backups yet",
|
||
"backup.error": "Backup failed",
|
||
"backup.failed": "Backup failed: {error}",
|
||
"backup.freed": "freed {size}",
|
||
"backup.freed_none": "no space to reclaim",
|
||
"backup.load_failed": "Failed to load backups",
|
||
"backup.optimize_error": "Optimize failed",
|
||
"backup.optimize_failed": "Optimize failed: {error}",
|
||
"backup.optimize_hint": "Reclaim space freed by message retention. Runs SQLite <code>VACUUM</code>; readers keep working but writes are paused for a few seconds.",
|
||
"backup.optimize_now": "Optimize now",
|
||
"backup.optimize_stuck": "Optimize is still running after 10 minutes — check container logs",
|
||
"backup.optimize_title": "Optimize database",
|
||
"backup.optimized": "Optimized: {freed} in {seconds}s",
|
||
"backup.optimizing": "Optimizing…",
|
||
"backup.size": "Current size: {size}",
|
||
"backup.size_unknown": "Size: unknown",
|
||
"backup.title": "Database Backup",
|
||
"backup.vacuum_running": "Running VACUUM…",
|
||
"backup.vacuum_running_for": "Running VACUUM… ({seconds}s)",
|
||
"channels.add_new": "Add New Channel",
|
||
"channels.confirm.remove": "Remove channel \"{name}\"?",
|
||
"channels.create_btn": "Create & Auto-generate Key",
|
||
"channels.create_title": "Create New Channel",
|
||
"channels.dropdown.none": "No channels",
|
||
"channels.fav_title": "Favorite channel",
|
||
"channels.join_btn": "Join Channel",
|
||
"channels.join_existing": "Join Existing",
|
||
"channels.join_title": "Join Existing Channel",
|
||
"channels.key": "Channel Key:",
|
||
"channels.key_label": "Channel Key (32 hex chars)",
|
||
"channels.key_optional": "- optional for channels starting with #",
|
||
"channels.key_ph": "485af7e164459d280d8818d9c99fb30d (leave empty for # channels)",
|
||
"channels.list.empty": "No channels configured",
|
||
"channels.list.load_error": "Error loading channels",
|
||
"channels.list.load_failed": "Failed to load channels",
|
||
"channels.name": "Channel Name",
|
||
"channels.name_hint": "Only letters, numbers, _ and -",
|
||
"channels.name_ph": "e.g., Malopolska",
|
||
"channels.notify.all": "Every message",
|
||
"channels.notify.manage": "Manage profiles…",
|
||
"channels.notify.no_profiles": "No profiles defined yet",
|
||
"channels.notify.off": "Off",
|
||
"channels.notify.profiles_header": "Only messages matching a profile",
|
||
"channels.notify.title_all": "Notifications: every message — click to change",
|
||
"channels.notify.title_off": "Notifications: off — click to change",
|
||
"channels.notify.title_profile": "Notifications: profile {name} — click to change",
|
||
"channels.qr_alt": "QR Code",
|
||
"channels.region_hint": "Only repeaters allowing the selected region will forward messages from this channel.",
|
||
"channels.region_scope_for": "Set Region Scope for",
|
||
"channels.scan_qr": "Scan QR Code",
|
||
"channels.scope_set_title": "Set region scope",
|
||
"channels.scope_title": "Region: {name} — click to change",
|
||
"channels.share_hint": "Share this QR code or key with others to let them join this channel.",
|
||
"channels.share_name": "Channel: {name}",
|
||
"channels.share_title": "Share Channel",
|
||
"channels.toast.already_joined": "Already joined channel \"{name}\".",
|
||
"channels.toast.copy_failed": "Failed to copy to clipboard",
|
||
"channels.toast.create_error": "Failed to create channel",
|
||
"channels.toast.create_failed": "Failed to create channel: {error}",
|
||
"channels.toast.created": "Channel \"{name}\" created!",
|
||
"channels.toast.exists": "Channel \"{name}\" already exists.",
|
||
"channels.toast.favorite_failed": "Failed to update favorite state",
|
||
"channels.toast.join_error": "Failed to join channel",
|
||
"channels.toast.join_failed": "Failed to join channel: {error}",
|
||
"channels.toast.joined": "Joined channel \"{name}\"!",
|
||
"channels.toast.key_copied": "Channel key copied to clipboard!",
|
||
"channels.toast.key_invalid": "Invalid key format. Must be 32 hex characters.",
|
||
"channels.toast.key_required": "Channel key is required for channels not starting with #",
|
||
"channels.toast.notify_failed": "Failed to change notifications",
|
||
"channels.toast.qr_error": "Failed to generate QR code",
|
||
"channels.toast.qr_failed": "Failed to generate QR code: {error}",
|
||
"channels.toast.qr_soon": "QR scanning feature coming soon! For now, manually enter the channel details.",
|
||
"channels.toast.remove_error": "Failed to remove channel",
|
||
"channels.toast.remove_failed": "Failed to remove channel: {error}",
|
||
"channels.toast.removed": "Channel \"{name}\" removed",
|
||
"channels.toast.switched": "Switched to channel: {name}",
|
||
"channels.unfav_title": "Unfavorite channel",
|
||
"channels.your": "Your Channels",
|
||
"chat.channels": "Channels",
|
||
"chat.confirm.block": "Block {name}? Their messages will be hidden from chat.",
|
||
"chat.confirm.mark_all_read": "Mark all messages as read?\n\nUnread channels:\n{channels}",
|
||
"chat.confirm.stale_resend": "This message was sent {age}. Repeaters only remember recent packets, so nothing will suppress it this time — everyone on the channel will see it again as a new message. Resend anyway?",
|
||
"chat.copy_route_title": "Tap to copy route",
|
||
"chat.edit_title": "Edit message",
|
||
"chat.emoji_title": "Insert emoji",
|
||
"chat.empty.no_messages": "No messages yet",
|
||
"chat.empty.no_messages_hint": "Send a message to get started!",
|
||
"chat.error.load_retry": "Will retry automatically",
|
||
"chat.error.load_title": "Could not load messages",
|
||
"chat.fab.hide": "Hide buttons",
|
||
"chat.fab.search_title": "Search Messages",
|
||
"chat.fab.show": "Show buttons",
|
||
"chat.filter.clear_title": "Clear",
|
||
"chat.filter.fab_title": "Filter Messages",
|
||
"chat.filter.me_title": "Filter my messages",
|
||
"chat.filter.no_matches": "No messages match \"{query}\"",
|
||
"chat.filter.ph": "Filter messages...",
|
||
"chat.image_alt": "Image",
|
||
"chat.image_preview": "Image Preview",
|
||
"chat.image_preview_alt": "Preview",
|
||
"chat.input_ph": "Type a message...",
|
||
"chat.loading_messages": "Loading messages...",
|
||
"chat.mentions.none": "No contacts found",
|
||
"chat.msg.analyzer_title": "View in Analyzer",
|
||
"chat.msg.block_title": "Block {name}",
|
||
"chat.msg.echo_title": {
|
||
"one": "Heard by {count} repeater: {paths}",
|
||
"other": "Heard by {count} repeaters: {paths}"
|
||
},
|
||
"chat.msg.ignore_title": "Ignore {name}",
|
||
"chat.msg.map_title": "Show on map",
|
||
"chat.msg.quote_title": "Quote",
|
||
"chat.msg.raw_resend_title": "Resend (rebroadcast same packet so unreached repeaters can pick it up)",
|
||
"chat.msg.reply_title": "Reply",
|
||
"chat.region_none": "Region --",
|
||
"chat.region_set_title": "Click to set a region for this channel",
|
||
"chat.region_title": "Click to change region for this channel",
|
||
"chat.route": "Route: {route}",
|
||
"chat.route_analyzer_title": "Show this route on the Path Analyzer map",
|
||
"chat.route_delivered": "received",
|
||
"chat.route_delivered_title": "The copy your node received and decoded. The other copies arrived later and were dropped as duplicates",
|
||
"chat.route_direct": "Direct",
|
||
"chat.route_hash_bytes": {
|
||
"one": "{count} byte",
|
||
"other": "{count} bytes"
|
||
},
|
||
"chat.route_hash_bytes_short": "{count}B",
|
||
"chat.route_hash_title": "Per-hop hash size of the route. Tap to list every route this message was heard on",
|
||
"chat.route_hops": "Hops: {count}",
|
||
"chat.route_multi": "Route ({count}): {route}",
|
||
"chat.route_region": "Region: {name}",
|
||
"chat.scroll_bottom_title": "Scroll to bottom",
|
||
"chat.title": "Chat",
|
||
"chat.toast.load_error": "Error loading messages: {error}",
|
||
"chat.toast.load_failed": "Failed to load messages",
|
||
"chat.toast.load_timeout": "Loading messages timed out — retrying...",
|
||
"chat.toast.refresh_failed": "Refresh failed",
|
||
"chat.toast.refreshed": "Messages refreshed",
|
||
"chat.toast.resend_failed": "Resend failed: {error}",
|
||
"chat.toast.resend_network_error": "Resend network error: {error}",
|
||
"chat.toast.resent": "Resent ({bytes} B) — waiting for echoes…",
|
||
"chat.toast.send_error": "Failed to send message",
|
||
"chat.toast.send_failed": "Failed to send: {error}",
|
||
"chat.toast.sent": "Message sent",
|
||
"chat.unnamed_channel": "Channel {index}",
|
||
"chat.unread_divider": "New messages",
|
||
"chat.updated": "Updated: {time}",
|
||
"common.add": "Add",
|
||
"common.apply": "Apply",
|
||
"common.back": "Back",
|
||
"common.cancel": "Cancel",
|
||
"common.cannot_undo": "This action cannot be undone.",
|
||
"common.close": "Close",
|
||
"common.connected": "Connected",
|
||
"common.connecting": "Connecting...",
|
||
"common.copied": "Copied!",
|
||
"common.copy": "Copy",
|
||
"common.copy_failed": "Copy failed",
|
||
"common.copy_route": "Copy route",
|
||
"common.days_ago": {
|
||
"one": "{count}d ago",
|
||
"other": "{count}d ago"
|
||
},
|
||
"common.days_ago_long": {
|
||
"one": "{count} day ago",
|
||
"other": "{count} days ago"
|
||
},
|
||
"common.delete": "Delete",
|
||
"common.deleting": "Deleting...",
|
||
"common.disabled": "Disabled",
|
||
"common.disconnected": "Disconnected",
|
||
"common.edit": "Edit",
|
||
"common.enabled": "Enabled",
|
||
"common.error.not_json": "The app did not answer — something in front of it did (HTTP {status}). Reload the page, and check the connection if it keeps happening.",
|
||
"common.error.not_json_auth": "Your session has expired — a login page in front of the app answered instead of the app (HTTP {status}). Reload the page and sign in again.",
|
||
"common.failed_with": "Failed: {error}",
|
||
"common.hours_ago": "{count}h ago",
|
||
"common.hours_ago_long": {
|
||
"one": "{count} hour ago",
|
||
"other": "{count} hours ago"
|
||
},
|
||
"common.just_now": "just now",
|
||
"common.load_failed": "Failed to load",
|
||
"common.loading": "Loading...",
|
||
"common.minutes_ago": "{count} min ago",
|
||
"common.minutes_ago_long": {
|
||
"one": "{count} minute ago",
|
||
"other": "{count} minutes ago"
|
||
},
|
||
"common.months_ago": {
|
||
"one": "{count} month ago",
|
||
"other": "{count} months ago"
|
||
},
|
||
"common.move_down": "Move down",
|
||
"common.move_up": "Move up",
|
||
"common.name": "Name",
|
||
"common.network_error": "Network error",
|
||
"common.network_error_detail": "Network error: {error}",
|
||
"common.never": "Never",
|
||
"common.no": "No",
|
||
"common.refresh": "Refresh",
|
||
"common.remove": "Remove",
|
||
"common.save": "Save",
|
||
"common.save_failed": "Save failed",
|
||
"common.saving": "Saving...",
|
||
"common.settings": "Settings",
|
||
"common.share": "Share",
|
||
"common.try_again": "Try again",
|
||
"common.unknown": "Unknown",
|
||
"common.unknown_error": "Unknown error",
|
||
"common.warning": "Warning:",
|
||
"common.years_ago": {
|
||
"one": "{count} year ago",
|
||
"other": "{count} years ago"
|
||
},
|
||
"common.yes": "Yes",
|
||
"common.yesterday": "Yesterday",
|
||
"console.clear_title": "Clear output history",
|
||
"console.connect_failed": "Failed to connect: {error}",
|
||
"console.error_prefix": "Error: {error}",
|
||
"console.help_hint": "Type 'help' for available commands.",
|
||
"console.history_empty": "No commands in history",
|
||
"console.history_title": "Command history",
|
||
"console.input_ph": "Enter command...",
|
||
"console.no_output": "(no output)",
|
||
"console.scroll_bottom_aria": "Scroll to latest",
|
||
"console.scroll_bottom_title": "Scroll to latest",
|
||
"console.title": "Console",
|
||
"contacts.active_recent": "Active (advert received recently)",
|
||
"contacts.add.adding": "Adding contact...",
|
||
"contacts.add.failed": "Failed to add contact.",
|
||
"contacts.add.key": "Key:",
|
||
"contacts.add.key_count": "{n} / 64 characters",
|
||
"contacts.add.name": "Name:",
|
||
"contacts.add.name_ph": "Contact name",
|
||
"contacts.add.preview": "Preview:",
|
||
"contacts.add.pubkey": "Public Key (64 hex chars):",
|
||
"contacts.add.pubkey_ph": "e.g. a1b2c3d4...",
|
||
"contacts.add.qr_invalid_uri": "QR code does not contain a valid meshcore:// URI.",
|
||
"contacts.add.qr_label": "Or upload a QR code image:",
|
||
"contacts.add.scanned": "Scanned:",
|
||
"contacts.add.start_camera": "Start Camera",
|
||
"contacts.add.stop_camera": "Stop Camera",
|
||
"contacts.add.success": "Contact added successfully!",
|
||
"contacts.add.tab_manual": "Manual",
|
||
"contacts.add.title": "Add Contact",
|
||
"contacts.add.type": "Contact Type:",
|
||
"contacts.add.type_short": "Type:",
|
||
"contacts.add.uri_hint": "Paste a meshcore:// URI from the MeshCore mobile app",
|
||
"contacts.add.uri_label": "MeshCore URI:",
|
||
"contacts.add.uri_ph": "meshcore://contact/add?name=...&public_key=...&type=...",
|
||
"contacts.blocked_by_name": "Blocked by name",
|
||
"contacts.btn.approve": "Approve",
|
||
"contacts.btn.approve_all": "Approve All",
|
||
"contacts.btn.block": "Block",
|
||
"contacts.btn.ignore": "Ignore",
|
||
"contacts.btn.ignore_all": "Ignore All",
|
||
"contacts.btn.protect": "Protect",
|
||
"contacts.btn.protected": "Protected",
|
||
"contacts.btn.to_cache": "To cache",
|
||
"contacts.btn.to_cache_title": "Remove from device, keep in cache",
|
||
"contacts.btn.to_device": "To device",
|
||
"contacts.btn.to_device_title": "Add this contact to the device",
|
||
"contacts.btn.unblock": "Unblock",
|
||
"contacts.btn.unignore": "Unignore",
|
||
"contacts.cache_badge": "Cache",
|
||
"contacts.cache_badge_title": "Not on device - type unknown",
|
||
"contacts.card.add_desc": "Add from URI, QR code, or manual entry",
|
||
"contacts.card.existing_desc": "Manage all stored contacts",
|
||
"contacts.card.pending_desc": "Contacts awaiting manual approval",
|
||
"contacts.cleanup.auto_enabled": "Enabled (runs daily at {time} {tz})",
|
||
"contacts.cleanup.completed": "Cleanup completed: {count} deleted",
|
||
"contacts.cleanup.completed_failed": ", {count} failed",
|
||
"contacts.cleanup.confirm": "Remove all contacts inactive for more than {hours} hours?",
|
||
"contacts.cleanup.confirm_title": "Confirm Contact Cleanup",
|
||
"contacts.cleanup.date_field": "Date Field:",
|
||
"contacts.cleanup.days": "Days of Inactivity (0 = ignore):",
|
||
"contacts.cleanup.days_hint": "Contacts inactive for more than this many days will be selected",
|
||
"contacts.cleanup.delete_btn": "Delete Contacts",
|
||
"contacts.cleanup.delete_confirm": "The following <span id=\"cleanupContactCount\">0</span> contact(s) will be permanently deleted:",
|
||
"contacts.cleanup.enable": "Enable Auto-Cleanup",
|
||
"contacts.cleanup.enable_hint": "When enabled, contacts matching the above criteria will be automatically deleted daily at the specified hour",
|
||
"contacts.cleanup.error": "Cleanup failed",
|
||
"contacts.cleanup.failed": "Cleanup failed: {error}",
|
||
"contacts.cleanup.last_advert": "Last Advert",
|
||
"contacts.cleanup.last_modified": "Last Modified",
|
||
"contacts.cleanup.name_filter": "Name Filter (optional):",
|
||
"contacts.cleanup.name_filter_ph": "Enter partial name to search...",
|
||
"contacts.cleanup.network_error": "Network error during cleanup",
|
||
"contacts.cleanup.preview": "Preview Cleanup",
|
||
"contacts.cleanup.run_at": "Run at:",
|
||
"contacts.cleanup.status_label": "Status:",
|
||
"contacts.cleanup.title": "Cleanup Contacts",
|
||
"contacts.cleanup.title_hint": "Remove contacts based on various filter criteria",
|
||
"contacts.cleanup.types": "Contact Types:",
|
||
"contacts.cleanup.undo_warning": "This action cannot be undone!",
|
||
"contacts.confirm.block": "Block this contact? Their messages will be hidden from chat.",
|
||
"contacts.confirm.push": "Push \"{name}\" to device?",
|
||
"contacts.confirm.to_cache": "Move \"{name}\" from device to cache?",
|
||
"contacts.copy_title": "Click to copy",
|
||
"contacts.delete.confirm": "Delete Contact",
|
||
"contacts.delete.question": "Are you sure you want to delete this contact?",
|
||
"contacts.delete.title": "Confirm Delete",
|
||
"contacts.existing.load_failed": "Failed to load contacts",
|
||
"contacts.existing.loading": "Loading contacts...",
|
||
"contacts.existing.none": "No contacts found",
|
||
"contacts.existing.none_hint": "Contacts will appear here once added",
|
||
"contacts.existing.search_ph": "Search by name or public key...",
|
||
"contacts.existing.title": "Existing Contacts",
|
||
"contacts.filter.advanced": "Advanced Filters",
|
||
"contacts.filter.all_sources": "All sources",
|
||
"contacts.filter.all_types": "All Types",
|
||
"contacts.filter.blocked": "Blocked",
|
||
"contacts.filter.cache_only": "Cache only",
|
||
"contacts.filter.ignored": "Ignored",
|
||
"contacts.filter.no_match": "No contacts match filters",
|
||
"contacts.filter.no_match_hint": "Try changing your filter criteria",
|
||
"contacts.filter.on_device": "On device",
|
||
"contacts.hops": {
|
||
"one": "{count} hop",
|
||
"other": "{count} hops"
|
||
},
|
||
"contacts.inactive": "Inactive",
|
||
"contacts.last_advert": "Last advert: {time}",
|
||
"contacts.last_advert_unknown": "Last advert: Unknown",
|
||
"contacts.manage.title": "Contact Management Settings",
|
||
"contacts.map.show": "Show:",
|
||
"contacts.map.title": "Map",
|
||
"contacts.never_seen": "Never seen",
|
||
"contacts.pending.approve_confirm": "You are about to approve <strong id=\"batchApprovalCount\">0</strong> contacts:",
|
||
"contacts.pending.approve_note": "These contacts will be added to your device and can receive/send messages.",
|
||
"contacts.pending.confirm_approve_title": "Confirm Batch Approval",
|
||
"contacts.pending.confirm_ignore_title": "Confirm Batch Ignore",
|
||
"contacts.pending.filter_hint": "Filter contacts by type or name, then approve or ignore them in bulk.",
|
||
"contacts.pending.ignore_confirm": "You are about to ignore <strong id=\"batchIgnoreCount\">0</strong> contacts:",
|
||
"contacts.pending.ignore_note": "Ignored contacts will not trigger new pending requests. You can unignore them later from the Existing Contacts page.",
|
||
"contacts.pending.load_failed": "Failed to load pending contacts",
|
||
"contacts.pending.loading": "Loading pending contacts...",
|
||
"contacts.pending.none": "No pending requests",
|
||
"contacts.pending.none_hint": "New contact requests will appear here when manual approval is enabled",
|
||
"contacts.pending.title": "Pending Contacts",
|
||
"contacts.progress.approving": "Approving {done}/{total}...",
|
||
"contacts.progress.ignoring": "Ignoring {done}/{total}...",
|
||
"contacts.protected_cannot_block": "Cannot block protected contact",
|
||
"contacts.protected_cannot_delete": "Cannot delete protected contact",
|
||
"contacts.protected_cannot_ignore": "Cannot ignore protected contact",
|
||
"contacts.protected_cannot_move": "Cannot move protected contact",
|
||
"contacts.protected_title": "Protected contact",
|
||
"contacts.qr.camera_denied": "Camera access denied or not available. Try uploading an image instead.",
|
||
"contacts.qr.read_failed": "Could not read QR code from image. Make sure the image contains a valid QR code.",
|
||
"contacts.recent_activity": "Recent activity",
|
||
"contacts.sort.advert_asc": "Last advert ↑",
|
||
"contacts.sort.advert_desc": "Last advert ↓",
|
||
"contacts.sort.name_asc": "Name A→Z",
|
||
"contacts.sort.name_desc": "Name Z→A",
|
||
"contacts.title": "Contact Management",
|
||
"contacts.toast.approve_failed": "Failed to approve: {error}",
|
||
"contacts.toast.approved": "Approved: {name}",
|
||
"contacts.toast.bulk_approve_failed": "Failed to approve contacts. Check console for details.",
|
||
"contacts.toast.bulk_approve_partial": "Approved {ok}, failed {failed}. Check console for details.",
|
||
"contacts.toast.bulk_approved": {
|
||
"one": "Successfully approved {count} contact",
|
||
"other": "Successfully approved {count} contacts"
|
||
},
|
||
"contacts.toast.bulk_ignore_failed": "Failed to ignore contacts. Check console for details.",
|
||
"contacts.toast.bulk_ignore_partial": "Ignored {ok}, failed {failed}. Check console for details.",
|
||
"contacts.toast.bulk_ignored": {
|
||
"one": "Successfully ignored {count} contact",
|
||
"other": "Successfully ignored {count} contacts"
|
||
},
|
||
"contacts.toast.cleanup_failed": "Cleanup failed: {error}",
|
||
"contacts.toast.copy_failed": "Failed to copy to clipboard",
|
||
"contacts.toast.delete_failed": "Failed to delete: {error}",
|
||
"contacts.toast.deleted": "Deleted: {name}",
|
||
"contacts.toast.key_copied": "Key copied to clipboard",
|
||
"contacts.toast.move_failed": "Failed to move contact",
|
||
"contacts.toast.need_days": "Set \"Days of Inactivity\" > 0 before enabling auto-cleanup",
|
||
"contacts.toast.need_type": "Please select at least one contact type",
|
||
"contacts.toast.need_type_enable": "Select at least one contact type before enabling auto-cleanup",
|
||
"contacts.toast.no_match": "No contacts match the selected criteria",
|
||
"contacts.toast.none_to_approve": "No contacts to approve",
|
||
"contacts.toast.none_to_ignore": "No contacts to ignore",
|
||
"contacts.toast.preview_failed": "Preview failed: {error}",
|
||
"contacts.toast.preview_network_error": "Network error during preview",
|
||
"contacts.toast.protect_failed": "Failed to update protection: {error}",
|
||
"contacts.toast.pubkey_copied": "Public key copied to clipboard",
|
||
"contacts.toast.push_failed": "Failed to push contact",
|
||
"contacts.toast.pushed": "{name} pushed to device",
|
||
"contacts.toast.settings_load_error": "Error loading settings",
|
||
"contacts.toast.settings_network_error": "Network error saving settings",
|
||
"contacts.toast.settings_save_failed": "Failed to save settings: {error}",
|
||
"contacts.toast.to_cache": "{name} moved to cache",
|
||
"coord.confirm": "Confirm",
|
||
"coord.hint": "Click on the map to select coordinates",
|
||
"coord.title": "Pick Coordinates",
|
||
"demo.badge": "Demo",
|
||
"demo.blocked_toast": "This instance runs in demo mode — the action is disabled.",
|
||
"demo.console_notice": "Demo mode: the console answers read-only commands such as <code>infos</code>, <code>stats</code>, <code>contacts</code> and <code>get</code>. Commands that change the device are refused.",
|
||
"demo.lock_btn": "Lock again",
|
||
"demo.locked_title": "Disabled in demo mode",
|
||
"demo.settings_notice": "This is a public demo instance, so settings that change the radio, the stored data or this server are read-only. Everything else — reading, sending messages, themes — works normally.",
|
||
"demo.unlock_bad": "That code was not accepted.",
|
||
"demo.unlock_btn": "Unlock",
|
||
"demo.unlock_hint": "If you administer this instance, enter the unlock code to restore full access on this device.",
|
||
"demo.unlock_label": "Operator access",
|
||
"demo.unlock_ph": "Unlock code",
|
||
"demo.unlocked_state": "Unlocked — you have full access on this device.",
|
||
"device.cmd.exec_failed": "Failed to execute {command}",
|
||
"device.cmd.failed": "Command failed: {error}",
|
||
"device.cmd.sent": "{command} sent successfully",
|
||
"device.info.bw": "Bandwidth",
|
||
"device.info.copy_title": "Copy to clipboard",
|
||
"device.info.cr": "Coding Rate",
|
||
"device.info.freq": "Frequency",
|
||
"device.info.load_failed": "Failed to load device info",
|
||
"device.info.loc_sharing": "Location Sharing",
|
||
"device.info.location": "Location",
|
||
"device.info.location_none": "Not available",
|
||
"device.info.manual_add": "Manual Add Contacts",
|
||
"device.info.multi_acks": "Multi Acks",
|
||
"device.info.name": "Name",
|
||
"device.info.none": "No device info available",
|
||
"device.info.pubkey": "Public Key",
|
||
"device.info.sf": "Spreading Factor",
|
||
"device.info.tx_power": "TX Power",
|
||
"device.info.type": "Type",
|
||
"device.info.type_unknown": "Unknown ({code})",
|
||
"device.reboot.btn": "Reboot",
|
||
"device.reboot.confirm": "Reboot the device?\n\nIt drops off the mesh for a few seconds while it restarts. Messages sent to you meanwhile may be lost. The app reconnects on its own.",
|
||
"device.reboot.failed": "Reboot failed",
|
||
"device.reboot.hint": "Restarts the device. It is offline for a few seconds.",
|
||
"device.reboot.sent": "Reboot command sent — the device is restarting",
|
||
"device.share.copy_uri": "Copy URI",
|
||
"device.share.hint": "Share this QR code or URI so others can add your device as a contact.",
|
||
"device.share.unavailable": "Device info not available",
|
||
"device.share.uri": "Contact URI:",
|
||
"device.share_hint": "Click to generate share code",
|
||
"device.stats.battery": "Battery",
|
||
"device.stats.channel_msgs": "Channel Msgs",
|
||
"device.stats.contacts_db": "Contacts (DB)",
|
||
"device.stats.database": "Database",
|
||
"device.stats.db_size": "DB Size",
|
||
"device.stats.direct_msgs": "Direct Msgs",
|
||
"device.stats.errors": "Errors",
|
||
"device.stats.load_failed": "Failed to load stats",
|
||
"device.stats.none": "No statistics available",
|
||
"device.stats.packets_rx": "Packets RX",
|
||
"device.stats.packets_tx": "Packets TX",
|
||
"device.stats.queue": "Queue",
|
||
"device.stats.rx_air": "RX Air Time",
|
||
"device.stats.tx_air": "TX Air Time",
|
||
"device.stats.uptime": "Uptime",
|
||
"device.stats_hint": "Click to load stats",
|
||
"device.tab.info": "Info",
|
||
"device.tab.share": "Share",
|
||
"device.tab.stats": "Stats",
|
||
"device.title": "Device",
|
||
"diag.confirm.delete": "Delete this capture?",
|
||
"diag.confirm.upload": "Send this capture to the configured share service? It contains the messages received while it was recording.",
|
||
"diag.counters": "<strong>{elapsed}</strong> of {total} · {events} events · {size}",
|
||
"diag.download": "Download",
|
||
"diag.dropped": "({n} events dropped)",
|
||
"diag.load_failed": "Failed to load diagnostics status",
|
||
"diag.minutes": "{n} min",
|
||
"diag.no_captures": "No captures yet.",
|
||
"diag.toast.delete_failed": "Failed to delete capture",
|
||
"diag.toast.no_token": "Save an upload token first",
|
||
"diag.toast.settings_failed": "Failed to save sharing settings",
|
||
"diag.toast.settings_saved": "Sharing settings saved",
|
||
"diag.toast.start_failed": "Failed to start the capture",
|
||
"diag.toast.started": "Recording started",
|
||
"diag.toast.stop_failed": "Failed to stop the capture",
|
||
"diag.toast.stopped": "Recording stopped — the capture is ready below",
|
||
"diag.toast.upload_failed": "Upload failed",
|
||
"diag.toast.uploaded": "Capture sent — the link is shown below",
|
||
"diag.token_missing": "No token saved. Ask the maintainer for one when they request a capture.",
|
||
"diag.token_set": "A token is saved. Leave the field blank to keep it.",
|
||
"diag.upload": "Send to maintainer",
|
||
"dm.attempt": "Attempt {n}/{max}",
|
||
"dm.auto_retry": "Auto Retry",
|
||
"dm.auto_retry_title": "Auto Retry: resend DM if no ACK received",
|
||
"dm.clear_selection_title": "Clear selection",
|
||
"dm.contact_info": "Contact Info",
|
||
"dm.contact_info_title": "Contact info",
|
||
"dm.copy_pubkey_title": "Click to copy full public key",
|
||
"dm.delivery_unknown": "Delivery unknown — no ACK received. Message may still have been delivered.",
|
||
"dm.dropdown.contacts": "Contacts",
|
||
"dm.dropdown.no_contacts": "No contacts available",
|
||
"dm.dropdown.no_matches": "No matches",
|
||
"dm.dropdown.recent": "Recent conversations",
|
||
"dm.empty.no_messages_hint": "Send a message to start the conversation",
|
||
"dm.empty.select": "Select a conversation",
|
||
"dm.empty.select_hint": "Choose from the list or start a new chat from channel messages",
|
||
"dm.import_device_path_title": "Import device path to configured paths",
|
||
"dm.keep_path": "Keep path",
|
||
"dm.keep_path_title": "Keep path: don't auto-reset to FLOOD after failed retries",
|
||
"dm.load_messages_error": "Error loading messages",
|
||
"dm.msg_input_ph": "Message {name}...",
|
||
"dm.no_contact_info": "No contact information available.",
|
||
"dm.select_chat_ph": "Select chat...",
|
||
"dm.sidebar.search_ph": "Search contacts...",
|
||
"dm.status.delivered": "Delivered",
|
||
"dm.status.failed": "Delivery failed — all retries exhausted",
|
||
"dm.status.sending": "Sending...",
|
||
"dm.title": "Direct Messages",
|
||
"dm.toast.auto_retry_off": "Auto Retry disabled",
|
||
"dm.toast.auto_retry_on": "Auto Retry enabled",
|
||
"dm.toast.device_path_imported": "Device path imported",
|
||
"dm.toast.import_failed": "Import failed",
|
||
"dm.toast.keep_path_off": "Keep path disabled",
|
||
"dm.toast.keep_path_on": "Keep path enabled",
|
||
"logs.clear_title": "Clear display",
|
||
"logs.entries": {
|
||
"one": "{count} entry",
|
||
"other": "{count} entries"
|
||
},
|
||
"logs.entries_filtered": {
|
||
"one": "{shown} / {count} entry",
|
||
"other": "{shown} / {count} entries"
|
||
},
|
||
"logs.filter.all_levels": "All levels",
|
||
"logs.filter.all_modules": "All modules",
|
||
"logs.filter.reset_title": "Reset filters",
|
||
"logs.filter.search_ph": "Search...",
|
||
"logs.load_failed": "Failed to load logs",
|
||
"logs.loading": "Loading logs...",
|
||
"logs.pause_title": "Pause/Resume",
|
||
"logs.title": "System Log",
|
||
"map.all_contacts": "All Contacts",
|
||
"map.cache": "Cache",
|
||
"map.cached_label": "{type} (cached)",
|
||
"map.own_device": "Own device",
|
||
"map.this_device": "This device",
|
||
"menu.advert": "Send Advert",
|
||
"menu.advert_desc": "Announce presence (normal)",
|
||
"menu.advert_title": "Send single advertisement (recommended for normal operation)",
|
||
"menu.archive_option": "{date} ({count} msgs)",
|
||
"menu.backup": "Backup",
|
||
"menu.backup_desc": "Database backup & restore",
|
||
"menu.channels": "Manage Channels",
|
||
"menu.check_updates_title": "Check for updates",
|
||
"menu.confirm.flood_advert": "Flood Advertisement uses high airtime and should only be used for network recovery.\n\nAre you sure you want to proceed?",
|
||
"menu.console_desc": "Direct meshcli commands",
|
||
"menu.contacts_desc": "Manage known contacts",
|
||
"menu.device_info": "Device Info",
|
||
"menu.dm_desc": "Private conversations",
|
||
"menu.exact_build_title": "Exact build",
|
||
"menu.filter": "Filter messages",
|
||
"menu.filter_desc": "Filter current chat",
|
||
"menu.flood_advert": "Flood Advert",
|
||
"menu.flood_advert_desc": "Network recovery only!",
|
||
"menu.flood_advert_title": "Flood advertisement - use sparingly! High airtime usage.",
|
||
"menu.history": "Message History",
|
||
"menu.logs_desc": "Real-time application logs",
|
||
"menu.map": "Map",
|
||
"menu.map_desc": "All contacts with GPS",
|
||
"menu.map_title": "Show all contacts with GPS on map",
|
||
"menu.pa_desc": "Routing path analysis",
|
||
"menu.refresh_desc": "Reload messages from server",
|
||
"menu.refresh_title": "Reload messages from the server",
|
||
"menu.repeaters_desc": "Repeater administration",
|
||
"menu.search": "Search messages",
|
||
"menu.search_desc": "Search all messages",
|
||
"menu.section.network": "Network",
|
||
"menu.section.system": "System",
|
||
"menu.section.tools": "Tools",
|
||
"menu.select_date_title": "Select date",
|
||
"menu.settings_desc": "Application settings",
|
||
"menu.title": "Menu",
|
||
"menu.today_live": "Today (Live)",
|
||
"meta.language_english_name": "English",
|
||
"meta.language_name": "English",
|
||
"meta.translator": "mc-webui",
|
||
"nav.ble_title": "Bluetooth Low Energy",
|
||
"nav.mark_all_read_title": "Mark all as read",
|
||
"nav.select_channel_title": "Select channel",
|
||
"nav.tcp_title": "TCP connection",
|
||
"notify.channels": {
|
||
"one": "{count} channel",
|
||
"other": "{count} channels"
|
||
},
|
||
"notify.dms": {
|
||
"one": "{count} private message",
|
||
"other": "{count} private messages"
|
||
},
|
||
"notify.new": "New: {parts}",
|
||
"notify.pending": {
|
||
"one": "{count} pending contact",
|
||
"other": "{count} pending contacts"
|
||
},
|
||
"notify.title_count": "{name} ({count})",
|
||
"observer.add_broker": "Add broker",
|
||
"observer.confirm.delete": "Delete broker \"{name}\"?",
|
||
"observer.counters": "packets captured: <strong>{seen}</strong>, published: <strong>{published}</strong>",
|
||
"observer.edit_broker": "Edit broker",
|
||
"observer.empty": "No brokers configured. Click \"Add broker\" to add one.",
|
||
"observer.host": "Host",
|
||
"observer.host_required": "Host is required",
|
||
"observer.iata_invalid": "Location code must be empty or exactly 3 letters",
|
||
"observer.load_failed": "Failed to load observer status",
|
||
"observer.name_ph": "e.g. My MQTT server",
|
||
"observer.name_required": "Name is required",
|
||
"observer.off": "Observer is <strong>off</strong>.",
|
||
"observer.password": "Password",
|
||
"observer.password_hint": "Leave the password blank to keep the current one.",
|
||
"observer.port": "Port",
|
||
"observer.port_invalid": "Port must be 1-65535",
|
||
"observer.ports_hint": "Typical ports: 1883 (plain), 8883 (TLS).",
|
||
"observer.state.connected": "connected",
|
||
"observer.state.error": "error",
|
||
"observer.state.offline": "offline",
|
||
"observer.state.running": "running",
|
||
"observer.state.waiting": "waiting",
|
||
"observer.toast.delete_error": "Network error deleting broker",
|
||
"observer.toast.delete_failed": "Failed to delete broker",
|
||
"observer.toast.save_error": "Network error saving broker",
|
||
"observer.toast.save_failed": "Failed to save broker",
|
||
"observer.toast.settings_error": "Network error saving observer settings",
|
||
"observer.toast.settings_failed": "Failed to save observer settings",
|
||
"observer.toast.update_error": "Network error updating broker",
|
||
"observer.toast.update_failed": "Failed to update broker",
|
||
"observer.use_tls": "Use TLS",
|
||
"observer.username": "Username",
|
||
"observer.verify_tls": "Verify TLS certificate",
|
||
"pa.ambiguous": "ambiguous ({count})",
|
||
"pa.any_hb": "Any HB",
|
||
"pa.any_hops": "Any hops",
|
||
"pa.byte_2_3": "2/3-byte",
|
||
"pa.candidates": {
|
||
"one": "{count} candidate",
|
||
"other": "{count} candidates"
|
||
},
|
||
"pa.clear": "Clear",
|
||
"pa.clear_filters_title": "Clear filters",
|
||
"pa.col.avgsnr": "Avg SNR (last hop)",
|
||
"pa.col.avgsnr_title": "Average echo SNR, counted only when this hash is the final hop - SNR is measured at our receiver for the last hop",
|
||
"pa.col.channel": "Channel",
|
||
"pa.col.contact": "Contact",
|
||
"pa.col.echoes": "Echoes",
|
||
"pa.col.echoes_seg_title": "Echoes whose path contains this segment (anywhere)",
|
||
"pa.col.hash": "Hash",
|
||
"pa.col.hb": "HB",
|
||
"pa.col.hb_title": "Path hash size in bytes per hop (from routed echoes)",
|
||
"pa.col.hops": "Hops",
|
||
"pa.col.lasthop": "As last hop",
|
||
"pa.col.lasthop_title": "Echoes where this hash is the final hop (heard directly by us)",
|
||
"pa.col.message": "Message",
|
||
"pa.col.message_plural": "Messages",
|
||
"pa.col.msgs_hash_title": "Distinct messages relayed through this hash",
|
||
"pa.col.msgs_seg_title": "Distinct messages whose path contains this segment",
|
||
"pa.col.pathend": "As path end",
|
||
"pa.col.pathend_title": "Echoes where this segment is the end of the path - the route the message took to reach us",
|
||
"pa.col.relayed": "Relayed",
|
||
"pa.col.relayed_title": "Echoes whose path contains this hash (anywhere)",
|
||
"pa.col.repeater": "Repeater",
|
||
"pa.col.route": "Route",
|
||
"pa.col.sender": "Sender",
|
||
"pa.col.time": "Time",
|
||
"pa.content_ph": "Message text",
|
||
"pa.content_title": "Filter by message content",
|
||
"pa.copy_hash_title": "Copy repeater hash {hash}",
|
||
"pa.copy_route_title": "Click to copy route",
|
||
"pa.copy_title": "Click to copy",
|
||
"pa.counter.filtered": "{shown} of {total} messages",
|
||
"pa.counter.messages": {
|
||
"one": "{count} message",
|
||
"other": "{count} messages"
|
||
},
|
||
"pa.counter.repeaters": {
|
||
"one": "{count} repeater",
|
||
"other": "{count} repeaters"
|
||
},
|
||
"pa.counter.routed": {
|
||
"one": "{count} routed message",
|
||
"other": "{count} routed messages"
|
||
},
|
||
"pa.counter.segments": {
|
||
"one": "{count} segment",
|
||
"other": "{count} segments"
|
||
},
|
||
"pa.direct_flood": "Direct (flood, 0 hops)",
|
||
"pa.empty_filtered": "No messages match the current filters.",
|
||
"pa.empty_no_echoes": "No routed echoes in the current selection.",
|
||
"pa.empty_range": "No messages in the selected time range.",
|
||
"pa.filter_by_segment_title": "Filter messages by this segment",
|
||
"pa.filter_by_title": "Filter messages by {token}",
|
||
"pa.filters": "Filters",
|
||
"pa.filters_toggle_title": "Show/hide filters",
|
||
"pa.hb_filter_title": "Filter by path hash size (bytes per hop, any routed echo)",
|
||
"pa.hops_4plus": "4+ hops",
|
||
"pa.hops_filter_title": "Filter by hop count (any echo)",
|
||
"pa.last_days": {
|
||
"one": "Last {count} day",
|
||
"other": "Last {count} days"
|
||
},
|
||
"pa.loading": "Loading messages…",
|
||
"pa.map.alt_path": "Alternative path",
|
||
"pa.map.candidate": "Candidate for hop {n}: <code>{token}</code>",
|
||
"pa.map.clear_title": "Clear drawn path",
|
||
"pa.map.exit_fullscreen": "Exit full screen",
|
||
"pa.map.fullscreen": "Maximize the map",
|
||
"pa.map.hint": "Click a message, then an echo to draw its path.",
|
||
"pa.map.hop": "Hop {n}: <code>{token}</code>",
|
||
"pa.map.no_routed": "No messages with routed echoes match the current filters.",
|
||
"pa.map.origin": "Origin (sender)",
|
||
"pa.me": "Me",
|
||
"pa.n_byte": {
|
||
"one": "{count}-byte",
|
||
"other": "{count}-byte"
|
||
},
|
||
"pa.n_hops": {
|
||
"one": "{count} hop",
|
||
"other": "{count} hops"
|
||
},
|
||
"pa.no_path_data": "no path data",
|
||
"pa.no_paths_min_hops": "No paths with at least {count} hops in the current selection.",
|
||
"pa.packet_hash": "Packet hash",
|
||
"pa.reload_title": "Reload",
|
||
"pa.repeater_hash": "Repeater hash",
|
||
"pa.reset_picks": "Reset picks",
|
||
"pa.reset_picks_title": "Clear all manual repeater assignments on this path",
|
||
"pa.seglen_title": "Number of consecutive hops per counted segment",
|
||
"pa.segment_length": "Segment length:",
|
||
"pa.sender_own": "{name} (own)",
|
||
"pa.sender_ph": "Sender",
|
||
"pa.sender_title": "Filter by sender name",
|
||
"pa.show_on_map_title": "Show this path on the map",
|
||
"pa.time_range": "Time range:",
|
||
"pa.title": "Path Analyzer",
|
||
"pa.toast.copied": "{what} copied to clipboard",
|
||
"pa.toast.copy_failed": "Failed to copy {what}",
|
||
"pa.toast.load_failed": "Failed to load messages: {error}",
|
||
"pa.toast.msg_gone": "This message is no longer in the analyzer data (max 7 days).",
|
||
"pa.toast.no_echoes": "This message has no routed echoes to draw.",
|
||
"pa.toggle_all_repeaters": "All repeaters",
|
||
"pa.toggle_alt_paths": "Alternative paths",
|
||
"pa.token_ph": "Repeater (3B or name)",
|
||
"pa.token_title": "Match a repeater anywhere in a path: hash prefix (hex) or contact name. Chain with '>' for a consecutive sequence, e.g. AFE6>6E9A",
|
||
"pa.undo_pick_title": "Undo this manual assignment",
|
||
"pa.unknown_position": "— unknown / no position",
|
||
"pa.use_contact_title": "Use this contact for the hop",
|
||
"pa.view.map": "Map",
|
||
"pa.view.map_title": "Map view",
|
||
"pa.view.messages": "Messages",
|
||
"pa.view.messages_title": "Message list",
|
||
"pa.view.repeaters": "Repeaters",
|
||
"pa.view.repeaters_title": "Per-repeater statistics",
|
||
"pa.view.routes": "Routes",
|
||
"pa.view.routes_title": "Route segment statistics",
|
||
"pa.view_aria": "View",
|
||
"quote.full": "Full quote",
|
||
"quote.max_bytes": "Max bytes:",
|
||
"quote.title": "Quote message",
|
||
"quote.truncated": "Truncated",
|
||
"repeaters.add": "Add repeater",
|
||
"repeaters.add_loading": "Loading device contacts...",
|
||
"repeaters.add_modal_title": "Add Repeater",
|
||
"repeaters.add_none_found": "No repeaters found on the device.",
|
||
"repeaters.add_search_ph": "Search repeaters on device...",
|
||
"repeaters.added_badge": "Added",
|
||
"repeaters.addpath.by_id": "ID",
|
||
"repeaters.addpath.by_name": "Name",
|
||
"repeaters.addpath.hash_1b": "1B (max 64)",
|
||
"repeaters.addpath.hash_2b": "2B (max 32)",
|
||
"repeaters.addpath.hash_3b": "3B (max 21)",
|
||
"repeaters.addpath.hash_size": "Hash Size",
|
||
"repeaters.addpath.label": "Label (optional)",
|
||
"repeaters.addpath.label_ph": "e.g. via Mountain RPT",
|
||
"repeaters.addpath.path_hex": "Path (hex)",
|
||
"repeaters.addpath.path_hex_ph": "e.g. 5e,e7 or 5e34,e761",
|
||
"repeaters.addpath.pick_list_title": "Pick repeater from list",
|
||
"repeaters.addpath.pick_map_title": "Pick repeater from map",
|
||
"repeaters.addpath.search_id_ph": "Search by first {chars} hex chars...",
|
||
"repeaters.addpath.search_ph": "Search by name...",
|
||
"repeaters.addpath.title": "Add Path",
|
||
"repeaters.confirm.clear_paths": "Clear all configured paths?\n\nThis will delete all paths from the database. The device path will not be changed.",
|
||
"repeaters.confirm.reset_flood": "Reset device path to FLOOD?\n\nThis resets the path on the device only. Your configured paths will be kept.",
|
||
"repeaters.confirm.set_direct": "Set device path to Direct?\n\nMessages will go straight to the node, with no repeater in between. This changes the path on the device only — your configured paths will be kept.",
|
||
"repeaters.count": {
|
||
"one": "{count} repeater",
|
||
"other": "{count} repeaters"
|
||
},
|
||
"repeaters.empty": "No repeaters yet.",
|
||
"repeaters.empty_hint": "Use <strong>Add repeater</strong> to pick repeaters stored on your device.",
|
||
"repeaters.empty_note": "Only repeaters saved in the device contacts can be managed.",
|
||
"repeaters.load_failed": "Failed to load repeaters",
|
||
"repeaters.map.cached": "Cached",
|
||
"repeaters.map.hint": "Click a repeater on the map",
|
||
"repeaters.map.last_seen": "Last seen: {time}",
|
||
"repeaters.map.title": "Select Repeater from Map",
|
||
"repeaters.on_device_count": {
|
||
"one": "{count} repeater on device",
|
||
"other": "{count} repeaters on device"
|
||
},
|
||
"repeaters.password.change_title": "Change password",
|
||
"repeaters.password.input_ph": "Repeater password",
|
||
"repeaters.password.login_btn": "Log in",
|
||
"repeaters.password.login_info": "Enter the repeater password to log in.",
|
||
"repeaters.password.login_title": "Log in",
|
||
"repeaters.password.remember": "Remember password (stored in the app database)",
|
||
"repeaters.password.retry_hint": "Check the password and try again.",
|
||
"repeaters.password.set_info": "The password is stored in the app database and used to log in to this repeater without asking again.",
|
||
"repeaters.password.set_title": "Set password",
|
||
"repeaters.password.toggle_title": "Show/hide password",
|
||
"repeaters.paths.add_title": "Add path",
|
||
"repeaters.paths.apply_title": "Set as device path",
|
||
"repeaters.paths.clear": "Clear Paths",
|
||
"repeaters.paths.clear_title": "Delete all configured paths from database",
|
||
"repeaters.paths.configured": "Configured paths",
|
||
"repeaters.paths.device_path": "Device path:",
|
||
"repeaters.paths.is_primary_title": "Primary path",
|
||
"repeaters.paths.load_failed": "Failed to load paths",
|
||
"repeaters.paths.none": "No paths configured. Use + to add.",
|
||
"repeaters.paths.reset_flood": "Reset to FLOOD",
|
||
"repeaters.paths.reset_flood_title": "Reset device path to FLOOD mode",
|
||
"repeaters.paths.set_direct": "Set Direct",
|
||
"repeaters.paths.set_direct_title": "Set device path to Direct (no repeaters)",
|
||
"repeaters.paths.set_primary_title": "Set as primary",
|
||
"repeaters.paths.title": "Paths",
|
||
"repeaters.picker.none": "No repeaters found",
|
||
"repeaters.picker.shared_prefix": {
|
||
"one": "{count} repeater shares this prefix",
|
||
"other": "{count} repeaters share this prefix"
|
||
},
|
||
"repeaters.refresh_title": "Refresh list",
|
||
"repeaters.remove.note": "The saved password will be deleted. The contact on the device is not affected.",
|
||
"repeaters.remove.question": "Remove <strong>{name}</strong> from My Repeaters?",
|
||
"repeaters.remove.title": "Remove Repeater",
|
||
"repeaters.row.last_login": "last login: {role}",
|
||
"repeaters.row.logging_in": "Logging in… (may take up to 60 s on flood paths)",
|
||
"repeaters.row.not_on_device": "Not stored on the device",
|
||
"repeaters.row.remove_title": "Remove from list",
|
||
"repeaters.row.set_path_title": "Set path",
|
||
"repeaters.term": "repeater",
|
||
"repeaters.title": "My Repeaters",
|
||
"repeaters.toast.add_failed": "Failed to add repeater",
|
||
"repeaters.toast.added": "{name} added",
|
||
"repeaters.toast.adjacent_dupes": {
|
||
"one": "Adjacent duplicate hop: {hops}",
|
||
"other": "Adjacent duplicate hops: {hops}"
|
||
},
|
||
"repeaters.toast.ambiguous_prefix": {
|
||
"one": "⚠ Ambiguous prefix: {hops}. Consider using a larger hash size.",
|
||
"other": "⚠ Ambiguous prefixes: {hops}. Consider using a larger hash size."
|
||
},
|
||
"repeaters.toast.clear_failed": "Clear failed",
|
||
"repeaters.toast.contacts_load_failed": "Failed to load device contacts",
|
||
"repeaters.toast.dupes": {
|
||
"one": "Duplicate hop: {hops}",
|
||
"other": "Duplicate hops: {hops}"
|
||
},
|
||
"repeaters.toast.hop_adjacent_self": "{hop} cannot be adjacent to itself",
|
||
"repeaters.toast.hop_already_used": "{hop} is already in the path",
|
||
"repeaters.toast.logged_in": "Logged in to {name} as {role}",
|
||
"repeaters.toast.login_failed": "Login failed",
|
||
"repeaters.toast.login_in_progress": "Another login is already in progress",
|
||
"repeaters.toast.not_on_device": "This repeater is not stored on the device — it cannot be managed",
|
||
"repeaters.toast.password_empty": "Password cannot be empty",
|
||
"repeaters.toast.password_save_failed": "Failed to save password",
|
||
"repeaters.toast.password_saved": "Password saved",
|
||
"repeaters.toast.path_add_failed": "Failed to add path",
|
||
"repeaters.toast.path_added": "Path added",
|
||
"repeaters.toast.path_duplicate": "This path is already on the list",
|
||
"repeaters.toast.path_hex_required": "Path hex is required",
|
||
"repeaters.toast.path_set_failed": "Failed to set device path",
|
||
"repeaters.toast.path_updated": "Device path updated",
|
||
"repeaters.toast.paths_cleared": {
|
||
"one": "{count} path cleared",
|
||
"other": "{count} paths cleared"
|
||
},
|
||
"repeaters.toast.remove_failed": "Failed to remove repeater",
|
||
"repeaters.toast.removed": "Repeater removed from list",
|
||
"repeaters.toast.reset_failed": "Reset failed",
|
||
"repeaters.toast.reset_flood_done": "Device path reset to FLOOD",
|
||
"repeaters.toast.set_direct_done": "Device path set to Direct",
|
||
"repeaters.toast.set_direct_failed": "Failed to set Direct path",
|
||
"rptmgmt.act.clock_sync": "Sync clock",
|
||
"rptmgmt.act.clock_sync_desc": "Set the repeater's clock from the server's current time. The firmware refuses to move the clock backwards.",
|
||
"rptmgmt.act.flood": "Send flood advert",
|
||
"rptmgmt.act.flood_desc": "Not recommended — the advert is flooded across the whole mesh (high network load).",
|
||
"rptmgmt.act.poweroff": "Power off repeater",
|
||
"rptmgmt.act.poweroff_btn": "Power off",
|
||
"rptmgmt.act.poweroff_desc": "Shuts the repeater down. It cannot be powered back on over the mesh — someone has to go to it. Requires firmware v1.17 or newer.",
|
||
"rptmgmt.act.reboot": "Reboot repeater",
|
||
"rptmgmt.act.reboot_btn": "Reboot",
|
||
"rptmgmt.act.reboot_desc": "The repeater drops off the mesh for a few seconds while it restarts.",
|
||
"rptmgmt.act.send": "Send",
|
||
"rptmgmt.act.sync": "Sync",
|
||
"rptmgmt.act.zerohop": "Send zero-hop advert",
|
||
"rptmgmt.act.zerohop_desc": "Announce this repeater to its direct neighbours only.",
|
||
"rptmgmt.action_failed": "Action failed",
|
||
"rptmgmt.admin_required": "Admin login required",
|
||
"rptmgmt.ago_dh": "{d}d {h}h ago",
|
||
"rptmgmt.ago_hm": "{h}h {m}m ago",
|
||
"rptmgmt.ago_ms": "{m}m {s}s ago",
|
||
"rptmgmt.ago_s": "{s}s ago",
|
||
"rptmgmt.back_title": "Back to My Repeaters",
|
||
"rptmgmt.back_to_list": "Back to list",
|
||
"rptmgmt.back_tools_title": "Back to tools",
|
||
"rptmgmt.cli.empty_reply": "(empty reply)",
|
||
"rptmgmt.cli.input_ph": "Enter command (e.g. get name)",
|
||
"rptmgmt.cli.intro": "Commands go to {name}. One command at a time — replies travel over the mesh.",
|
||
"rptmgmt.cli.the_repeater": "the repeater",
|
||
"rptmgmt.cli.waiting": "Waiting for reply…",
|
||
"rptmgmt.command_failed": "Command failed",
|
||
"rptmgmt.confirm.radio": "Change radio parameters to {params}?\n\nWrong values can make the repeater unreachable over the mesh. The change takes effect after a reboot.",
|
||
"rptmgmt.confirm.reboot": "Reboot {name}?\n\nIt will drop off the mesh for a few seconds. The firmware does not reply to this command.",
|
||
"rptmgmt.copy_pubkey_title": "Copy full public key",
|
||
"rptmgmt.danger_zone": "Danger zone",
|
||
"rptmgmt.done": "Done",
|
||
"rptmgmt.erase_fs_note": "Erase file system is not available over the mesh — the firmware only accepts it on the USB serial console (use the MeshCore flasher instead).",
|
||
"rptmgmt.error_generic": "Something went wrong.",
|
||
"rptmgmt.failed": "Failed",
|
||
"rptmgmt.invalid_pubkey": "Invalid repeater public key in URL.",
|
||
"rptmgmt.load_failed": "Failed to load repeater",
|
||
"rptmgmt.loc.hint": "Click the map to set the repeater location.",
|
||
"rptmgmt.loc.title": "Pick Location",
|
||
"rptmgmt.loc.use": "Use this location",
|
||
"rptmgmt.login.empty_password": "Password cannot be empty",
|
||
"rptmgmt.login.failed": "Login failed",
|
||
"rptmgmt.login.logged_in": "Logged in as {role}",
|
||
"rptmgmt.login.modal_title": "Log in — {name}",
|
||
"rptmgmt.login.progress": "Logging in to {name}… (may take up to 60 s on flood paths)",
|
||
"rptmgmt.login.prompt": "Enter the repeater password to log in.",
|
||
"rptmgmt.login.repeater_fallback": "repeater",
|
||
"rptmgmt.login.retry_hint": "Check the password and try again.",
|
||
"rptmgmt.login.waiting": "Waiting for password…",
|
||
"rptmgmt.login_request_failed": "Login request failed",
|
||
"rptmgmt.logout": "Logout",
|
||
"rptmgmt.logout_failed": "Logout failed",
|
||
"rptmgmt.logout_title": "Log out of this repeater",
|
||
"rptmgmt.managed_repeater": "managed repeater",
|
||
"rptmgmt.neigh.confirm_remove_all": "Remove all neighbours from {name}?\n\nThe list is stored on the repeater. Neighbours reappear as soon as they are heard again.",
|
||
"rptmgmt.neigh.count": {
|
||
"one": "{count} neighbor",
|
||
"other": "{count} neighbors"
|
||
},
|
||
"rptmgmt.neigh.discover": "Discover neighbours",
|
||
"rptmgmt.neigh.discover_sending": "Sending the discovery request…",
|
||
"rptmgmt.neigh.discover_sent": "Discovery request sent. Nearby repeaters can take up to 60 seconds to answer — refresh the list to see new neighbours.",
|
||
"rptmgmt.neigh.heard": "Heard",
|
||
"rptmgmt.neigh.heard_at": "Heard: {time}",
|
||
"rptmgmt.neigh.list": "List",
|
||
"rptmgmt.neigh.loading": "Requesting neighbours from the repeater…",
|
||
"rptmgmt.neigh.loading_hint": "Long lists are fetched in pages and can take a while.",
|
||
"rptmgmt.neigh.map": "Map",
|
||
"rptmgmt.neigh.menu_title": "Neighbour actions",
|
||
"rptmgmt.neigh.position": "Position",
|
||
"rptmgmt.neigh.position_title": "Position known",
|
||
"rptmgmt.neigh.pubkey": "Public key",
|
||
"rptmgmt.neigh.refresh_title": "Refresh neighbours",
|
||
"rptmgmt.neigh.remove_all": "Remove all neighbours",
|
||
"rptmgmt.neigh.remove_btn": "Remove from list",
|
||
"rptmgmt.neigh.remove_note": "The list lives on the repeater — a removed neighbour comes back as soon as it is heard again.",
|
||
"rptmgmt.neigh.removed_all": "Neighbour list cleared.",
|
||
"rptmgmt.neigh.removed_one": "{name} removed from the neighbour list.",
|
||
"rptmgmt.neigh.removing": "Removing…",
|
||
"rptmgmt.neigh.repeater": "Repeater",
|
||
"rptmgmt.neigh.row_hint": "Select a neighbour to see details.",
|
||
"rptmgmt.neigh.showing": "(showing {count})",
|
||
"rptmgmt.neighbours_failed": "Failed to get neighbours",
|
||
"rptmgmt.neighbours_none": "No neighbours reported yet.",
|
||
"rptmgmt.neighbours_none_hint": "Neighbours are learned from zero-hop repeater adverts.",
|
||
"rptmgmt.no_result": "No result",
|
||
"rptmgmt.not_on_device": "This repeater is not stored on the device — it cannot be managed.",
|
||
"rptmgmt.poweroff.no_name": "This repeater has no saved name, so the power-off confirmation cannot be completed.",
|
||
"rptmgmt.poweroff.prompt": "Type the repeater name ({name}) to confirm:",
|
||
"rptmgmt.poweroff.title": "Power off repeater",
|
||
"rptmgmt.poweroff.warning": "This cannot be undone over the mesh. The repeater will stay off until someone powers it on physically.",
|
||
"rptmgmt.pubkey_copied": "Public key copied",
|
||
"rptmgmt.read_failed": "Failed to read",
|
||
"rptmgmt.reboot_required": "reboot required",
|
||
"rptmgmt.reg.action_failed": "Region command failed",
|
||
"rptmgmt.reg.action_ok": "Done",
|
||
"rptmgmt.reg.add": "Add region",
|
||
"rptmgmt.reg.added": "Region {name} added",
|
||
"rptmgmt.reg.added_under": "Region {name} added inside {parent}",
|
||
"rptmgmt.reg.allow_flood": "Allow flood",
|
||
"rptmgmt.reg.confirm_leave": "You have region changes that have not been saved to the repeater yet.\n\nThey are active now but will be lost when it reboots. Leave without saving?",
|
||
"rptmgmt.reg.confirm_remove": "Remove region {name} from this repeater?\n\nA region that still has regions nested under it cannot be removed.",
|
||
"rptmgmt.reg.default_current": "This repeater currently scopes its own flood packets to {name}.",
|
||
"rptmgmt.reg.default_current_none": "This repeater currently sends its own flood packets unscoped.",
|
||
"rptmgmt.reg.default_help": "The region this repeater stamps on flood packets it sends itself. Only repeaters that allow that region will forward them. Saved on the repeater immediately — it does not need the Save button.",
|
||
"rptmgmt.reg.default_none": "— none (unscoped) —",
|
||
"rptmgmt.reg.default_pick_first": "Choose a default scope first, or refresh to read the current one.",
|
||
"rptmgmt.reg.default_saved": "Default scope updated",
|
||
"rptmgmt.reg.default_title": "Default scope",
|
||
"rptmgmt.reg.default_unknown": "— could not be read —",
|
||
"rptmgmt.reg.default_unread": "Could not read the default scope: {error}",
|
||
"rptmgmt.reg.deny_flood": "Deny flood",
|
||
"rptmgmt.reg.dirty_note": "Unsaved changes. They are already active, but the repeater will forget them when it reboots — use Save to keep them.",
|
||
"rptmgmt.reg.err_char": "That character is not allowed in a region name.",
|
||
"rptmgmt.reg.err_empty": "Enter a region name.",
|
||
"rptmgmt.reg.err_long": "Name too long (max {max} characters).",
|
||
"rptmgmt.reg.flood_allowed": "Flood allowed",
|
||
"rptmgmt.reg.flood_denied": "Flood denied",
|
||
"rptmgmt.reg.home": "home",
|
||
"rptmgmt.reg.intro": "Which flood-scoped packets this repeater passes on. A region set to Flood allowed is repeated; one set to Flood denied is dropped. Changes take effect at once but live in memory only until you save them.",
|
||
"rptmgmt.reg.menu_title": "Region actions",
|
||
"rptmgmt.reg.move": "Move to another region",
|
||
"rptmgmt.reg.move_confirm": "Move",
|
||
"rptmgmt.reg.move_help": "Choose the region to nest it under, or top level. Its own nested regions move with it, and stay in the same order underneath.",
|
||
"rptmgmt.reg.move_label": "Move {name} into",
|
||
"rptmgmt.reg.moved": "Region {name} moved into {parent}",
|
||
"rptmgmt.reg.moved_flood_lost": "{name} was moved, but its Flood denied setting could not be restored and it is now allowing flood. Set it again.",
|
||
"rptmgmt.reg.moved_root": "Region {name} moved to the top level",
|
||
"rptmgmt.reg.name_help": "Letters, digits and - $ # are allowed; no spaces. Max 30 characters.",
|
||
"rptmgmt.reg.name_placeholder": "Region name, e.g. pl",
|
||
"rptmgmt.reg.none": "This repeater has no regions configured.",
|
||
"rptmgmt.reg.parent": "Inside",
|
||
"rptmgmt.reg.parent_help": "Nest the new region inside an existing one, or leave it at the top level.",
|
||
"rptmgmt.reg.parent_root": "— top level —",
|
||
"rptmgmt.reg.read_failed": "Could not read the regions",
|
||
"rptmgmt.reg.reading": "Reading regions from the repeater…",
|
||
"rptmgmt.reg.refresh_title": "Re-read the region list from the repeater",
|
||
"rptmgmt.reg.remove": "Delete region",
|
||
"rptmgmt.reg.row_desc": "Which regions this repeater floods",
|
||
"rptmgmt.reg.save": "Save",
|
||
"rptmgmt.reg.save_failed": "Could not save the regions",
|
||
"rptmgmt.reg.save_title": "Write the region list to the repeater so it survives a reboot",
|
||
"rptmgmt.reg.saved": "Regions saved on the repeater",
|
||
"rptmgmt.reg.set_home": "Set as home region",
|
||
"rptmgmt.reg.title": "Regions",
|
||
"rptmgmt.reg.truncated": "The repeater sent more regions than fit in one reply, so this list may be incomplete. Use the CLI (`region list allowed`) to see the rest.",
|
||
"rptmgmt.reg.wildcard": "Packets without a region scope",
|
||
"rptmgmt.request_failed": "Request failed",
|
||
"rptmgmt.set.advanced": "Advanced",
|
||
"rptmgmt.set.advert": "Advertisement",
|
||
"rptmgmt.set.advert_interval": "Local advert interval (minutes)",
|
||
"rptmgmt.set.advert_interval_help": "Firmware accepts 60–240 minutes, or 0 to disable.",
|
||
"rptmgmt.set.agc_reset": "AGC reset interval",
|
||
"rptmgmt.set.agc_reset_help": "Multiple of 4; 0 disables periodic AGC resets.",
|
||
"rptmgmt.set.allow_read_only": "Allow read-only (guest) access",
|
||
"rptmgmt.set.allow_read_only_help": "Lets anyone log in with the guest password for read-only access: they can see status and settings, but change nothing.",
|
||
"rptmgmt.set.apply_count": "Apply ({count})",
|
||
"rptmgmt.set.basic": "Basic",
|
||
"rptmgmt.set.cad": "Channel activity detection (CAD)",
|
||
"rptmgmt.set.cad_help": "The radio checks whether the channel is busy before transmitting and defers if it is. Independent of the interference threshold. Requires firmware v1.17 or newer.",
|
||
"rptmgmt.set.direct_txdelay": "Direct TX delay factor (0–2)",
|
||
"rptmgmt.set.direct_txdelay_help": "The same random wait, for packets repeated along a known direct route instead of flooded. Usually lower than the flood delay, because a direct packet only concerns the repeaters on that one path.",
|
||
"rptmgmt.set.dutycycle": "Duty cycle (%)",
|
||
"rptmgmt.set.dutycycle_help": "The largest share of time the repeater may spend transmitting, in percent. 100 means no throttling; lower values make a busy repeater hold packets back. This replaced the older airtime factor (af) setting.",
|
||
"rptmgmt.set.features": "Features",
|
||
"rptmgmt.set.flood_advert_interval": "Flood advert interval (hours)",
|
||
"rptmgmt.set.flood_advert_interval_help": "How often the repeater floods a self-advert across the whole mesh, in hours. Firmware accepts 3–168, or 0 to stop flooding adverts. Every one of them reaches the entire network, so a short interval costs everybody airtime.",
|
||
"rptmgmt.set.flood_max": "Max flood hops",
|
||
"rptmgmt.set.flood_max_advert": "Max flood hops (adverts)",
|
||
"rptmgmt.set.flood_max_advert_help": "Hop limit applied to flooded adverts only. Default 8.",
|
||
"rptmgmt.set.flood_max_help": "Hop limit for every flood packet the repeater forwards: one that has already passed this many hops is dropped instead of repeated. Default 64. The two narrower limits below apply on top of this one.",
|
||
"rptmgmt.set.flood_max_unscoped": "Max flood hops (unscoped)",
|
||
"rptmgmt.set.flood_max_unscoped_help": "Hop limit for flood packets that carry no region scope. Default 64.",
|
||
"rptmgmt.set.guest_password": "Guest password",
|
||
"rptmgmt.set.guest_password_help": "Password for read-only guest logins.",
|
||
"rptmgmt.set.int_thresh": "Interference threshold",
|
||
"rptmgmt.set.int_thresh_help": "Interference threshold in dB above the measured noise floor: while the repeater hears something stronger than this it treats the channel as busy and waits before transmitting. 0 turns the check off. Works independently of CAD.",
|
||
"rptmgmt.set.intro": "Nothing is read from the repeater until you ask for it — every field is one round-trip through the mesh. Use the button next to a field to read that field, or Read all fields for the whole section. Values shown without a fresh read are the ones read last time.",
|
||
"rptmgmt.set.lat": "Latitude",
|
||
"rptmgmt.set.lat_help": "Latitude in decimal degrees, sent out with the repeater's adverts and used to place it on maps. Pick from map fills this in from a map click.",
|
||
"rptmgmt.set.load_errors": {
|
||
"one": "{count} field failed to load — Refresh retries the whole section.",
|
||
"other": "{count} fields failed to load — Refresh retries the whole section."
|
||
},
|
||
"rptmgmt.set.location": "Location",
|
||
"rptmgmt.set.lon": "Longitude",
|
||
"rptmgmt.set.lon_help": "Longitude in decimal degrees, sent out with the repeater's adverts and used to place it on maps. Pick from map fills this in from a map click.",
|
||
"rptmgmt.set.loop_detect": "Loop detection",
|
||
"rptmgmt.set.loop_detect_help": "Drops flood packets that keep coming back to this node — what a routing loop between neighbouring repeaters looks like from here. Stricter levels catch a loop sooner, but are also quicker to drop a genuine packet that simply travelled a long way round.",
|
||
"rptmgmt.set.map_pick": "Pick from map",
|
||
"rptmgmt.set.map_pick_title": "Read latitude and longitude first, then pick a point",
|
||
"rptmgmt.set.multi_acks": "Send multiple ACKs",
|
||
"rptmgmt.set.multi_acks_help": "How many acknowledgements the repeater sends back for a packet it delivers. 0 sends one; 1 sends it twice, which gets through more often at the cost of extra airtime.",
|
||
"rptmgmt.set.name": "Repeater name",
|
||
"rptmgmt.set.name_help": "The name this repeater advertises to the mesh and shows up under in contact lists. The firmware rejects unusual characters. Others see the new name after the repeater's next advert.",
|
||
"rptmgmt.set.network": "Network health",
|
||
"rptmgmt.set.operator": "Operator info",
|
||
"rptmgmt.set.owner_info": "Owner info",
|
||
"rptmgmt.set.owner_info_help": "Free-form operator / contact info shown to clients. Multiple lines allowed.",
|
||
"rptmgmt.set.password": "Admin password",
|
||
"rptmgmt.set.password_help": "Write-only — the current password is never shown. Changing it does not log out already active sessions.",
|
||
"rptmgmt.set.path_hash_mode": "Path hash mode (0–2)",
|
||
"rptmgmt.set.path_hash_mode_help": "Bytes per hop in the routing paths this repeater builds. 0 = 1 byte (shortest paths, most prefix collisions), 1 = 2 bytes, 2 = 3 bytes (longest, fewest collisions).",
|
||
"rptmgmt.set.pw_unchanged_ph": "(unchanged)",
|
||
"rptmgmt.set.radio": "Radio",
|
||
"rptmgmt.set.radio_note": "Frequency, bandwidth, SF and CR are applied after a reboot. TX power applies immediately.",
|
||
"rptmgmt.set.radio_params": "Radio parameters",
|
||
"rptmgmt.set.radio_params_help": "Frequency in MHz, bandwidth in kHz, spreading factor and coding rate. These must match the rest of your mesh exactly, or the repeater becomes invisible to it — including to you. Firmware limits: 150–2500 MHz, 7–500 kHz, SF 5–12, CR 5–8. Takes effect after a reboot.",
|
||
"rptmgmt.set.read_all": "Read all fields",
|
||
"rptmgmt.set.read_field_title": "Read this field from the repeater",
|
||
"rptmgmt.set.reading": "Reading from repeater… (one mesh round-trip per field)",
|
||
"rptmgmt.set.reboot_note": "Some changes take effect after a reboot — use Actions → Reboot.",
|
||
"rptmgmt.set.repeat": "Repeat packets",
|
||
"rptmgmt.set.repeat_help": "The master switch for forwarding. Off stops the node repeating other people's packets entirely — it still advertises itself and still answers admin commands, it just stops being a repeater.",
|
||
"rptmgmt.set.rxdelay": "RX delay base (0–20)",
|
||
"rptmgmt.set.rxdelay_help": "Staggers the repeating of flood packets by reception quality — a node that heard the packet well repeats it sooner, one that barely heard it waits and then drops it if a better-placed repeater got there first. 0 disables it, which is the firmware default.",
|
||
"rptmgmt.set.rxgain": "RX boosted gain",
|
||
"rptmgmt.set.rxgain_help": "Boosted LNA gain on receive: a little more sensitivity for slightly higher current draw. Not every radio chip has it — nodes without it report the field as not supported.",
|
||
"rptmgmt.set.stale_field_title": "From the last read — may be out of date",
|
||
"rptmgmt.set.stale_note": "The values above were read on {when} and may no longer be what the repeater holds. Read a field again to see its current value.",
|
||
"rptmgmt.set.tx": "TX power (dBm)",
|
||
"rptmgmt.set.tx_help": "Transmit power in dBm. The usable range depends on the board and on the power limits that apply where the repeater stands. Takes effect immediately.",
|
||
"rptmgmt.set.txdelay": "TX delay factor (0–2)",
|
||
"rptmgmt.set.txdelay_help": "Random wait before repeating a flood packet, as a multiple of that packet's airtime. It scatters neighbouring repeaters in time so they do not all answer at the same instant and collide. 0 repeats immediately.",
|
||
"rptmgmt.settings_apply_failed": "Failed to apply settings",
|
||
"rptmgmt.settings_read_failed": "Failed to read settings",
|
||
"rptmgmt.status.battery": "Battery",
|
||
"rptmgmt.status.clock": "Clock",
|
||
"rptmgmt.status.clock_fetch": "fetch",
|
||
"rptmgmt.status.duplicates": "Duplicates",
|
||
"rptmgmt.status.events": "Debug / error events",
|
||
"rptmgmt.status.last_rssi": "Last RSSI",
|
||
"rptmgmt.status.last_snr": "Last SNR",
|
||
"rptmgmt.status.loading": "Requesting status from the repeater…",
|
||
"rptmgmt.status.noise": "Noise floor",
|
||
"rptmgmt.status.packets": "Packet Statistics",
|
||
"rptmgmt.status.queue": "Queue length",
|
||
"rptmgmt.status.radio": "Radio Statistics",
|
||
"rptmgmt.status.received": "Received",
|
||
"rptmgmt.status.refresh_title": "Refresh status",
|
||
"rptmgmt.status.rx_airtime": "RX airtime",
|
||
"rptmgmt.status.rx_errors": "RX errors",
|
||
"rptmgmt.status.sent": "Sent",
|
||
"rptmgmt.status.system": "System Information",
|
||
"rptmgmt.status.tx_airtime": "TX airtime",
|
||
"rptmgmt.status.uptime": "Uptime",
|
||
"rptmgmt.status.utilization": "Channel utilization",
|
||
"rptmgmt.status_failed": "Failed to get status",
|
||
"rptmgmt.tel.channel": "Channel {n}",
|
||
"rptmgmt.tel.device_suffix": "device",
|
||
"rptmgmt.tel.loading": "Requesting telemetry from the repeater…",
|
||
"rptmgmt.tel.loading_hint": "Multi-hop paths can take up to a minute.",
|
||
"rptmgmt.tel.mcu_temp": "MCU temperature",
|
||
"rptmgmt.tel.refresh_title": "Refresh telemetry",
|
||
"rptmgmt.telemetry_failed": "Failed to get telemetry",
|
||
"rptmgmt.telemetry_none": "No telemetry data reported.",
|
||
"rptmgmt.this_repeater": "This repeater",
|
||
"rptmgmt.title": "Repeater Management",
|
||
"rptmgmt.toast.admin_required": "Admin login required for this tool",
|
||
"rptmgmt.toast.applied": "Settings applied",
|
||
"rptmgmt.toast.applied_reboot": "Applied — reboot required for some changes",
|
||
"rptmgmt.toast.apply_failed_count": {
|
||
"one": "{count} setting failed to apply",
|
||
"other": "{count} settings failed to apply"
|
||
},
|
||
"rptmgmt.toast.apply_partial_unsupported": {
|
||
"one": "Applied — {count} setting is not supported by this repeater",
|
||
"other": "Applied — {count} settings are not supported by this repeater"
|
||
},
|
||
"rptmgmt.toast.apply_unsupported_count": {
|
||
"one": "{count} setting is not supported by this repeater — nothing was applied",
|
||
"other": "{count} settings are not supported by this repeater — nothing was applied"
|
||
},
|
||
"rptmgmt.toast.field_empty": "\"{field}\" cannot be empty",
|
||
"rptmgmt.toast.password_synced": "Saved password updated to the new one",
|
||
"rptmgmt.tool_coming": "The <strong>{tool}</strong> tool is coming in a later stage.",
|
||
"rptmgmt.tools.actions": "Actions",
|
||
"rptmgmt.tools.actions_desc": "Advert, clock sync, reboot, power off",
|
||
"rptmgmt.tools.cli": "CLI",
|
||
"rptmgmt.tools.cli_desc": "Send text commands to the repeater",
|
||
"rptmgmt.tools.neighbors": "Neighbors",
|
||
"rptmgmt.tools.neighbors_desc": "Zero-hop repeaters heard",
|
||
"rptmgmt.tools.settings": "Settings",
|
||
"rptmgmt.tools.settings_desc": "Configure repeater parameters",
|
||
"rptmgmt.tools.status": "Status",
|
||
"rptmgmt.tools.status_desc": "Battery, radio and packet statistics",
|
||
"rptmgmt.tools.telemetry": "Telemetry",
|
||
"rptmgmt.tools.telemetry_desc": "Sensor channels (Cayenne LPP)",
|
||
"rptmgmt.tools_heading": "Management Tools",
|
||
"rptmgmt.unsupported": "not supported",
|
||
"rptmgmt.unsupported_hint": "This repeater's firmware or hardware does not have this setting.",
|
||
"rptmgmt.updated_ago": "Updated {time} ago",
|
||
"rptmgmt.updated_just_now": "Updated just now",
|
||
"search.count": {
|
||
"one": "{count} result",
|
||
"other": "{count} results"
|
||
},
|
||
"search.dm_received": "(received)",
|
||
"search.dm_sent": "(sent)",
|
||
"search.empty": "Search across all channel and direct messages",
|
||
"search.failed": "Search failed. Please try again.",
|
||
"search.fts5_ref": "Full FTS5 syntax reference",
|
||
"search.help_title": "Search syntax help",
|
||
"search.min_chars": "Type at least 2 characters to search",
|
||
"search.no_results": "No results for \"{query}\"",
|
||
"search.ph": "Search all messages...",
|
||
"search.searching": "Searching...",
|
||
"search.special_chars": "Special characters (<code>. , - :</code>) should be wrapped in quotes.",
|
||
"search.tip_and": "messages containing both words",
|
||
"search.tip_not": "hello but not world",
|
||
"search.tip_or": "either word",
|
||
"search.tip_phrase": "exact phrase",
|
||
"search.tip_prefix": "prefix match (hello,hellas...)",
|
||
"search.tips": "Search tips:",
|
||
"settings.analyzer.intro": "Add MeshCore Analyzer services to choose from when clicking <i class=\"bi bi-clipboard-data\"></i> under a group chat message. The URL must contain the <code>{packetHash}</code> placeholder — it is replaced with the message's packet hash.",
|
||
"settings.analyzer.services": "Analyzer Services",
|
||
"settings.analyzer.tip": "Tip: star one analyzer to use it without being asked. Clear the star to be prompted on every click. Disabled analyzers stay in the list but are hidden from the chooser.",
|
||
"settings.appear.btn_size": "Button size (px)",
|
||
"settings.appear.item": "Item",
|
||
"settings.appear.language": "Language",
|
||
"settings.appear.language_hint": "Applies to this browser and becomes the server default for others. Add your own by dropping a catalog into the translations folder — see <code>docs/translations.md</code>.",
|
||
"settings.appear.place_fab": "Quick Access",
|
||
"settings.appear.place_menu": "Main Menu",
|
||
"settings.appear.placement": "Placement",
|
||
"settings.appear.placement_aria": "{item} placement",
|
||
"settings.appear.position": "Position",
|
||
"settings.appear.quick_access": "Quick Access Buttons",
|
||
"settings.appear.reload_title": "Re-scan the translations folder",
|
||
"settings.appear.reset_default": "Reset to default",
|
||
"settings.appear.show_fab": "Show Quick Access buttons",
|
||
"settings.appear.show_fab_hint": "Off by default. When off, all items appear in the Main Menu.",
|
||
"settings.appear.spacing": "Spacing (px)",
|
||
"settings.appear.theme": "Theme",
|
||
"settings.appear.theme_dark": "Dark",
|
||
"settings.appear.theme_dark_desc": "Easy on the eyes, deep navy palette",
|
||
"settings.appear.theme_light": "Light",
|
||
"settings.appear.theme_light_desc": "Classic bright interface",
|
||
"settings.appear.toast.reload_failed": "Failed to reload translations",
|
||
"settings.appear.toast.reloaded": "Translations reloaded: {names}",
|
||
"settings.chat.autoclose": "Auto-close after (s)",
|
||
"settings.chat.autoclose_help": "Seconds before the route popup closes automatically",
|
||
"settings.chat.no_autoclose": "Don't close automatically",
|
||
"settings.chat.no_autoclose_help": "Popup stays open until you tap outside it",
|
||
"settings.chat.quote": "Quote",
|
||
"settings.chat.quote_len": "Quote length (bytes)",
|
||
"settings.chat.quote_len_help": "Default max UTF-8 bytes for truncated quotes",
|
||
"settings.chat.route_popup": "Route popup",
|
||
"settings.chat.route_popup_desc": "The popup shown when tapping the Route line under a message. Also applies to DMs.",
|
||
"settings.contacts.auto_ignore": "Automatically add new contacts to \"Ignored\"",
|
||
"settings.contacts.auto_ignore_help": "Every new advert is automatically marked as Ignored — no notifications, no badge. Contacts still appear under Existing Contacts (Cache) and can be promoted with 'To Device'. Requires Manual approval ON.",
|
||
"settings.contacts.manual_approval": "Manual approval enabled",
|
||
"settings.contacts.manual_approval_help": "When enabled, new contacts must be manually approved before they can communicate with your node",
|
||
"settings.contacts.suppress_notifs": "Suppress new advert notifications",
|
||
"settings.contacts.suppress_notifs_help": "Hide the badge over Contact Management and the browser notification when new pending contacts arrive. The Pending Contacts list itself still shows them. Requires Manual approval ON.",
|
||
"settings.device.bw": "Bandwidth (kHz)",
|
||
"settings.device.confirm.radio": "Changing radio settings will disconnect from the mesh network. Continue?",
|
||
"settings.device.cr": "Coding Rate",
|
||
"settings.device.freq": "Frequency (MHz)",
|
||
"settings.device.hash_1b": "1 byte (default)",
|
||
"settings.device.hash_2b": "2 bytes",
|
||
"settings.device.hash_3b": "3 bytes",
|
||
"settings.device.load_preset": "Load preset...",
|
||
"settings.device.name_ph": "Device name",
|
||
"settings.device.path_hash_mode": "Path hash mode",
|
||
"settings.device.path_hash_mode_help": "Bytes per hop in routing paths. 1B = shortest path, more collisions; 3B = longest, fewest collisions.",
|
||
"settings.device.public_info": "Public Info",
|
||
"settings.device.radio": "Radio Settings",
|
||
"settings.device.sf": "Spreading Factor",
|
||
"settings.device.share_pos": "Share position in advert",
|
||
"settings.device.share_pos_help": "Include GPS coordinates in device advertisement",
|
||
"settings.device.toast.cr_invalid": "Coding rate must be 5-8",
|
||
"settings.device.toast.freq_invalid": "Invalid frequency",
|
||
"settings.device.toast.info_save_failed": "Failed to save public info",
|
||
"settings.device.toast.info_saved": "Public info saved",
|
||
"settings.device.toast.name_empty": "Device name cannot be empty",
|
||
"settings.device.toast.radio_save_failed": "Failed to save radio settings",
|
||
"settings.device.toast.radio_saved": "Radio settings saved",
|
||
"settings.device.toast.sf_invalid": "Spreading factor must be 5-12",
|
||
"settings.device.toast.tx_invalid": "TX power must be 0-30 dBm",
|
||
"settings.device.tx_power": "TX Power (dBm)",
|
||
"settings.diag.captures": "Saved captures",
|
||
"settings.diag.debug_logs": "Record detailed logs",
|
||
"settings.diag.debug_logs_hint": "Raises the log level to DEBUG while recording. Leave this on — most of what the analysis needs is only written at that level.",
|
||
"settings.diag.duration": "Record for",
|
||
"settings.diag.indicator": "Recording",
|
||
"settings.diag.indicator_title": "A diagnostic capture is running. Open Settings → Diagnostics to stop it.",
|
||
"settings.diag.intro": "Records what the app receives from the radio into a file you can send to the maintainer. Use it when something is wrong and a screenshot cannot show why — a missing repeater badge, messages that never arrive.",
|
||
"settings.diag.note": "Note",
|
||
"settings.diag.note_ph": "e.g. no repeater badge on #general",
|
||
"settings.diag.privacy": "The capture contains the <strong>text of every message received while it runs</strong>, along with contact names and public keys. Only send it to someone you trust.",
|
||
"settings.diag.recording": "Recording…",
|
||
"settings.diag.reproduce_hint": "Now reproduce the problem — send the message that misbehaves, then come back here and stop the recording.",
|
||
"settings.diag.sharing": "Sending",
|
||
"settings.diag.sharing_hint": "Optional. With a token from the maintainer a capture can be uploaded straight from here; without one, download it and send it yourself.",
|
||
"settings.diag.start": "Start recording",
|
||
"settings.diag.stop": "Stop and save",
|
||
"settings.diag.upload_token": "Upload token",
|
||
"settings.diag.upload_token_ph": "paste the token you were given",
|
||
"settings.diag.upload_url": "Upload URL",
|
||
"settings.iface.breakpoint": "Sidebar breakpoint (px)",
|
||
"settings.iface.breakpoint_help": "Default: 992. Range: 600-2000.",
|
||
"settings.iface.breakpoint_reset_title": "Reset to default (992)",
|
||
"settings.iface.layout": "Layout",
|
||
"settings.iface.layout_desc": "Window width above which the channel/contact list is shown as a sidebar. Below this width it collapses to a dropdown at the top of the screen. Saved per device (browser).",
|
||
"settings.iface.pos_bottom_left": "Bottom left",
|
||
"settings.iface.pos_bottom_right": "Bottom right",
|
||
"settings.iface.pos_center": "Center",
|
||
"settings.iface.pos_top_left": "Top left",
|
||
"settings.iface.pos_top_right": "Top right",
|
||
"settings.iface.position": "Position on screen",
|
||
"settings.iface.toast.autoclose_invalid": "Invalid auto-close duration",
|
||
"settings.iface.toast_autoclose_help": "Seconds before a notification closes automatically",
|
||
"settings.iface.toast_desc": "Controls the small toasts shown after actions (e.g. \"Advert Sent\", errors).",
|
||
"settings.iface.toast_no_autoclose_help": "Notifications stay until dismissed via their close button",
|
||
"settings.msg.direct_header": "When path is known (DIRECT)",
|
||
"settings.msg.direct_interval_help": "Seconds to wait between direct retries",
|
||
"settings.msg.direct_retries": "Direct retries",
|
||
"settings.msg.direct_retries_help": "Attempts via known path before switching to flood",
|
||
"settings.msg.flood_header": "When no path (FLOOD)",
|
||
"settings.msg.flood_interval_help": "Seconds to wait between flood retries",
|
||
"settings.msg.flood_retries": "Flood retries",
|
||
"settings.msg.flood_retries_help": "Flood attempts after direct retries exhausted (when no configured paths)",
|
||
"settings.msg.grace": "Grace period (s)",
|
||
"settings.msg.grace_help": "Wait for late ACKs after all retries exhausted",
|
||
"settings.msg.interval": "Interval (s)",
|
||
"settings.msg.intro": "Retries are counted after the initial send, e.g. 3 retries = 4 total attempts.",
|
||
"settings.msg.max_retries": "Max retries",
|
||
"settings.msg.max_retries_help": "Flood retry attempts (also used after path rotation)",
|
||
"settings.msg.other": "Other",
|
||
"settings.notif.blocked": "Blocked",
|
||
"settings.notif.desc": "Browser notifications appear when the app is hidden or in the background.",
|
||
"settings.notif.profiles.add": "Add profile",
|
||
"settings.notif.profiles.add_rule": "Add rule",
|
||
"settings.notif.profiles.confirm.delete": "Delete profile \"{name}\"?",
|
||
"settings.notif.profiles.confirm.delete_in_use": "Delete profile \"{name}\"? Channels using it ({count}) will go back to notifying about every message.",
|
||
"settings.notif.profiles.desc": "A profile narrows what counts as news on a channel: only messages matching its rules raise the unread badge and post a notification. Assign it per channel with the bell in Manage Channels. Direct messages always notify.",
|
||
"settings.notif.profiles.edit": "Edit profile",
|
||
"settings.notif.profiles.empty": "No profiles yet. Click \"Add profile\" to create one.",
|
||
"settings.notif.profiles.in_use": {
|
||
"one": "{count} channel",
|
||
"other": "{count} channels"
|
||
},
|
||
"settings.notif.profiles.load_failed": "Failed to load profiles",
|
||
"settings.notif.profiles.match_all": "all rules match",
|
||
"settings.notif.profiles.match_any": "any rule matches",
|
||
"settings.notif.profiles.match_label": "Notify when",
|
||
"settings.notif.profiles.name_ph": "e.g. Mentions and webui",
|
||
"settings.notif.profiles.name_required": "Name is required",
|
||
"settings.notif.profiles.rule.mention": "Mentions me",
|
||
"settings.notif.profiles.rule.mention_hint": "looks for \"{name}\"",
|
||
"settings.notif.profiles.rule.no_name": "device name unknown — matches nothing until the device reports it",
|
||
"settings.notif.profiles.rule.sender": "Sender name contains",
|
||
"settings.notif.profiles.rule.text": "Message contains",
|
||
"settings.notif.profiles.rule.value_ph": "text or part of a word",
|
||
"settings.notif.profiles.rule_value_required": "Every text rule needs some text",
|
||
"settings.notif.profiles.rules": "Rules",
|
||
"settings.notif.profiles.rules_hint": "Text rules match a word or any part of one.",
|
||
"settings.notif.profiles.rules_required": "Add at least one rule",
|
||
"settings.notif.profiles.summary.all": "all of: {rules}",
|
||
"settings.notif.profiles.summary.any": "any of: {rules}",
|
||
"settings.notif.profiles.summary.mention": "mentions me",
|
||
"settings.notif.profiles.summary.sender": "from \"{value}\"",
|
||
"settings.notif.profiles.summary.text": "contains \"{value}\"",
|
||
"settings.notif.profiles.tip": "Matching ignores letter case and diacritics, so <code>krakow</code> also finds <em>Kraków</em>. A mention means your device name anywhere in the message, whether written as <code>@[name]</code> or plainly.",
|
||
"settings.notif.profiles.title": "Notification profiles",
|
||
"settings.notif.profiles.toast.delete_failed": "Failed to delete profile",
|
||
"settings.notif.profiles.toast.save_error": "Error saving profile",
|
||
"settings.notif.profiles.toast.save_failed": "Failed to save profile",
|
||
"settings.notif.toast.blocked_help": "Notifications are blocked. Change browser settings: Settings → Site Settings → Notifications",
|
||
"settings.notif.toast.denied": "Notifications blocked. Change browser settings to enable them.",
|
||
"settings.notif.toast.disabled": "Notifications disabled",
|
||
"settings.notif.toast.enabled": "Notifications enabled",
|
||
"settings.notif.toast.error": "Error enabling notifications",
|
||
"settings.notif.toast.unsupported": "Notifications are not supported in this browser",
|
||
"settings.notif.unavailable": "Unavailable",
|
||
"settings.notif.works_hidden": "Works when app is hidden",
|
||
"settings.observer.advert_interval": "Flood advert interval (h)",
|
||
"settings.observer.advert_interval_ph": "0 = off",
|
||
"settings.observer.brokers": "MQTT Brokers",
|
||
"settings.observer.enable": "Enable observer",
|
||
"settings.observer.hint": "The 3-letter location code is used in MQTT topics; leave it empty for the flat <code>meshcore/packets</code> topics. Advert interval 0 disables scheduled flood adverts.",
|
||
"settings.observer.iata": "Location code (IATA)",
|
||
"settings.observer.iata_ph": "e.g. KRK",
|
||
"settings.observer.intro": "Publish every mesh packet this device overhears to MQTT brokers, in the <code>meshcore-packet-capture</code> format (topics <code>meshcore/IATA/PUBKEY/packets</code>). Compatible with letsmesh-style packet analyzers.",
|
||
"settings.regions.confirm.delete": "Delete region \"{name}\"?\nChannels using this region will revert to no scope.",
|
||
"settings.regions.delete_title": "Delete region",
|
||
"settings.regions.empty": "No regions defined. Add one below.",
|
||
"settings.regions.intro": "Only repeaters allowing a region will forward messages tagged with it. Find standardised region names at <a href=\"https://regions.meshcore.nz/\" target=\"_blank\" rel=\"noopener\">regions.meshcore.nz</a>.",
|
||
"settings.regions.is_default": "default",
|
||
"settings.regions.load_failed": "Failed to load regions",
|
||
"settings.regions.name_ph": "Region name (e.g. pl, pl-ma)",
|
||
"settings.regions.none_row": "None — use firmware default",
|
||
"settings.regions.picker_empty": "No regions defined yet.",
|
||
"settings.regions.picker_manage": "Manage Regions",
|
||
"settings.regions.registry": "Region Registry",
|
||
"settings.regions.tip": "Tip: pick the default region via the radio button, or select <em>None</em> to fall back to the firmware default. The chosen region is also pushed to the firmware so any untagged channel uses it.",
|
||
"settings.regions.toast.add_error": "Network error adding region",
|
||
"settings.regions.toast.add_failed": "Failed to add region",
|
||
"settings.regions.toast.clear_error": "Network error clearing default",
|
||
"settings.regions.toast.clear_failed": "Failed to clear default region",
|
||
"settings.regions.toast.default_error": "Network error setting default",
|
||
"settings.regions.toast.default_failed": "Failed to set default region",
|
||
"settings.regions.toast.delete_error": "Network error deleting region",
|
||
"settings.regions.toast.delete_failed": "Failed to delete region",
|
||
"settings.regions.toast.name_invalid": "Invalid region name. Allowed: letters, digits, - $ # (max 30 bytes, no spaces).",
|
||
"settings.regions.toast.scope_error": "Network error saving region scope",
|
||
"settings.regions.toast.scope_failed": "Failed to save region scope",
|
||
"settings.reset_defaults": "Reset to defaults",
|
||
"settings.tab.analyzer": "Analyzer",
|
||
"settings.tab.appearance": "Appearance",
|
||
"settings.tab.chat": "Group Chat",
|
||
"settings.tab.contacts": "Contacts",
|
||
"settings.tab.diagnostics": "Diagnostics",
|
||
"settings.tab.interface": "Interface",
|
||
"settings.tab.messages": "Messages",
|
||
"settings.tab.notifications": "Notifications",
|
||
"settings.tab.observer": "Observer",
|
||
"settings.tab.regions": "Regions",
|
||
"settings.toast.invalid_value": "Invalid value for {field}",
|
||
"settings.toast.save_failed": "Failed to save settings",
|
||
"settings.toast.saved": "Settings saved",
|
||
"settings.toast.setting_network_error": "Network error saving setting",
|
||
"settings.toast.setting_save_failed": "Failed to save setting",
|
||
"share.btn_title": "Share a contact, channel or location",
|
||
"share.card.add_channel": "Add Channel",
|
||
"share.card.add_contact": "Add Contact",
|
||
"share.card.channel_conflict": "Name used by a different key",
|
||
"share.card.channel_joined": "Already joined",
|
||
"share.card.copy_coords": "Copy coordinates",
|
||
"share.card.from_you": "{distance} · {bearing} from you",
|
||
"share.card.hashtag_channel": "Hashtag Channel",
|
||
"share.card.in_cache": "In cache",
|
||
"share.card.in_contacts": "Already in contacts",
|
||
"share.card.open_apple": "Apple Maps",
|
||
"share.card.open_google": "Google Maps",
|
||
"share.card.open_in_map": "Shared location",
|
||
"share.card.open_osm": "OpenStreetMap",
|
||
"share.card.private_channel": "Private Channel",
|
||
"share.card.push_to_device": "Push to device",
|
||
"share.card.region": "Region: {name}",
|
||
"share.card.update_name": "Update name to {name}",
|
||
"share.card.view_on_map": "View on map",
|
||
"share.card.you_shared_channel": "Shared channel",
|
||
"share.card.you_shared_contact": "Shared contact",
|
||
"share.menu.channel": "Share Channel",
|
||
"share.menu.contact": "Share Contact",
|
||
"share.menu.location": "Share Location",
|
||
"share.pick.channel_title": "Share a channel",
|
||
"share.pick.contact_title": "Share a contact",
|
||
"share.pick.empty": "Nothing to share here yet",
|
||
"share.pick.location_title": "Share a location",
|
||
"share.pick.map_hint": "Or tap the map to pick any point",
|
||
"share.pick.me": "Me",
|
||
"share.pick.my_position": "My device position",
|
||
"share.pick.no_position": "This device has no position set. Add one in Settings, or pick a point on the map below.",
|
||
"share.pick.search_ph": "Search contacts...",
|
||
"share.pick.truncated": "Showing {shown} of {total} — type to narrow it down",
|
||
"share.preview.label": "Will be sent as:",
|
||
"share.toast.action_failed": "Action failed",
|
||
"share.toast.channel_unshareable": "This channel has no key to share",
|
||
"share.toast.contact_added": "Contact {name} added to cache",
|
||
"share.toast.contact_pushed": "{name} saved on the device",
|
||
"share.toast.contact_updated": "Contact renamed to {name}",
|
||
"share.toast.map_unavailable": "The map is not available here",
|
||
"share.toast.over_budget": "The message is now {bytes} of {max} bytes and will not send",
|
||
"share.toast.scope_applied": "Region scope {name} applied to this channel",
|
||
"share.toast.scope_unknown": "Region \"{name}\" is not configured here — add it in Settings to match the sender's scope",
|
||
"share.toast.too_long": "This does not fit in one message ({bytes} of {max} bytes)",
|
||
"share.type.1": "Companion",
|
||
"share.type.2": "Repeater",
|
||
"share.type.3": "Room Server",
|
||
"share.type.4": "Sensor",
|
||
"update.checking": "Checking for updates...",
|
||
"update.done": "Updated to {version}",
|
||
"update.link_available": "Update available",
|
||
"update.link_available_title": "Update available: {version}",
|
||
"update.link_now": "Update now",
|
||
"update.link_now_title": "Click to update",
|
||
"update.network_error": "Network error during update",
|
||
"update.new_version": "New version available: {version}",
|
||
"update.no_new_commit": "Already on the newest commit - nothing was pulled.",
|
||
"update.no_new_image": "The change is already on GitHub, but its Docker image is still being built. Try again in a few minutes.",
|
||
"update.now": "Update Now",
|
||
"update.rebuilding": "Rebuilding containers... ({attempt}/{max})",
|
||
"update.reload": "Reload Page",
|
||
"update.start_failed": "Failed to start update",
|
||
"update.starting": "Starting update...",
|
||
"update.timed_out": "Update timed out. Please check server manually.",
|
||
"update.title": "Update mc-webui",
|
||
"update.toast.available": "Update available: {version}",
|
||
"update.toast.check_error": "Network error checking for updates",
|
||
"update.toast.check_failed": "Failed to check for updates",
|
||
"update.toast.latest": "You are running the latest version",
|
||
"update.updating": "Updating...",
|
||
"update.waiting": "Update started. Waiting for server to restart...",
|
||
"update.whats_new": "What's new?"
|
||
}
|