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,14 +1,16 @@
module ClubBillingProfile
extend ActiveSupport::Concern
BILLING_ENTITY_TYPES = {
"company" => "Società / ASD con P.IVA",
"individual" => "Persona fisica",
"nonprofit" => "Associazione senza scopo di lucro"
}.freeze
BILLING_ENTITY_TYPE_KEYS = %w[company individual nonprofit].freeze
class_methods do
def billing_entity_types
ClubBillingProfile::BILLING_ENTITY_TYPE_KEYS.index_with { |key| I18n.t("billing.entity_types.#{key}") }
end
end
included do
validates :billing_entity_type, inclusion: { in: BILLING_ENTITY_TYPES.keys }, allow_nil: true
validates :billing_entity_type, inclusion: { in: BILLING_ENTITY_TYPE_KEYS }, allow_nil: true
validates :billing_email, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true
validates :billing_recipient_code, length: { is: 7 }, allow_blank: true
validates :billing_province, length: { is: 2 }, allow_blank: true
@@ -23,28 +25,28 @@ module ClubBillingProfile
# Campi minimi per intestazione fattura e invio (email PDF + SDI/PEC).
def billing_profile_errors
errors = []
errors << "Tipo intestatario" if billing_entity_type.blank?
errors << "Ragione sociale o nome intestatario" if billing_legal_name.blank?
errors << "Email di fatturazione" if billing_email.blank?
errors << "Indirizzo" if billing_address_line.blank?
errors << "Città" if billing_city.blank?
errors << "Provincia (sigla 2 lettere)" if billing_province.blank? || billing_province.to_s.length != 2
errors << "CAP" if billing_postal_code.blank?
errors << "Paese (ISO)" if billing_country.blank? || billing_country.to_s.length != 2
errors << I18n.t("billing.profile_errors.entity_type") if billing_entity_type.blank?
errors << I18n.t("billing.profile_errors.legal_name") if billing_legal_name.blank?
errors << I18n.t("billing.profile_errors.email") if billing_email.blank?
errors << I18n.t("billing.profile_errors.address") if billing_address_line.blank?
errors << I18n.t("billing.profile_errors.city") if billing_city.blank?
errors << I18n.t("billing.profile_errors.province") if billing_province.blank? || billing_province.to_s.length != 2
errors << I18n.t("billing.profile_errors.postal_code") if billing_postal_code.blank?
errors << I18n.t("billing.profile_errors.country") if billing_country.blank? || billing_country.to_s.length != 2
errors.concat(billing_tax_id_errors)
errors << "Codice destinatario SDI (7 caratteri) o PEC" if billing_recipient_code.blank? && billing_pec.blank?
errors << "Codice destinatario SDI (7 caratteri)" if billing_recipient_code.present? && billing_recipient_code.length != 7
errors << I18n.t("billing.profile_errors.sdi_or_pec") if billing_recipient_code.blank? && billing_pec.blank?
errors << I18n.t("billing.profile_errors.sdi_invalid") if billing_recipient_code.present? && billing_recipient_code.length != 7
errors
end
def billing_tax_id_errors
case billing_entity_type
when "company"
billing_vat_number.blank? ? ["Partita IVA"] : []
billing_vat_number.blank? ? [I18n.t("billing.profile_errors.vat_number")] : []
when "individual"
billing_fiscal_code.blank? ? ["Codice Fiscale"] : []
billing_fiscal_code.blank? ? [I18n.t("billing.profile_errors.fiscal_code")] : []
else
billing_vat_number.blank? && billing_fiscal_code.blank? ? ["P.IVA o Codice Fiscale"] : []
billing_vat_number.blank? && billing_fiscal_code.blank? ? [I18n.t("billing.profile_errors.vat_or_fiscal")] : []
end
end
@@ -56,7 +58,7 @@ module ClubBillingProfile
# Righe per intestazione fattura in admin (etichetta, valore).
def billing_profile_invoice_lines
lines = []
lines << ["Tipo", BILLING_ENTITY_TYPES[billing_entity_type]] if billing_entity_type.present?
lines << ["Tipo", self.class.billing_entity_types[billing_entity_type]] if billing_entity_type.present?
lines << ["Intestatario", billing_legal_name]
lines << ["P.IVA", billing_vat_number] if billing_vat_number.present?
lines << ["Codice fiscale", billing_fiscal_code] if billing_fiscal_code.present?
+4 -4
View File
@@ -47,11 +47,11 @@ class Match < ApplicationRecord
def public_status_label
active = active_stream_session
return "In corso / da riprendere" if active
return "Programmata" if scheduled_upcoming?
return "Pronta" if scheduled_at.present?
return I18n.t("matches.status.active") if active
return I18n.t("matches.status.scheduled") if scheduled_upcoming?
return I18n.t("matches.status.ready") if scheduled_at.present?
"Senza orario"
I18n.t("matches.status.no_time")
end
scope :search_teams_or_opponents, lambda { |query|
+6 -6
View File
@@ -164,13 +164,13 @@ class Recording < ApplicationRecord
end
def status_label
return "Eliminato" if deleted?
return "Scaduto" if status == "expired"
return "Errore" if status == "failed"
return "In elaborazione" if status == "processing"
return "Scade presto" if expires_at.present? && expires_at <= 7.days.from_now
return I18n.t("recordings.status.deleted") if deleted?
return I18n.t("recordings.status.expired") if status == "expired"
return I18n.t("recordings.status.failed") if status == "failed"
return I18n.t("recordings.status.processing") if status == "processing"
return I18n.t("recordings.status.expiring_soon") if expires_at.present? && expires_at <= 7.days.from_now
"Disponibile"
I18n.t("recordings.status.available")
end
private
+12 -13
View File
@@ -2,12 +2,10 @@ class TeamRosterMember < ApplicationRecord
CATEGORIES = %w[staff coach manager player].freeze
# Ordine di visualizzazione in pagina organico
DISPLAY_ORDER = %w[coach manager player staff].freeze
CATEGORY_LABELS = {
"staff" => "Staff",
"coach" => "Allenatori",
"manager" => "Dirigenti",
"player" => "Giocatori"
}.freeze
def self.category_label(category)
I18n.t("roster.category.#{category}", default: category.to_s.capitalize)
end
VOLLEYBALL_PLAYER_ROLES = Sports::PlayerRoles::BY_BOARD["volley"].freeze
@@ -27,7 +25,7 @@ class TeamRosterMember < ApplicationRecord
scope :by_category, ->(cat) { where(category: cat).ordered }
def category_label
CATEGORY_LABELS[category] || category
self.class.category_label(category)
end
def initials
@@ -58,10 +56,11 @@ class TeamRosterMember < ApplicationRecord
def default_role_label
case category
when "coach" then "Allenatore"
when "manager" then "Dirigente"
when "staff" then "Staff"
when "player" then jersey_number.present? ? "Giocatore ##{jersey_number}" : "Giocatore"
when "coach" then I18n.t("roster.role.coach")
when "manager" then I18n.t("roster.role.manager")
when "staff" then I18n.t("roster.role.staff")
when "player"
jersey_number.present? ? I18n.t("roster.role.player_numbered", number: jersey_number) : I18n.t("roster.role.player")
else category
end
end
@@ -70,12 +69,12 @@ class TeamRosterMember < ApplicationRecord
allowed = Sports::PlayerRoles.for_team(team)
return if role_label.in?(allowed)
errors.add(:role_label, "non è un ruolo valido per questo sport")
errors.add(:role_label, I18n.t("roster.errors.role_invalid"))
end
def photo_file_type
return if photo_file.content_type.in?(%w[image/png image/jpeg image/webp])
errors.add(:photo_file, "deve essere PNG, JPEG o WebP")
errors.add(:photo_file, I18n.t("roster.errors.photo_invalid_type"))
end
end