activemodel_error.rb 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. # This file collects a few Rails extensions to make it possible to translate both
  3. # errors built into Rails or other gems, and also our custom errors in the Zammad codebase.
  4. #
  5. # It works generally like this:
  6. # - The zammad:translation_catalog generator has an extractor that will find error message strings
  7. # defined by Rails and other gems and put them into the catalog of translatable strings.
  8. # - The I18n gem is extended to perform the actual translation lookups via the Zammad Translations API.
  9. #
  10. # Ensure the error message hash is loaded, as the code below relies on it.
  11. I18n.eager_load!
  12. module ActiveModel
  13. class Error
  14. # Make it possible to retrieve errors that are translated with a Zammad locale.
  15. # no_field_name indicates that the default behaviour of Rails which includes the field name
  16. # in the error message should be modified to say "This field ..." rather than "#{fieldname} ...".
  17. def localized_full_message(locale:, no_field_name: false)
  18. override_errors_format(locale, no_field_name) do
  19. ::I18n.with_zammad_locale(locale) do
  20. full_message
  21. end
  22. end
  23. end
  24. private
  25. def override_errors_format(locale, no_field_name)
  26. errors_hash = ::I18n.backend.translations[:en][:errors]
  27. orig_format = errors_hash[:format]
  28. errors_hash[:format] = ::Translation.translate(locale, 'This field %s', '%{message}') if no_field_name
  29. yield
  30. ensure
  31. errors_hash[:format] = orig_format
  32. end
  33. end
  34. class Errors
  35. if !method_defined?(:orig_add)
  36. alias orig_add add
  37. # This will add custom string errors to the I18n translation store.
  38. def add(attribute, type = :invalid, **options)
  39. return orig_add(attribute, type, **options) if !type.is_a?(String)
  40. # I18n uses namespacing to access the messages, so generate a safe symbol without the namespace separator '.'.
  41. type_sym = type.gsub(%r{\W}, '').to_sym
  42. ::I18n.backend.translations[:en][:errors][:messages][type_sym] = type
  43. orig_add(attribute, type_sym, **options)
  44. end
  45. end
  46. end
  47. end
  48. module I18n
  49. def self.with_zammad_locale(locale)
  50. backend.zammad_locale = locale
  51. yield
  52. ensure
  53. backend.zammad_locale = nil
  54. end
  55. end
  56. class I18n::Backend::Simple
  57. attr_accessor :zammad_locale
  58. if !method_defined?(:orig_lookup)
  59. alias orig_lookup lookup
  60. # Allow I18n to load the default rails error messages from the YAML files,
  61. # but translate them to the target locale before the error messages get generated
  62. # including placeholder subsitition.
  63. def lookup(...)
  64. result = orig_lookup(...)
  65. if result.is_a?(String) && zammad_locale
  66. return Translation.translate(zammad_locale, result)
  67. end
  68. result
  69. end
  70. end
  71. end