Merge pull request #47105 from ghiculescu/i18n-raise
Make `raise_on_missing_translations` raise on any missing translation
This commit is contained in:
commit
4af571c22d
@ -54,7 +54,7 @@
|
||||
# Suppress logger output for asset requests.
|
||||
config.assets.quiet = true
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
|
@ -41,7 +41,7 @@
|
||||
# Print deprecation notices to the stderr.
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
|
@ -54,7 +54,7 @@
|
||||
# Suppress logger output for asset requests.
|
||||
config.assets.quiet = true
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names
|
||||
|
@ -41,7 +41,7 @@
|
||||
# Print deprecation notices to the stderr.
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names
|
||||
|
@ -43,7 +43,7 @@
|
||||
# Suppress logger output for asset requests.
|
||||
config.assets.quiet = true
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names
|
||||
|
@ -36,7 +36,7 @@
|
||||
# Store uploaded files on the local file system in a temporary directory
|
||||
config.active_storage.service = :test
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names
|
||||
|
@ -1,3 +1,13 @@
|
||||
* `config.i18n.raise_on_missing_translations = true` now raises on any missing translation.
|
||||
|
||||
Previously it would only raise when called in a view or controller. Now it will raise
|
||||
anytime `I18n.t` is provided an unrecognised key.
|
||||
|
||||
If you do not want this behaviour, you can customise the i18n exception handler. See the
|
||||
upgrading guide or i18n guide for more information.
|
||||
|
||||
*Alex Ghiculescu*
|
||||
|
||||
* `ActiveSupport::CurrentAttributes` now raises if a restricted attribute name is used.
|
||||
|
||||
Attributes such as `set` and `reset` cannot be used as they clash with the
|
||||
|
@ -85,6 +85,15 @@ def self.forward_raise_on_missing_translations_config(app)
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
|
||||
end
|
||||
|
||||
if app.config.i18n.raise_on_missing_translations &&
|
||||
I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
|
||||
|
||||
I18n.exception_handler = ->(exception, *) {
|
||||
exception = exception.to_exception if exception.is_a?(I18n::MissingTranslation)
|
||||
raise exception
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.include_fallbacks_module
|
||||
|
@ -813,8 +813,7 @@ Sets the path Rails uses to look for locale files. Defaults to `config/locales/*
|
||||
|
||||
#### `config.i18n.raise_on_missing_translations`
|
||||
|
||||
Determines whether an error should be raised for missing translations
|
||||
in controllers and views. This defaults to `false`.
|
||||
Determines whether an error should be raised for missing translations. This defaults to `false`.
|
||||
|
||||
#### `config.i18n.fallbacks`
|
||||
|
||||
|
@ -1148,47 +1148,34 @@ The I18n API defines the following exceptions that will be raised by backends wh
|
||||
| `I18n::ReservedInterpolationKey` | the translation contains a reserved interpolation variable name (i.e. one of: `scope`, `default`) |
|
||||
| `I18n::UnknownFileType` | the backend does not know how to handle a file type that was added to `I18n.load_path` |
|
||||
|
||||
The I18n API will catch all of these exceptions when they are thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for `MissingTranslationData` exceptions. When a `MissingTranslationData` exception has been caught, it will return the exception's error message string containing the missing key/scope.
|
||||
#### Customizing how `I18n::MissingTranslationData` is handled
|
||||
|
||||
The reason for this is that during development you'd usually want your views to still render even though a translation is missing.
|
||||
If `config.i18n.raise_on_missing_translations` is `true`, `I18n::MissingTranslationData` errors will be raised. It's a good idea to turn this on in your test environment, so you can catch places where missing translations are requested.
|
||||
|
||||
In other contexts you might want to change this behavior, though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module or a class with a `call` method:
|
||||
If `config.i18n.raise_on_missing_translations` is `false` (the default in all environments), the exception's error message will be printed. This contains the missing key/scope so you can fix your code.
|
||||
|
||||
If you want to customize this behaviour further, you should set `config.i18n.raise_on_missing_translations = false` and then implement a `I18n.exception_handler`. The custom exception handler can be a proc or a class with a `call` method:
|
||||
|
||||
```ruby
|
||||
# config/initializers/i18n.rb
|
||||
module I18n
|
||||
class JustRaiseExceptionHandler < ExceptionHandler
|
||||
class RaiseExceptForSpecificKeyExceptionHandler
|
||||
def call(exception, locale, key, options)
|
||||
if exception.is_a?(MissingTranslation)
|
||||
if key == "special.key"
|
||||
"translation missing!" # return this, don't raise it
|
||||
elsif exception.is_a?(MissingTranslation)
|
||||
raise exception.to_exception
|
||||
else
|
||||
super
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
I18n.exception_handler = I18n::JustRaiseExceptionHandler.new
|
||||
I18n.exception_handler = I18n::RaiseExceptForSpecificKeyExceptionHandler.new
|
||||
```
|
||||
|
||||
This would re-raise only the `MissingTranslationData` exception, passing all other input to the default exception handler.
|
||||
|
||||
However, if you are using `I18n::Backend::Pluralization` this handler will also raise `I18n::MissingTranslationData: translation missing: en.i18n.plural.rule` exception that should normally be ignored to fall back to the default pluralization rule for English locale. To avoid this you may use an additional check for the translation key:
|
||||
|
||||
```ruby
|
||||
if exception.is_a?(MissingTranslation) && key.to_s != 'i18n.plural.rule'
|
||||
raise exception.to_exception
|
||||
else
|
||||
super
|
||||
end
|
||||
```
|
||||
|
||||
Another example where the default behavior is less desirable is the Rails TranslationHelper which provides the method `#t` (as well as `#translate`). When a `MissingTranslationData` exception occurs in this context, the helper wraps the message into a span with the CSS class `translation_missing`.
|
||||
|
||||
To do so, the helper forces `I18n#translate` to raise exceptions no matter what exception handler is defined by setting the `:raise` option:
|
||||
|
||||
```ruby
|
||||
I18n.t :foo, raise: true # always re-raises exceptions from the backend
|
||||
```
|
||||
This would raise all exceptions the same way the default handler would, except in the case of `I18n.t("special.key")`.
|
||||
|
||||
Translating Model Content
|
||||
-------------------------
|
||||
|
@ -297,6 +297,37 @@ Option `config.action_mailer.preview_path` is deprecated in favor of `config.act
|
||||
config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"
|
||||
```
|
||||
|
||||
### `config.i18n.raise_on_missing_translations = true` now raises on any missing translation.
|
||||
|
||||
Previously it would only raise when called in a view or controller. Now it will raise anytime `I18n.t` is provided an unrecognised key.
|
||||
|
||||
```ruby
|
||||
# with config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# in a view or controller:
|
||||
t("missing.key") # raises in 7.0, raises in 7.1
|
||||
I18n.t("missing.key") # didn't raise in 7.0, raises in 7.1
|
||||
|
||||
# anywhere:
|
||||
I18n.t("missing.key") # didn't raise in 7.0, raises in 7.1
|
||||
```
|
||||
|
||||
If you don't want this behaviour, you can set `config.i18n.raise_on_missing_translations = false`:
|
||||
|
||||
```ruby
|
||||
# with config.i18n.raise_on_missing_translations = false
|
||||
|
||||
# in a view or controller:
|
||||
t("missing.key") # didn't raise in 7.0, doesn't raise in 7.1
|
||||
I18n.t("missing.key") # didn't raise in 7.0, doesn't raise in 7.1
|
||||
|
||||
# anywhere:
|
||||
I18n.t("missing.key") # didn't raise in 7.0, doesn't raise in 7.1
|
||||
```
|
||||
|
||||
Alternatively, you can customise the `I18n.exception_handler`.
|
||||
See the [i18n guide](https://guides.rubyonrails.org/v7.1/i18n.html#using-different-exception-handlers) for more information.
|
||||
|
||||
Upgrading from Rails 6.1 to Rails 7.0
|
||||
-------------------------------------
|
||||
|
||||
|
@ -69,7 +69,7 @@ Rails.application.configure do
|
||||
config.assets.quiet = true
|
||||
<%- end -%>
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
|
@ -57,7 +57,7 @@ Rails.application.configure do
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Raises error for missing translations in controllers and views.
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
|
@ -4335,6 +4335,98 @@ def new(app); self; end
|
||||
assert_match(/The `legacy_connection_handling` setter was deprecated in 7.0 and removed in 7.1, but is still defined in your configuration. Please remove this call as it no longer has any effect./, error.message)
|
||||
end
|
||||
|
||||
test "raise_on_missing_translations = true" do
|
||||
add_to_config "config.i18n.raise_on_missing_translations = true"
|
||||
app "development"
|
||||
|
||||
assert_equal true, Rails.application.config.i18n.raise_on_missing_translations
|
||||
|
||||
assert_raise(I18n::MissingTranslationData) do
|
||||
I18n.t("translations.missing")
|
||||
end
|
||||
end
|
||||
|
||||
test "raise_on_missing_translations = false" do
|
||||
add_to_config "config.i18n.raise_on_missing_translations = false"
|
||||
app "development"
|
||||
|
||||
assert_equal false, Rails.application.config.i18n.raise_on_missing_translations
|
||||
|
||||
assert_nothing_raised do
|
||||
I18n.t("translations.missing")
|
||||
end
|
||||
end
|
||||
|
||||
test "raise_on_missing_translations = true and custom exception handler in initializer" do
|
||||
add_to_config "config.i18n.raise_on_missing_translations = true"
|
||||
app_file "config/initializers/i18n.rb", <<~RUBY
|
||||
I18n.exception_handler = ->(exception, *) {
|
||||
if exception.is_a?(I18n::MissingTranslation)
|
||||
"handled I18n::MissingTranslation"
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
}
|
||||
RUBY
|
||||
app "development"
|
||||
|
||||
assert_equal true, Rails.application.config.i18n.raise_on_missing_translations
|
||||
|
||||
assert_equal "handled I18n::MissingTranslation", I18n.t("translations.missing")
|
||||
assert_raise(I18n::InvalidLocale) do
|
||||
I18n.t("en.errors.messages.required", locale: "dsafdsafdsa")
|
||||
end
|
||||
end
|
||||
|
||||
test "raise_on_missing_translations = false and custom exception handler in initializer" do
|
||||
add_to_config "config.i18n.raise_on_missing_translations = false"
|
||||
app_file "config/initializers/i18n.rb", <<~RUBY
|
||||
I18n.exception_handler = ->(exception, *) {
|
||||
if exception.is_a?(I18n::MissingTranslation)
|
||||
"handled I18n::MissingTranslation"
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
}
|
||||
RUBY
|
||||
app "development"
|
||||
|
||||
assert_equal false, Rails.application.config.i18n.raise_on_missing_translations
|
||||
|
||||
assert_equal "handled I18n::MissingTranslation", I18n.t("translations.missing")
|
||||
assert_raise(I18n::InvalidLocale) do
|
||||
I18n.t("en.errors.messages.required", locale: "dsafdsafdsa")
|
||||
end
|
||||
end
|
||||
|
||||
test "i18n custom exception handler in initializer and pluralization backend" do
|
||||
app_file "config/initializers/i18n.rb", <<~RUBY
|
||||
I18n.exception_handler = ->(exception, *) {
|
||||
if exception.is_a?(I18n::MissingTranslation)
|
||||
"handled I18n::MissingTranslation"
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
}
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
I18n.backend.class.include(I18n::Backend::Pluralization)
|
||||
I18n.backend.send(:init_translations)
|
||||
I18n.backend.store_translations :en, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
|
||||
I18n.backend.store_translations :en, apples: { one: 'one or none', other: 'more than one' }
|
||||
I18n.backend.store_translations :en, pears: { pear: "pear", pears: "pears" }
|
||||
end
|
||||
RUBY
|
||||
|
||||
app "development"
|
||||
|
||||
assert I18n.backend.class.include?(I18n::Backend::Pluralization)
|
||||
assert_equal "one or none", I18n.t(:apples, count: 0)
|
||||
assert_raises I18n::InvalidPluralizationData do
|
||||
assert_equal "pears", I18n.t(:pears, count: 0)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_custom_config(contents, config_source = "custom".inspect)
|
||||
app_file "config/custom.yml", contents
|
||||
|
Loading…
Reference in New Issue
Block a user