- Rename ChannelVisibility.PUBLIC to ChannelVisibility.COMMUNITY - Update stored value from 'public' to 'community' across model, schema, API, CLI, and frontend - Add Alembic migration to update existing database rows - Consolidate upgrade docs: merge v0.11.0, v0.12.0, v0.13.0 into single v0.11.0 section - Add i18n visibility level translation keys (en, nl) - Update section headings on channels page to use t() for i18n - Keep visibility badges lowercase per UI design
21 KiB
Translation Reference Guide
This document provides a comprehensive reference for translating the MeshCore Hub web dashboard.
File Structure
Translation files are JSON files named by language code (e.g., en.json, es.json, fr.json) and located in /src/meshcore_hub/web/static/locales/.
Variable Interpolation
Many translations use {{variable}} syntax for dynamic content. These must be preserved exactly:
"total": "{{count}} total"
When translating, keep the variable names unchanged:
"total": "{{count}} au total" // French example
Translation Sections
1. entities
Core entity names used throughout the application. These are referenced by other translations for composition.
| Key | English | Context |
|---|---|---|
home |
Home | Homepage/breadcrumb navigation |
dashboard |
Dashboard | Main dashboard page |
nodes |
Nodes | Mesh network nodes (plural) |
node |
Node | Single mesh network node |
node_detail |
Node Detail | Node details page |
advertisements |
Adverts | Network advertisements (plural, used in nav menus and hero cards) |
advertisement |
Advert | Single advertisement |
messages |
Messages | Network messages (plural) |
message |
Message | Single message |
map |
Map | Network map page |
members |
Members | Network members (plural) |
member |
Member | Single network member |
tags |
Tags | Node metadata tags (plural) |
tag |
Tag | Single tag |
channel |
Channel | Mesh network channel |
Usage: These are used with composite patterns. For example, t('common.add_entity', { entity: t('entities.node') }) produces "Add Node".
2. common
Reusable patterns and UI elements used across multiple pages.
Actions
| Key | English | Context |
|---|---|---|
filter |
Filter | Filter button/action |
clear |
Clear | Clear action |
clear_filters |
Clear Filters | Reset all filters |
search |
Search | Search button/action |
cancel |
Cancel | Cancel button in dialogs |
delete |
Delete | Delete button |
edit |
Edit | Edit button |
move |
Move | Move button |
save |
Save | Save button |
save_changes |
Save Changes | Save changes button |
add |
Add | Add button |
close |
close | Close button (lowercase for accessibility) |
sign_in |
Sign In | Authentication sign in |
sign_out |
Sign Out | Authentication sign out |
go_home |
Go Home | Return to homepage button |
Composite Patterns with Entity
These patterns use {{entity}} variable - the entity name is provided dynamically:
| Key | English | Example Output |
|---|---|---|
add_entity |
Add {{entity}} | "Add Node", "Add Tag" |
add_new_entity |
Add New {{entity}} | "Add New Member" |
edit_entity |
Edit {{entity}} | "Edit Tag" |
delete_entity |
Delete {{entity}} | "Delete Member" |
delete_all_entity |
Delete All {{entity}} | "Delete All Tags" |
move_entity |
Move {{entity}} | "Move Tag" |
move_entity_to_another_node |
Move {{entity}} to Another Node | "Move Tag to Another Node" |
copy_entity |
Copy {{entity}} | "Copy Tags" |
copy_all_entity_to_another_node |
Copy All {{entity}} to Another Node | "Copy All Tags to Another Node" |
view_entity |
View {{entity}} | "View Node" |
recent_entity |
Recent {{entity}} | "Recent Advertisements" |
total_entity |
Total {{entity}} | "Total Nodes" |
all_entity |
All {{entity}} | "All Messages" |
Empty State Patterns
These patterns indicate when data is absent. Use {{entity}} in lowercase (e.g., "nodes", not "Nodes"):
| Key | English | Context |
|---|---|---|
no_entity_found |
No {{entity}} found | Search/filter returned no results |
no_entity_recorded |
No {{entity}} recorded | No historical records exist |
no_entity_defined |
No {{entity}} defined | No configuration/definitions exist |
no_entity_in_database |
No {{entity}} in database | Database is empty |
no_entity_configured |
No {{entity}} configured | System not configured |
no_entity_yet |
No {{entity}} yet | Empty state, expecting data later |
entity_not_found_details |
{{entity}} not found: {{details}} | Specific item not found with details |
page_not_found |
Page not found | 404 error message |
Confirmation Patterns
Used in delete/move dialogs. Variables: {{entity}}, {{name}}, {{count}}:
| Key | English | Context |
|---|---|---|
delete_entity_confirm |
Are you sure you want to delete {{entity}} {{name}}? | Single item delete confirmation |
delete_all_entity_confirm |
Are you sure you want to delete all {{count}} {{entity}} from {{name}}? | Bulk delete confirmation |
cannot_be_undone |
This action cannot be undone. | Warning in delete dialogs |
Success Messages
Toast/flash messages after successful operations:
| Key | English | Context |
|---|---|---|
entity_added_success |
{{entity}} added successfully | After creating new item |
entity_updated_success |
{{entity}} updated successfully | After updating item |
entity_deleted_success |
{{entity}} deleted successfully | After deleting item |
entity_moved_success |
{{entity}} moved successfully | After moving tag to another node |
all_entity_deleted_success |
All {{entity}} deleted successfully | After bulk delete |
copy_all_entity_description |
Copy all {{count}} {{entity}} from {{name}} to another node. | Copy operation description |
Navigation & Status
| Key | English | Context |
|---|---|---|
previous |
Previous | Pagination previous |
next |
Next | Pagination next |
loading |
Loading... | Loading indicator |
error |
Error | Error state |
failed_to_load_page |
Failed to load page | Page load error |
Counts & Metrics
| Key | English | Context |
|---|---|---|
total |
{{count}} total | Total count display |
shown |
{{count}} shown | Filtered count display |
count_entity |
{{count}} {{entity}} | Generic count with entity |
Form Fields & Labels
| Key | English | Context |
|---|---|---|
type |
Type | Type field/column header |
name |
Name | Name field/column header |
key |
Key | Key field (for tags) |
value |
Value | Value field (for tags) |
time |
Time | Time column header |
actions |
Actions | Actions column header |
updated |
Updated | Last updated timestamp |
view_details |
View Details | View details link |
all_types |
All Types | "All types" filter option |
all_channels |
All Channels | "All channels" filter option |
all_members |
All Members | "All members" filter option (only shown when OIDC is enabled) |
all_operators |
All Operators | "All operators" filter option for operator-only filter dropdowns |
filter_member_label |
Member | Label for member filter dropdown |
filter_operator_label |
Operator | Label for operator filter dropdown (used on Nodes, Advertisements, Map pages) |
node_type |
Node Type | Node type field |
show |
Show | Show/display action |
search_placeholder |
Search by name, ID, or public key... | Search input placeholder |
contact |
Contact | Contact information field |
description |
Description | Description field |
callsign |
Callsign | Amateur radio callsign field |
tags |
Tags | Tags label/header |
last_seen |
Last Seen | Last seen timestamp (table header) |
first_seen_label |
First seen: | First seen label (inline with colon) |
last_seen_label |
Last seen: | Last seen label (inline with colon) |
location |
Location | Geographic location |
public_key |
Public Key | Node public key |
received |
Received | Received timestamp |
received_by |
Received By | Received by field |
receivers |
Receivers | Multiple receivers |
from |
From | Message sender |
hops |
Hops | Number of mesh hops (table header) |
observers |
Observers | Observers of an event |
snr_db |
SNR (dB) | Signal-to-noise ratio in decibels (table header) |
unnamed |
Unnamed | Fallback for unnamed items |
unnamed_node |
Unnamed Node | Fallback for unnamed nodes |
sort_by |
Sort by | Label before mobile sort dropdown |
Note: Keys ending in _label have colons and are used inline. Keys without _label are for table headers.
3. links
Platform and external link labels:
| Key | English | Context |
|---|---|---|
website |
Website | Website link label |
github |
GitHub | GitHub link label (preserve capitalization) |
discord |
Discord | Discord link label |
youtube |
YouTube | YouTube link label (preserve capitalization) |
profile |
Profile | Radio profile label |
4. auto_refresh
Auto-refresh controls for list pages (nodes, advertisements, messages):
| Key | English | Context |
|---|---|---|
pause |
Pause auto-refresh | Tooltip on pause button when auto-refresh is active |
resume |
Resume auto-refresh | Tooltip on play button when auto-refresh is paused |
5. time
Time-related labels and formats:
| Key | English | Context |
|---|---|---|
days_ago |
{{count}}d ago | Days ago (abbreviated) |
hours_ago |
{{count}}h ago | Hours ago (abbreviated) |
minutes_ago |
{{count}}m ago | Minutes ago (abbreviated) |
less_than_minute |
<1m ago | Less than one minute ago |
last_7_days |
Last 7 days | Last 7 days label |
per_day_last_7_days |
Per day (last 7 days) | Per day over last 7 days |
over_time_last_7_days |
Over time (last 7 days) | Over time last 7 days |
activity_per_day_last_7_days |
Activity per day (last 7 days) | Activity chart label |
6. node_types
Mesh network node type labels:
| Key | English | Context |
|---|---|---|
chat |
Chat | Chat node type |
repeater |
Repeater | Repeater/relay node type |
companion |
Companion | Companion/observer node type |
room |
Room Server | Room server/group node type |
unknown |
Unknown | Unknown node type fallback |
7. home
Homepage-specific content:
| Key | English | Context |
|---|---|---|
welcome_default |
Welcome to the {{network_name}} mesh network dashboard. Monitor network activity, view connected nodes, and explore message history. | Default welcome message |
all_discovered_nodes |
All discovered nodes | Stat description |
network_info |
Network Info | Network info card title |
network_activity |
Network Activity | Activity chart title |
frequency |
Frequency | Radio frequency label |
bandwidth |
Bandwidth | Radio bandwidth label |
spreading_factor |
Spreading Factor | LoRa spreading factor label |
coding_rate |
Coding Rate | LoRa coding rate label |
tx_power |
TX Power | Transmit power label |
advertisements |
Adverts | Homepage stat label |
messages |
Messages | Homepage stat label |
Note: MeshCore tagline "Off-Grid, Open-Source Encrypted Messaging" is hardcoded in English and should not be translated (trademark).
8. dashboard
Dashboard page content:
| Key | English | Context |
|---|---|---|
all_discovered_nodes |
All discovered nodes | Stat label |
recent_channel_messages |
Recent Channel Messages | Recent messages card title |
channel |
Channel {{number}} | Channel label with number |
9. nodes
Node detail page labels:
| Key | English | Context |
|---|---|---|
scan_to_add |
Scan to add as contact | QR code instruction |
ownership |
Ownership | Adoption card heading on node detail page |
adopt |
Adopt | Adopt button (operator/admin only) |
release |
Release | Release button (owner or admin only) |
adopted_by |
Adopted by {{name}} | Display name of adopting user ({{name}} = user name or ID) |
not_adopted |
This node has not been adopted by any operator. | Shown when node is unadopted and user is operator/admin |
adopt_success |
Node adopted successfully | Flash message after adopting |
release_success |
Node released successfully | Flash message after releasing |
release_confirm |
Are you sure you want to release this node? | Confirmation dialog for release |
Sort Options (nodes.sort)
Mobile sort dropdown labels for the nodes list page:
| Key | English | Context |
|---|---|---|
sort.last_seen_newest |
Last Seen (newest) | Default sort: most recently seen first |
sort.last_seen_oldest |
Last Seen (oldest) | Least recently seen first |
sort.name_az |
Name (A–Z) | Alphabetical by display name |
sort.name_za |
Name (Z–A) | Reverse alphabetical by display name |
sort.key_asc |
Public Key (ascending) | Sorted by public key ascending |
sort.key_desc |
Public Key (descending) | Sorted by public key descending |
10. advertisements
Route type filter and sort options for the advertisements list page:
Route Type Filter
| Key | English | Context |
|---|---|---|
filter_route_type_label |
Advert Type | Label for route type filter dropdown |
route_type_all |
All | Show all route types (no filter) |
route_type_flood |
Flood & Relay | Show flood and transport_flood adverts (default) |
route_type_direct |
Zero-hop only | Show only direct (zero-hop) adverts |
route_type_unknown |
Unknown | Displayed when route_type is NULL (historical records) |
col_route_type |
Type | Table column header for route type |
Sort Options (advertisements.sort)
Mobile sort dropdown labels for the advertisements list page:
| Key | English | Context |
|---|---|---|
sort.newest |
Time (newest) | Default sort: most recent first |
sort.oldest |
Time (oldest) | Oldest first |
sort.node_az |
Node (A–Z) | Alphabetical by node display name |
sort.node_za |
Node (Z–A) | Reverse alphabetical by node display name |
sort.key_asc |
Public Key (ascending) | Sorted by public key ascending |
sort.key_desc |
Public Key (descending) | Sorted by public key descending |
11. messages
Message type labels:
| Key | English | Context |
|---|---|---|
type_direct |
Direct | Direct message type |
type_channel |
Channel | Channel message type |
type_contact |
Contact | Contact message type |
type_public |
Public | Public message type |
Sort Options (messages.sort)
Mobile sort dropdown labels for the messages list page:
| Key | English | Context |
|---|---|---|
sort.newest |
Time (newest) | Default sort: most recent first |
sort.oldest |
Time (oldest) | Oldest first |
sort.type_az |
Type (A–Z) | Alphabetical by message type |
sort.type_za |
Type (Z–A) | Reverse alphabetical by message type |
sort.from_az |
From (A–Z) | Alphabetical by sender name |
sort.from_za |
From (Z–A) | Reverse alphabetical by sender name |
sort.message_az |
Message (A–Z) | Alphabetical by message text |
sort.message_za |
Message (Z–A) | Reverse alphabetical by message text |
12. map
Map page content:
| Key | English | Context |
|---|---|---|
show_labels |
Show Labels | Toggle to show node labels |
infrastructure_only |
Infrastructure Only | Toggle to show only infrastructure nodes (OIDC required, only rendered when OIDC_ENABLED=true; data sourced from node adoption, not tags) |
legend |
Legend: | Map legend header (only rendered when OIDC_ENABLED=true) |
infrastructure |
Infrastructure | Infrastructure node category (only rendered when OIDC_ENABLED=true; based on adoption status) |
public |
Public | Public node category |
nodes_on_map |
{{count}} nodes on map | Status text with coordinates |
nodes_none_have_coordinates |
{{count}} nodes (none have coordinates) | Status text without coordinates |
gps_description |
Nodes are placed on the map based on GPS coordinates from node reports or manual tags. | Map data source explanation |
owner |
Owner: | Node owner label |
role |
Role: | Member role label |
select_destination_node |
-- Select destination node -- | Dropdown placeholder |
13. members
Members page content:
| Key | English | Context |
|---|---|---|
empty_state_description |
No members yet. | Empty state heading |
empty_description |
Members will appear here once users log in and adopt nodes. | Empty state description |
14. channels
Channel management and filter UI:
| Key | English | Context |
|---|---|---|
title |
Channels | Page title |
add_channel |
Add Channel | Add button label |
edit_channel |
Edit Channel | Edit modal title |
delete_channel |
Delete Channel | Delete modal title |
delete_confirm |
Are you sure you want to delete channel {{name}}? | Delete confirmation message |
name_label |
Channel Name | Form label |
key_label |
Channel Key (hex) | Form label |
visibility_label |
Visibility | Form label |
visibility_community |
Community | Community visibility section heading |
visibility_member |
Member | Member visibility section heading |
visibility_operator |
Operator | Operator visibility section heading |
visibility_admin |
Admin | Admin visibility section heading |
enabled_label |
Enabled | Form label |
channel_hash_label |
Hash | Column header |
disabled |
Disabled | Disabled channel badge |
optgroup_standard |
Standard | Optgroup label for built-in channels (Public, Test) in channel filter dropdown |
optgroup_custom |
Custom | Optgroup label for user-defined channels in channel filter dropdown |
15. not_found
404 page content:
| Key | English | Context |
|---|---|---|
description |
The page you're looking for doesn't exist or has been moved. | 404 description |
16. custom_page
Custom markdown page errors:
| Key | English | Context |
|---|---|---|
failed_to_load |
Failed to load page | Page load error |
17. auth
Authentication UI:
| Key | English | Context |
|---|---|---|
login |
Login | Login button text |
logout |
Logout | Logout menu item |
login_required |
Login required | Login required heading |
admin_required |
Admin access required | Admin access denied message |
login_hint |
Log in to access admin features | Hint shown to non-admin users |
logged_in_as |
Logged in as {{name}} | Logged in status ({{name}} = user display name) |
session_expired |
Session expired, please log in again | Session expiry notice |
role_admin |
admin | Admin role badge text |
role_member |
member | Member role badge text |
18. footer
Footer content:
| Key | English | Context |
|---|---|---|
powered_by |
Powered by | "Powered by" attribution |
19. user_profile
User profile page (OIDC authenticated users):
| Key | English | Context |
|---|---|---|
title |
Profile | Page heading |
your_profile |
Your Profile | Profile card heading |
profile_updated |
Profile updated successfully | Flash message after save |
save_profile |
Save Profile | Save button label |
name_label |
Display Name | Form label |
name_placeholder |
Your name or preferred name | Input placeholder |
callsign_label |
Callsign | Form label |
callsign_placeholder |
Amateur radio callsign (e.g., W1ABC) | Input placeholder |
description_label |
Description | Form label |
description_placeholder |
A short bio or description | Input placeholder |
url_label |
Website / Profile Link | Form label |
url_placeholder |
https://... | Input placeholder |
adopted_nodes |
Adopted Nodes | Adopted nodes card heading |
no_adopted_nodes |
No adopted nodes | Empty state |
login_to_view |
Log in to view your profile | Unauthenticated notice |
Translation Tips
-
Preserve HTML tags: Some strings contain
<code>,<strong>, or<br/>tags - keep these intact. -
Preserve variables: Keep
{{variable}}placeholders exactly as-is, only translate surrounding text. -
Entity composition: Many translations reference
entities.*keys. When translating entities, consider how they'll work in composite patterns (e.g., "Add {{entity}}" should make sense with "Node", "Tag", etc.). -
Capitalization:
- Entity names should follow your language's capitalization rules for UI elements
- Inline labels (with colons) typically use sentence case
- Table headers typically use title case
- Action buttons can vary by language convention
-
Colons: Keys ending in
_labelinclude colons in English. Adjust punctuation to match your language's conventions for inline labels. -
Plurals: Some languages have complex plural rules. You may need to add plural variants for
{{count}}patterns. Consult the i18n library documentation for plural support. -
Length: UI space is limited. Try to keep translations concise, especially for button labels and table headers.
-
Brand names: Preserve "MeshCore", "GitHub", "YouTube" capitalization.
Testing Your Translation
- Create your translation file:
locales/xx.json(wherexxis your language code) - Copy the structure from
en.json - Translate all values, preserving all variables and HTML
- Test in the application by setting the language
- Check all pages for:
- Text overflow/truncation
- Proper variable interpolation
- Natural phrasing in context
Getting Help
If you're unsure about the context of a translation key, check:
- The "Context" column in this reference
- The JavaScript files in
/src/meshcore_hub/web/static/js/spa/pages/ - Grep for the key:
grep -r "t('section.key')" src/