iachat/app/mailers/administrator_notifications/account_notification_mailer.rb
Gabriel Jablonski 053e684261
feat(mailer): add i18n support for transactional emails with pt-BR (#255)
* feat(mailer): add i18n support for all transactional emails with pt-BR translations

- Create Liquid i18n filter (LiquidFilters::I18nFilter) exposing I18n.t() as `t` filter
  with interpolation support via positional args
- Replace hardcoded English strings in all 26 Liquid email templates with i18n keys
- Replace hardcoded English strings in all 5 Devise ERB email templates (OSS + enterprise)
  with t() calls, using raw output (<%==) to prevent HTML-encoding of apostrophes
- Translate all mailer subjects to use I18n.t() for locale-aware rendering
- Add comprehensive pt-BR translations for all transactional emails
- Add SSO-specific i18n keys for enterprise confirmation instructions
- Fix ApplicationRecord#to_drop to walk STI hierarchy (e.g. SuperAdmin -> UserDrop)

* fix: address CodeRabbit review feedback

- Use _html suffix for Devise translation keys to prevent XSS while
  keeping apostrophes unencoded (Rails escapes interpolations automatically)
- Replace hardcoded English in SLA templates with full_body i18n keys
- Fix _target="blank" typo to target="_blank" in attachment links
- Add i18n for tiktok_disconnect subject and create liquid template
- Extract brand_name to private method in AccountNotificationMailer

* fix: address round 2 review feedback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 15:48:52 -03:00

75 lines
2.7 KiB
Ruby

class AdministratorNotifications::AccountNotificationMailer < AdministratorNotifications::BaseMailer
def account_deletion_user_initiated(account, reason)
subject = I18n.t('mailer.administrator_notifications.account_notifications.account_deletion_user_initiated.subject', brand_name: brand_name)
action_url = settings_url('general')
meta = {
'account_name' => account.name,
'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']),
'reason' => reason
}
send_notification(subject, action_url: action_url, meta: meta)
end
def account_deletion_for_inactivity(account, reason)
subject = I18n.t('mailer.administrator_notifications.account_notifications.account_deletion_for_inactivity.subject', brand_name: brand_name)
action_url = settings_url('general')
meta = {
'account_name' => account.name,
'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']),
'reason' => reason
}
send_notification(subject, action_url: action_url, meta: meta)
end
def contact_import_complete(resource)
subject = I18n.t('mailer.administrator_notifications.account_notifications.contact_import_complete.subject')
action_url = if resource.failed_records.attached?
Rails.application.routes.url_helpers.rails_blob_url(resource.failed_records)
else
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{resource.account.id}/contacts"
end
meta = {
'failed_contacts' => resource.total_records - resource.processed_records,
'imported_contacts' => resource.processed_records
}
send_notification(subject, action_url: action_url, meta: meta)
end
def contact_import_failed
subject = I18n.t('mailer.administrator_notifications.account_notifications.contact_import_failed.subject')
send_notification(subject)
end
def contact_export_complete(file_url, email_to)
subject = I18n.t('mailer.administrator_notifications.account_notifications.contact_export_complete.subject')
send_notification(subject, to: email_to, action_url: file_url)
end
def automation_rule_disabled(rule)
subject = I18n.t('mailer.administrator_notifications.account_notifications.automation_rule_disabled.subject')
action_url = settings_url('automation/list')
meta = { 'rule_name' => rule.name }
send_notification(subject, action_url: action_url, meta: meta)
end
private
def brand_name
GlobalConfig.get('BRAND_NAME')['BRAND_NAME'].presence || 'Chatwoot'
end
def format_deletion_date(deletion_date_str)
return 'Unknown' if deletion_date_str.blank?
Time.zone.parse(deletion_date_str).strftime('%B %d, %Y')
rescue StandardError
'Unknown'
end
end