Raccoglie eventi aggregati dietro consenso cookie, senza PII né tracking su admin/regia/live/replay; GA4 resta invariato. Co-authored-by: Cursor <cursoragent@cursor.com>
531 lines
19 KiB
YAML
531 lines
19 KiB
YAML
en:
|
|
admin:
|
|
layout:
|
|
title: "Match Live TV — Admin"
|
|
nav:
|
|
dashboard: Dashboard
|
|
ops: Ops
|
|
announcements: Notices
|
|
clubs: Clubs & teams
|
|
billing: Billing
|
|
youtube: YouTube
|
|
sessions: Sessions
|
|
analytics: Analytics
|
|
stream_nodes: Stream nodes
|
|
password: Password
|
|
logout: Log out
|
|
flash:
|
|
login_required: Please log in to continue
|
|
login_success: Signed in successfully
|
|
invalid_credentials: Invalid username or password
|
|
logout_success: Signed out
|
|
password_current_incorrect: Current password is incorrect
|
|
password_too_short: The new password must be at least 8 characters long
|
|
password_too_long: New password cannot exceed 72 characters
|
|
password_too_weak: "New password must include at least 3 of: lowercase, uppercase, numbers and symbols"
|
|
password_same_as_current: New password must be different from the current password
|
|
password_mismatch: Passwords do not match
|
|
password_updated: Password updated
|
|
ops_acknowledged: Incident acknowledged
|
|
ops_resolved: Incident resolved
|
|
ops_muted: Notifications muted for 24 hours
|
|
stream_node_created: "Lab node %{slug} provisioned."
|
|
stream_node_destroyed: "Node %{slug} removed."
|
|
stream_node_draining: "Node %{slug} is draining (no new sessions)."
|
|
autoscale_kill_on: "Autoscaler kill-switch engaged. No automatic scale-out."
|
|
autoscale_kill_off: "Autoscaler kill-switch cleared (still needs STREAM_AUTOSCALE_ENABLED=1)."
|
|
comped_granted: "%{plan} complimentary subscription activated for %{club}."
|
|
comped_revoked: "Complimentary subscription revoked for %{club}."
|
|
session_already_terminated: "Session already ended (%{status})."
|
|
session_stopped: Session ended.
|
|
session_stop_error: "Error while stopping: %{error}"
|
|
session_terminated_no_regia: "Session ended: unable to generate the control link."
|
|
session_regia_generated: Control link generated.
|
|
invoice_upload_hint: Upload the PDF from the "Payments to invoice" list.
|
|
invoice_draft_created: Draft created. Upload the PDF from the Billing page or below.
|
|
invoice_issued: "Invoice %{number} issued and sent to %{email}."
|
|
invoice_updated: "Invoice %{number} updated."
|
|
invoice_uploaded: "Invoice uploaded and sent to %{email}."
|
|
youtube_not_configured: Configure YOUTUBE_CLIENT_ID and YOUTUBE_CLIENT_SECRET in .env
|
|
announcement_created: Notice saved
|
|
announcement_updated: Notice updated
|
|
announcement_destroyed: Notice deleted
|
|
common:
|
|
free_plan: Free
|
|
yes: "Yes"
|
|
no: "No"
|
|
dash: "—"
|
|
live_page: Live page
|
|
auth:
|
|
new:
|
|
title: Admin login
|
|
username_label: Username
|
|
password_label: Password
|
|
submit: Log in
|
|
initial_credentials_html: "Initial credentials: <code>admin</code> / <code>AdminPass123</code>. Change the password after the first login."
|
|
passwords:
|
|
edit:
|
|
title: Change password
|
|
current_password_label: Current password
|
|
new_password_label: "New password (min. 8, at least 3 character types)"
|
|
confirm_password_label: Confirm new password
|
|
submit: Save password
|
|
cancel: Cancel
|
|
dashboard:
|
|
kpi:
|
|
active_sessions: Active sessions
|
|
active_sessions_sub: live right now
|
|
today: Streamed today
|
|
today_sub: "%{duration} of streaming"
|
|
month: Streamed this month
|
|
month_sub: "%{duration} total"
|
|
teams: Teams
|
|
teams_sub: "%{count} users"
|
|
cpu: CPU
|
|
cpu_sub: "load %{one} / %{five} / %{fifteen}"
|
|
ram: RAM
|
|
ram_sub: "%{used} / %{total}"
|
|
network: Bandwidth (total)
|
|
network_sub: "↑ %{tx} · ↓ %{rx}"
|
|
recordings: Recordings in archive
|
|
recordings_sub: "%{count} recordings"
|
|
charts:
|
|
cpu: CPU (%)
|
|
ram: RAM (%)
|
|
ops_panel:
|
|
title: Platform status (Ops)
|
|
critical: "%{count} critical incident(s) open"
|
|
view_dashboard: View Ops dashboard
|
|
warnings: "%{count} warning(s)"
|
|
dashboard_link: Ops dashboard
|
|
none_html: "No open incidents. %{link}"
|
|
disk:
|
|
system_title: System disk
|
|
free_label: "Free: %{free} (%{percent}% used)"
|
|
not_available: Disk metrics not available
|
|
recordings_title: Recordings archive
|
|
recordings_content: "Recordings content: %{size}"
|
|
recordings_not_mounted: "Path %{path} not mounted"
|
|
sessions:
|
|
title: Active sessions
|
|
table:
|
|
match: Match
|
|
status: Status
|
|
ingest: Ingest
|
|
start: Start
|
|
link: Link
|
|
none: No active session right now.
|
|
detail: Details
|
|
stop: Stop
|
|
stop_confirm: End this session?
|
|
regia: Control
|
|
teams:
|
|
title: Teams
|
|
matches_count: "%{count} matches"
|
|
view_all: "View clubs (%{count} teams)"
|
|
stream_nodes:
|
|
title: Streaming nodes
|
|
providers: "Cloud: %{cloud} · DNS: %{dns}"
|
|
actions_title: Actions
|
|
table_title: Registered nodes
|
|
empty: No nodes registered.
|
|
free_slots:
|
|
one: "%{count} free"
|
|
other: "%{count} free"
|
|
kpi:
|
|
autoscaler: Autoscaler
|
|
kill_switch: KILL
|
|
kind: "%{kind} mode"
|
|
free_slots: Free slots
|
|
soft_slots: "soft threshold ≤ %{soft}"
|
|
spare: Spare
|
|
warm_spare: warm spares ready
|
|
overflow: Overflow
|
|
overflow_nodes: cloud/lab nodes
|
|
budget: Estimated budget
|
|
budget_of: "of €%{budget}/mo (%{ok})"
|
|
autoscale: "Autoscaler %{enabled} · free_slots=%{free} (soft≤%{soft}) · spare=%{spare}/%{warm} · overflow=%{overflow}/%{max} · kind=%{kind}"
|
|
autoscale_budget: "budget €%{eur}/€%{budget} (%{ok})"
|
|
kill_switch_active: "KILL-SWITCH ACTIVE"
|
|
engage_kill_switch: "Kill-switch ON"
|
|
clear_kill_switch: "Kill-switch OFF"
|
|
kill_switch_confirm: "Immediately block the autoscaler? Existing nodes stay up."
|
|
provision_lab: Provision lab
|
|
provision_cloud: Provision Hetzner
|
|
provision_cloud_confirm: "Create a Hetzner Cloud server + DNS record on mltv-stream.net? Billing applies until destroyed."
|
|
hetzner_token_missing: "Set HCLOUD_TOKEN to enable Cloud provisioning."
|
|
drain: Drain
|
|
destroy: Delete
|
|
destroy_confirm: "Delete node %{slug}?"
|
|
hosts_title: Lab DNS records (/etc/hosts snippet)
|
|
col:
|
|
slug: Slug
|
|
role: Role
|
|
status: Status
|
|
slots: Slots
|
|
hostname: Hostname
|
|
provider: Provider
|
|
ops:
|
|
kpi:
|
|
critical: Critical open
|
|
warning: Warnings open
|
|
total: Total open
|
|
open:
|
|
title: Open incidents
|
|
table:
|
|
severity: Severity
|
|
kind: Type
|
|
title: Title
|
|
occurrences: Occ.
|
|
last_seen: Last seen
|
|
none: No open incidents. The platform looks healthy.
|
|
actions:
|
|
acknowledge: Acknowledge
|
|
resolve: Resolve
|
|
mute: Mute 24h
|
|
resolved:
|
|
title: Resolved (last 30 days)
|
|
table:
|
|
resolved_at: Resolved
|
|
none: No incidents resolved recently.
|
|
clubs:
|
|
index:
|
|
title: Clubs & teams
|
|
subtitle_html: "Complimentary subscriptions (sponsors / promotions) and YouTube status per team. Stripe payments: %{billing_link}."
|
|
billing_link: Billing
|
|
table:
|
|
club: Club
|
|
plan: Plan
|
|
teams: Teams
|
|
comped: Comp
|
|
stripe: Stripe
|
|
manage: Manage
|
|
invoices: Invoices
|
|
show:
|
|
back: "← Clubs & teams"
|
|
sport_label: "Sport: %{sport}"
|
|
recordings_archive: Recordings archive
|
|
billing_link: Payments & invoices
|
|
youtube_platform_link: Platform YouTube channel
|
|
youtube_club_label: "Club YouTube:"
|
|
youtube_connected: (connected)
|
|
youtube_premium_full_not_connected: Premium Full — club channel not connected (using Match Live TV)
|
|
no_channel_hint_html: "Without a connected channel, the app uses the Match Live TV channel. %{link}."
|
|
link_channel: Connect channel (club page)
|
|
teams_title: Teams
|
|
no_teams: No teams registered.
|
|
table:
|
|
team: Team
|
|
sport: Sport
|
|
matches_and_details: Matches & details
|
|
replay: Replays
|
|
comped:
|
|
title: Complimentary subscription
|
|
description: "Sponsor or promotion: grant Premium Light/Full without a Stripe payment. Revocable at any time."
|
|
active_label: "Active:"
|
|
since_label_html: "Since %{date}"
|
|
by_admin: "· admin %{username}"
|
|
stripe_note: "Note: a linked Stripe subscription also exists; the comp takes priority over the plan."
|
|
revoke_button: Revoke comp (revert to Free or restore Stripe)
|
|
revoke_confirm: "Revoke the complimentary subscription for %{club}?"
|
|
current_plan: "Current plan: %{plan}"
|
|
stripe_active: "· active Stripe payment"
|
|
plan_label: Plan
|
|
reason_label: "Reason (e.g. 2026 sponsor)"
|
|
reason_placeholder: "Sponsor, promotion…"
|
|
grant_button: Grant comp
|
|
teams:
|
|
show:
|
|
back: "← %{club}"
|
|
sport_label: "Sport: %{sport}"
|
|
club_label_html: "Club: %{club} · %{replay_link} · %{billing_link}"
|
|
replay_link: Team replays
|
|
billing_link: Payments & invoices
|
|
youtube_label: "YouTube:"
|
|
youtube_platform_default_no_oauth: ", default without club OAuth"
|
|
youtube_platform_channel: "(platform channel%{suffix})"
|
|
youtube_club_channel: (club channel)
|
|
youtube_not_ready: not ready
|
|
no_channel_hint_html: "Without a connected channel, the app uses the Match Live TV channel. %{link}."
|
|
link_channel: Connect channel (club page)
|
|
matches_title: Matches
|
|
sessions:
|
|
index:
|
|
title: Stream sessions
|
|
lead: Search by club, team, opponent or venue. Filter by status, platform, node and date range.
|
|
results: "Showing %{shown} of %{total}"
|
|
none: No sessions match these filters.
|
|
filters:
|
|
q: Search
|
|
q_placeholder: Club, team, opponent…
|
|
status: Status
|
|
platform: Platform
|
|
club: Club
|
|
node: Ingest node
|
|
from: From
|
|
to: To
|
|
any: All
|
|
apply: Filter
|
|
reset: Reset
|
|
table:
|
|
club: Club
|
|
match: Match
|
|
status: Status
|
|
started: Started
|
|
ended: Ended
|
|
duration: Duration
|
|
ingest: Ingest
|
|
disconnects: Disconnects
|
|
link: Link
|
|
detail: Details
|
|
regia: Control
|
|
show:
|
|
title: "Session %{id}"
|
|
heading: Session details
|
|
back: "← Back to sessions"
|
|
club_label: "Club:"
|
|
summary_title: Summary
|
|
timing_title: Timing
|
|
ingest_title: Ingest & media
|
|
devices_title: Devices
|
|
events_title: Events
|
|
events_none: No events recorded.
|
|
status_label: "Status:"
|
|
stop_button: End session
|
|
stop_confirm: "End the broadcast? The RTMP path will be removed and the status will move to ended."
|
|
match_label: "Match: %{team} vs %{opponent}"
|
|
youtube_studio: YouTube Studio
|
|
broadcast: Broadcast
|
|
open_replays: Open club replays
|
|
audio_muted: Audio muted
|
|
audio_on: Audio on
|
|
ingest_node: "Ingest node:"
|
|
rtmp_ingest: "RTMP ingest:"
|
|
fields:
|
|
id: ID
|
|
club: Club
|
|
team: Team
|
|
opponent: Opponent
|
|
operator: Operator
|
|
platform: Platform
|
|
privacy: Privacy
|
|
quality: Quality
|
|
min_quality: Min quality
|
|
audio: Audio
|
|
created: Created
|
|
started: Started
|
|
ended: Ended
|
|
duration: Duration
|
|
disconnects: Disconnects
|
|
scheduled: Scheduled match
|
|
location: Venue
|
|
node: Node
|
|
rtmp: RTMP ingest
|
|
hls: HLS
|
|
recording: Replay
|
|
devices:
|
|
role: Role
|
|
network: Network
|
|
battery: Battery
|
|
bitrate: Bitrate
|
|
fps: FPS
|
|
last_seen: Last seen
|
|
table:
|
|
type: Type
|
|
when: When
|
|
meta: Details
|
|
links:
|
|
watch_title: Live link
|
|
none: No video link available for this session.
|
|
regia_title: Control link
|
|
terminated: Session ended — control link not available.
|
|
regia_label: Control
|
|
valid_until: "Valid until %{date}"
|
|
active_exists: An active control link already exists (the URL cannot be retrieved). Generate a new one if you need to share it again.
|
|
none_active: No active control link. Generate a link to share with whoever manages the score.
|
|
generate_button: Generate control link
|
|
ingest:
|
|
none: "—"
|
|
role:
|
|
home: Home-lab
|
|
lab: Lab
|
|
cloud: Hetzner
|
|
|
|
analytics:
|
|
missing_path: Select a page.
|
|
filters:
|
|
from: From
|
|
to: To
|
|
device: Device
|
|
any: All
|
|
apply: Filter
|
|
reset: Reset
|
|
index:
|
|
title: Site analytics
|
|
lead: Aggregated first-party heatmaps and scroll depth, only with analytics consent. No personal data.
|
|
none: No data in the selected period.
|
|
heatmap: Heatmap
|
|
table:
|
|
path: Page
|
|
pageviews: Pageviews
|
|
clicks: Clicks
|
|
avg_scroll: Avg scroll
|
|
max_scroll: Max scroll
|
|
show:
|
|
title: Page heatmap
|
|
back: "← Back to Analytics"
|
|
summary: Summary
|
|
scroll_depth: Scroll depth
|
|
scroll_hint: "Average %{avg}% · max %{max}%"
|
|
heatmap_title: Click map
|
|
no_clicks: No aggregated clicks for this page in the period.
|
|
|
|
billing:
|
|
index:
|
|
title: Payments to invoice
|
|
description: Stripe payments that are paid but have no invoice PDF yet. Generate the PDF in your systems, then upload it here — it will be linked to the payment and emailed to the customer.
|
|
filter_label: "Filter clubs:"
|
|
filter_all: All
|
|
incomplete_profile_warning: Billing details incomplete
|
|
no_billing_data: No billing details — the customer must complete their profile.
|
|
pdf_label: Invoice PDF
|
|
upload_button: Upload PDF and send to customer
|
|
none_pending: "No payments awaiting an invoice%{club}."
|
|
none_pending_for_club: " for %{club}"
|
|
uploaded_title: Invoices already uploaded
|
|
table:
|
|
date: Date
|
|
club: Club
|
|
description: Description
|
|
amount: Amount
|
|
invoice: Invoice
|
|
status: Status
|
|
back_dashboard: "← Dashboard"
|
|
billing_invoices:
|
|
index:
|
|
title: "Billing — %{club}"
|
|
back_dashboard: "← Dashboard"
|
|
new_invoice: New invoice (no payment)
|
|
payments_title: Payments
|
|
table:
|
|
date: Date
|
|
description: Description
|
|
amount: Amount
|
|
invoice: Invoice
|
|
pdf_suffix: " (PDF)"
|
|
edit_send_pdf: Edit / send PDF
|
|
create_invoice: Create invoice
|
|
no_payments: No payments recorded for this club.
|
|
all_invoices_title: All invoices
|
|
table2:
|
|
number: Number
|
|
date: Date
|
|
amount: Amount
|
|
status: Status
|
|
payment: Payment
|
|
edit: Edit
|
|
no_invoices: No invoices.
|
|
new:
|
|
title: "New invoice — %{club}"
|
|
linked_payment: "Linked to the payment of %{date} (%{amount})."
|
|
number_label: Invoice number
|
|
issued_on_label: Issue date
|
|
amount_label: "Amount (€)"
|
|
notes_label: Internal notes (optional)
|
|
hint: Save the draft, then upload the PDF and send it to the customer from the next screen.
|
|
submit: Create invoice draft
|
|
back: "← Payments & invoices"
|
|
edit:
|
|
title: "Invoice %{number} — %{club}"
|
|
status_label: "Status:"
|
|
sent_at: "· Sent on %{date}"
|
|
number_label: Invoice number
|
|
issued_on_label: Issue date
|
|
amount_label: "Amount (€)"
|
|
payment_info: "Payment: %{description} — %{amount}"
|
|
pdf_label: Invoice PDF
|
|
pdf_already: "PDF already uploaded: %{filename}"
|
|
notes_label: Internal notes
|
|
save_draft: Save draft
|
|
issue_send: Issue and send by email
|
|
issue_confirm: "Send the invoice to %{email}?"
|
|
back: "← Payments & invoices"
|
|
youtube:
|
|
platform_token:
|
|
title: Match Live TV channel token
|
|
page_title: "YouTube token — Match Live TV"
|
|
channel_label: "Channel: %{title}"
|
|
success: "Connection successful. Copy this line right away:"
|
|
env_hint_html: "Add to <code>/opt/matchlivetv/infra/.env</code>:"
|
|
not_shown_again_html: "It won't be shown again. Then run: <code>docker compose -f docker-compose.prod.yml restart rails sidekiq</code>"
|
|
no_refresh_token_html: "Google did not return a refresh token. In your <a href=\"https://myaccount.google.com/permissions\" target=\"_blank\" rel=\"noopener\">Google account → app access</a>, revoke «Match Live TV», then repeat from <a href=\"/admin/youtube/platform\">/admin/youtube/platform</a>."
|
|
back_dashboard: "← Admin dashboard"
|
|
club_recordings:
|
|
index:
|
|
title: "Replays — %{club}"
|
|
announcements:
|
|
index:
|
|
title: Notices
|
|
lead: Messages for the apps, the public website and the logged-in area — maintenance, updates or announcements.
|
|
new: New notice
|
|
table_title: Notices
|
|
none: No notices yet. Create one to notify the apps and the website.
|
|
new:
|
|
title: New notice
|
|
edit:
|
|
title: Edit notice
|
|
table:
|
|
status: Status
|
|
kind: Type
|
|
title: Message
|
|
channels: Destinations
|
|
window: Window
|
|
actions:
|
|
edit: Edit
|
|
delete: Delete
|
|
delete_confirm: Delete this notice? It will no longer appear in the selected destinations.
|
|
kind:
|
|
info: Information
|
|
maintenance: Maintenance
|
|
update: App update
|
|
severity:
|
|
info: Info
|
|
warning: Warning
|
|
critical: Critical
|
|
status:
|
|
draft: Draft
|
|
published: Published
|
|
archived: Archived
|
|
channels:
|
|
android: Android app
|
|
ios: iOS app
|
|
web_public: Public website
|
|
web_private: Logged-in area
|
|
window:
|
|
always: Always (while published)
|
|
from: "From %{time}"
|
|
until: "Until %{time}"
|
|
form:
|
|
kind: Type
|
|
severity: Severity
|
|
status: Status
|
|
channels: Where to show it
|
|
channels_hint: "Select one or more destinations. Android and iOS are independent: create two notices if the copy should differ. The logged-in area is visible after sign-in (club, teams, account)."
|
|
title: Title
|
|
body: Body
|
|
starts_at: Visible from
|
|
ends_at: Visible until
|
|
window_hint: Leave empty to show immediately while published.
|
|
action_url: Link (optional)
|
|
action_url_hint: For updates, an empty URL opens that platform's store (Play Store on Android, App Store on iOS).
|
|
action_label: Button label
|
|
locales_legend: Copy by language
|
|
locales_hint: Fill in every app language (Italian, English, French, German, Spanish). Italian is required; missing languages fall back to English, then Italian.
|
|
locale_required: required
|
|
dismissible: Users can dismiss the notice
|
|
dismissible_hint: Uncheck for maintenance notices that should stay visible.
|
|
submit_create: Save notice
|
|
submit_update: Update notice
|
|
cancel: Cancel
|
|
|