Completa i18n IT/EN/FR/DE/ES su sito pubblico, area autenticata e admin.

Tutte le view marketing, legali, viewer, dashboard e admin usano t(); mailer utente-facing e flash localizzati; admin con LocaleResolver e selettore lingua.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-24 00:38:50 +02:00
co-authored by Cursor
parent fb7068f86c
commit b88f44ab1c
158 changed files with 9444 additions and 1536 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ module AdminHelper
def admin_session_watch_links(session)
links = []
if session.matchlivetv_platform?
links << { label: "Pagina live", url: session.watch_page_url }
links << { label: I18n.t("admin.common.live_page"), url: session.watch_page_url }
end
youtube = session.youtube_watch_url
links << { label: "YouTube", url: youtube } if youtube.present?
+28
View File
@@ -31,6 +31,34 @@ module ApplicationHelper
I18n.locale.to_s
end
def confirm_dialog_i18n_json
{
eyebrow: t("common.confirm_eyebrow"),
title: t("common.confirm_title"),
deleteTitle: t("common.delete_title"),
confirmOk: t("common.confirm_ok"),
deleteOk: t("common.delete_ok"),
cancel: t("common.cancel"),
multiDeleteTitle: t("common.multi_delete_title"),
multiDeleteMessage: t("common.multi_delete_message"),
deleteYoutubeTitle: t("common.delete_youtube_title"),
deleteYoutubeMessage: t("common.delete_youtube_message"),
deleteYoutubeOk: t("common.delete_youtube_ok"),
deleteSiteTitle: t("common.delete_site_title"),
deleteSiteMessageWithYoutube: t("common.delete_site_message_with_youtube"),
deleteSiteMessageOnly: t("common.delete_site_message_only"),
deleteSiteOk: t("common.delete_site_ok"),
choiceSiteLabel: t("common.choice_site_label"),
choiceSiteHintWithYoutube: t("common.choice_site_hint_with_youtube"),
choiceSiteHintOnly: t("common.choice_site_hint_only"),
choiceYoutubeLabel: t("common.choice_youtube_label"),
choiceYoutubeHintWithSite: t("common.choice_youtube_hint_with_site"),
choiceYoutubeHintOnly: t("common.choice_youtube_hint_only"),
choiceBothLabel: t("common.choice_both_label"),
choiceBothHint: t("common.choice_both_hint")
}.to_json
end
def sport_catalog_options(selected = nil)
options = Sports::Catalog.as_api_list.map { |entry| [entry[:label], entry[:key]] }
options_for_select(options, Sports::Catalog.normalize_key(selected))
+7 -7
View File
@@ -2,25 +2,25 @@ module Public
module BillingHelper
def plan_billing_action(current_slug:, target_plan:, stripe_subscription_active:, current_interval: nil, subscription: nil)
if target_plan.slug == "free"
return { kind: :current, label: "Piano attuale" } if current_slug == "free"
return { kind: :current, label: I18n.t("billing.actions.current_plan") } if current_slug == "free"
return { kind: :none } if stripe_subscription_active
return { kind: :contact, label: "Per passare al piano Free, contatta il supporto." }
return { kind: :contact, label: I18n.t("billing.actions.contact_for_free") }
end
unless MatchLiveTv.stripe_enabled?
return { kind: :disabled, label: "Stripe non configurato" }
return { kind: :disabled, label: I18n.t("billing.actions.stripe_not_configured") }
end
intervals = Billing::Stripe::PriceCatalog.available_intervals(plan_slug: target_plan.slug)
return { kind: :disabled, label: "Prezzi Stripe non configurati" } if intervals.empty?
return { kind: :disabled, label: I18n.t("billing.actions.stripe_prices_not_configured") } if intervals.empty?
active_interval = current_interval.presence || Billing::Stripe::PriceCatalog::DEFAULT_INTERVAL
if current_slug == target_plan.slug && stripe_subscription_active && !subscription&.plan_change_pending?
other_intervals = intervals - [active_interval]
if other_intervals.empty?
label = "Piano attuale#{Billing::Stripe::PriceCatalog.label(plan_slug: target_plan.slug, interval: active_interval)}"
label = "#{I18n.t('billing.actions.current_plan')}#{Billing::Stripe::PriceCatalog.label(plan_slug: target_plan.slug, interval: active_interval)}"
return { kind: :current, label: label }
end
@@ -87,9 +87,9 @@ module Public
def billing_profile_incomplete_message(club)
missing = club.billing_profile_errors
return "Completa i dati di fatturazione prima di attivare un piano premium." if missing.empty?
return I18n.t("billing.actions.profile_incomplete_default") if missing.empty?
"Mancano: #{missing.join(", ")}."
I18n.t("billing.actions.profile_missing", fields: missing.join(", "))
end
end
+16 -13
View File
@@ -8,16 +8,16 @@ module Public
when "basket", "timed"
live_score_period_label(match, score_state)
when "timer"
"Cronometro"
t("score.timer_label")
when "generic"
"Punteggio"
t("score.generic_label")
else
"Set #{score_state.current_set} · Set vinti #{score_state.home_sets}-#{score_state.away_sets}"
t("score.sets_label", set: score_state.current_set, home: score_state.home_sets, away: score_state.away_sets)
end
end
def live_score_period_label(match, score_state)
return "" unless score_state
return t("score.fallback") unless score_state
score_state.current_period_label
end
@@ -35,16 +35,16 @@ module Public
def live_scheduled_label(datetime)
return nil unless datetime
datetime.in_time_zone.strftime("%d/%m/%Y alle %H:%M")
datetime.in_time_zone.strftime(t("score.scheduled_date_format"))
end
def live_scheduled_relative(datetime)
return nil unless datetime
if datetime.to_date == Time.zone.today
"Oggi alle #{datetime.strftime('%H:%M')}"
t("score.scheduled_today", time: datetime.strftime("%H:%M"))
elsif datetime.to_date == Time.zone.tomorrow
"Domani alle #{datetime.strftime('%H:%M')}"
t("score.scheduled_tomorrow", time: datetime.strftime("%H:%M"))
else
live_scheduled_label(datetime)
end
@@ -52,7 +52,7 @@ module Public
def live_match_card_heading(match, link_team: true)
team = match.team
club_name = team.club&.name.presence || "Società"
club_name = team.club&.name.presence || t("score.default_club_name")
team_slug = team.respond_to?(:slug) ? team.slug : nil
team_label = if link_team && team_slug.present?
link_to(team.name, public_team_page_path(team_slug), class: "live-card__team-link")
@@ -69,7 +69,7 @@ module Public
def live_match_page_heading(match)
team = match.team
club_name = team.club&.name.presence || "Società"
club_name = team.club&.name.presence || t("score.default_club_name")
content_tag(:div, class: "live-page-heading") do
safe_join([
content_tag(:p, club_name, class: "live-page-heading__club"),
@@ -79,7 +79,7 @@ module Public
end
def live_score_points_label(match, score_state)
return "" unless score_state
return t("score.fallback") unless score_state
board = match.effective_board_type
home = case board
@@ -91,7 +91,7 @@ module Public
else score_state.away_points
end
"#{match.team.name} #{home} - #{away} #{match.opponent_name}"
t("score.points_label", team: match.team.name, home: home, away: away, opponent: match.opponent_name)
end
private
@@ -101,8 +101,11 @@ module Public
set_no = data[:set] || data["set"]
home = data[:home] || data["home"]
away = data[:away] || data["away"]
label = set_no.present? ? "Set #{set_no}" : "Set"
"#{label} #{home}-#{away}"
if set_no.present?
t("score.partial_entry", set: set_no, home: home, away: away)
else
t("score.partial_entry_no_number", home: home, away: away)
end
end
end
end
+49 -3
View File
@@ -12,14 +12,60 @@ module Public
when "basket", "timed"
live_score_period_label(match, score)
when "timer"
"Cronometro"
t("regia.board.stopwatch")
when "generic"
"Punteggio"
t("regia.board.generic_score")
else
"Set #{score.current_set}"
t("regia.board.set_label", number: score.current_set)
end
end
def regia_js_i18n_json
{
periodUpdated: t("regia.js.period_updated"),
nowInPeriodTemplate: t("regia.js.now_in_period", period: "%{period}"),
setWonTitle: t("regia.modal.set_won_title"),
setWonBodyTemplate: t("regia.js.set_won_body", winner: "%{winner}"),
matchWonTitle: t("regia.js.match_won_title"),
matchWonBodyTemplate: t("regia.js.match_won_body", winner: "%{winner}"),
closeSetTitle: t("regia.board.close_set"),
closeSetConfirm: t("regia.js.close_set_confirm"),
scoreUpdateError: t("regia.js.score_update_error"),
genericError: t("regia.js.generic_error"),
setsLineTemplate: t("regia.js.sets_line", current: "%{current}", home: "%{home}", away: "%{away}"),
setPartialLabelTemplate: t("regia.board.set_label", number: "%{number}"),
freeScore: t("regia.board.free_score"),
stopwatch: t("regia.board.stopwatch"),
quarterLabelTemplate: t("regia.js.quarter_label", n: "%{n}"),
halfLabelTemplate: t("regia.js.half_label", n: "%{n}"),
overtimeBasketTemplate: t("regia.js.overtime_basket", n: "%{n}"),
overtimeOther: t("regia.js.overtime_other"),
unknownPeriod: t("regia.js.unknown_period"),
partialsPrefix: t("regia.board.partials_prefix"),
resumed: t("regia.js.resumed"),
pausedCover: t("regia.js.paused_cover"),
closed: t("regia.js.closed"),
resumeError: t("regia.js.resume_error"),
pauseError: t("regia.js.pause_error"),
closeError: t("regia.js.close_error"),
closeConfirm: t("regia.js.close_confirm"),
linkUnavailable: t("regia.js.link_unavailable"),
linkCopied: t("regia.js.link_copied"),
copyPrompt: t("regia.js.copy_prompt"),
shareRegiaText: t("regia.js.share_regia_text"),
shareLiveText: t("regia.js.share_live_text"),
previewWaiting: t("regia.preview_waiting"),
streamEnded: t("regia.preview_ended"),
resumeLabel: t("regia.resume"),
pauseLabel: t("regia.pause"),
endedBadge: t("regia.status.ended"),
pausedBadge: t("regia.status.paused"),
liveBadge: t("regia.status.live"),
waitingBadge: t("regia.status.waiting"),
subtitlePrefix: t("regia.subtitle_prefix")
}.to_json
end
def format_clock_secs(secs, count_up: false)
total = secs.to_i
return count_up ? "0:00" : "" if total <= 0 && !count_up
@@ -1,7 +1,7 @@
module Public
module TeamPagesHelper
def team_page_meta_description(team, club)
"Segui #{team.name} (#{club.name}): dirette live, calendario partite e replay su Match Live TV."
t("team_pages.show.meta_description", team: team.name, club: club.name)
end
def filter_params_for_canonical
+2 -2
View File
@@ -19,14 +19,14 @@ module RosterHelper
def roster_category_options
TeamRosterMember::DISPLAY_ORDER.map do |cat|
[TeamRosterMember::CATEGORY_LABELS[cat], cat]
[TeamRosterMember.category_label(cat), cat]
end
end
def roster_player_role_options(team, selected = nil)
roles = Sports::PlayerRoles.for_team(team)
options_for_select(
[["— Seleziona ruolo —", ""]] + roles.map { |r| [r, r] },
[[t("roster.role_select_blank"), ""]] + roles.map { |r| [r, r] },
selected
)
end