In Rails, how to specify default flash messages in i18n locale file
Asked Answered
B

3

24

I know there are some preset structures in i18n locale file so that Rails pulls values automatically. For example, if you want to set the default submit button text for new records:

# /config/locales/en.yml
en:
  helpers:
    submit:
      create: "Create %{model}"
      user:
        create: "Sign Up"

With this set, in views the following will result:

# /app/views/things/new.html.erb
<%= f.submit %> #=> Renders a submit button reading "Create Thing"

# /app/views/users/new.html.erb
<%= f.submit %> #=> Renders a submit button reading "Sign Up"

So Rails uses a preset hierarchy for getting the submit button text for different models. (i.e., you don't have to tell it which i18n text to get when using f.submit.) I've been trying to find a way to do this with flash notices and alerts. Is there a similar preset structure for specifying default flash messages?

I know you can specify your own arbitrary structures like the following:

# /config/locales/en.yml
en:
  controllers:
    user_accounts:
      create:
        flash:
          notice: "User account was successfully created."

# /app/controllers/users_controller.rb
def create
  ...
  redirect_to root_url, notice: t('controllers.user_accounts.create.flash.notice')
  ...
end

But it's tedious to specify the notice: t('controllers.user_accounts.create.flash.notice') every time. Is there a way to do this so that the controller "just knows" when to grab and display the appropriate flash messages specified in the locale file? If so, what's the default YAML structure for these?

Bibeau answered 17/3, 2014 at 23:48 Comment(0)
M
32

The Rails i18n guide section 4.1.4 on "lazy" lookups says:

Rails implements a convenient way to look up the locale inside views

(Emphasis theirs, and implying to me, at least, that it is restricted only to views...) However, it seems that this commit to Rails brought "lazy" lookups into controllers as well, with the key being in the form of:

"#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }"

which in your case should get you users.create.notice.

So, if you're happy with something like:

# /app/controllers/users_controller.rb
def create
  ...
  redirect_to root_url, notice: t('.notice')
  ...
end

You should be able to just declare that value in:

# /config/locales/en.yml
en:
  users:
    create:
      notice: "User account was successfully created."

I know this doesn't take you quite all the way of having a default spot where Rails would automatically go and fetch a flash notice on failure to create a user, but it's a bit better than typing out a full i18n key every time.

Meso answered 18/3, 2014 at 14:13 Comment(4)
Thanks for finding that, @PaulFioravanti. That is better. Note: You'll still need the full namespace when testing (e.g. page.should have_content(t('users.create.notice'))).Bibeau
That's true. Specs don't know anything about lazy loading i18n values, so you'll always need the full path when using them there.Meso
Good answer. I would like to know, don't I need to mention inside the yams if controller key like this ?Temperamental
@ArupRakshit if you use custom key namespaces, then I think you won't be able to use default Rails lazy loading in controllers, and instead will require the full key path. Since the I18nLazyLookup gem mentioned in the answer you linked seems to support controllers, I'd say give it a try and see if it does what you want it to.Meso
F
9

I think that currenly (Fall 2015) the most graceful and somewhat conventional way to implement lazy flash messages for you controllers is to use responders gem:

gem 'responders', '~> 2.1'

FlashResponder sets the flash based on the controller action and resource status. For instance, if you do: respond_with(@post) on a POST request and the resource @post does not contain errors, it will automatically set the flash message to "Post was successfully created" as long as you configure your I18n file:

flash:
  actions:
    create:
      notice: "%{resource_name} was successfully created."
    update:
      notice: "%{resource_name} was successfully updated."
    destroy:
      notice: "%{resource_name} was successfully destroyed."
      alert: "%{resource_name} could not be destroyed."

This allows to completely remove flash-related code from the controllers.

However, as you have already understood, you'll need to rewrite your controllers with their respond_with method for that:

# app/controllers/users_controller.rb

class UsersController < ApplicationController
  respond_to :html, :json

  def show
    @user = User.find params[:id]
    respond_with @user
  end
end
Fordham answered 27/10, 2015 at 0:51 Comment(1)
I'll add that if you're using devise, you already have this dependency.Timikatiming
G
5

Follow-up for @robertwbradford's comment on testing, in a Rails 4 / MiniTest functional (controller) test, you can call the translate method on the @controller instance variable:

assert_equal @controller.t('.notice'), flash[:notice]
Gallous answered 2/3, 2015 at 20:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.