Rails: i18n parameters that are also translated - ruby-on-rails

I'm having difficulty finding the answer for my question, but it may just be that I have no idea how to phrase it. In svenfuchs's yml files in his rails-i18n repo, he has this listed under error:
format: #{attribute}#{message}
and below that he specifies possible error messages. It's really nifty, as it automatically translated error messages for me.
I'd like to use this format to translate headers and buttons. In Japanese, we'd say "FAQ Create" while in English we'd say "Create FAQ", so I can't just print out those translations and I'd like to not have to make each button's translation myself (a.k.a. create_faq: FAQを作る).
So far I've got in my view: t('button.format'), :attribute => "faq", :message => "create"
ja.yml:
model:
faq: FAQ
button:
format: #{attribute}#{message}
messages:
create: を作る
But that just prints out faqcreate for Japanese. What I'm trying to do is access the translations of model.faq and button.messages.create to pass as the parameters. Anybody know how?
p.s. messages: was also plural in the working errors message.

I'm sorry, I'm so stupid, I keep answering my questions right after I finally decided to ask for help;;; for anybody interested, just pass in another t(' '), so:
t('button.format', :attribute t('model.faq'), :message t('button.messages.create'))

Related

Using a/an on an I18n message

I have the following error message on my en.yml file:
invalid_transition: "Cannot %{action} a %{state} timesheet"
Problem is that sometimes the state can be approved, other times it can be rejected.
With that, the error can end up being mispelled like "Cannot submit a approved timesheet", instead of "an approved timesheet".
Does Rails provide this flexibility in I18n?
The most simple answer to your question, I think, would be to pass in the entire state with the correct indefinite article together.
There's a question that looks at how to prepend "a" or "an" depending on a given word. A short answer is that there's a gem indefinite_article that does it.
Your translation then becomes:
invalid_transition: "Cannot %{action} %{state_with_article} timesheet"
Then call the I18n.t and pass in "a rejected" or "an approved" as a variable to interpolate.
However, if you want to get your hands a bit dirtier you may be able to use the i18n-inflector gem and it's companion i18n-inflector-rails gem. For many languages the choice is more complicated than in English because of different genders and tenses affecting the choice of indefinite article. (Disclaimer: I've not used either of those gems but they look like they would help you solve the problem).

How to use embedded ruby in ActionMailer subject

I'm trying to use some embedded ruby in the subject line of an email coming from ActionMailer but keep getting different errors.
I couldn't find any documentation on the proper syntax. Any resources to fix this line of code?
mail(to: #user.email, subject: "Your Reservation Confirmation for" + #restaurant.name)
I've passed in all of the variables fine. I just need to see how I can combine text and these inputs.
Thanks
There are two common ways to it:
First:(regarding rep)
"...Confirmation for" + #restaurant.name.to_s
Second:
you can use string interpolation
"...Confirmation for #{#restaurant.name}"
I don't know whether this is intentional, but apparently #restaurant.name is returning a number (as you clarified, you're getting a TypeError: no implicit conversion of Fixnum into String). Calling #restaurant.name.to_s will solve that.
As G.B mentioned in another answer, string interpolation like "...Confirmation for #{#restaurant.name}" works too, since it calls #to_s for you automatically.
I'm putting the solution into an answer, since we found it while clarifying in the comments.

Capitalization API

Is there any good API or service that handles capitalization well? It should be able to handle input like "i need help fixing my iphone asap" with a desired output of "I Need Help Fixing My iPhone ASAP".
Edit: This is in conjunction with titleize. Titleize doesn't handle words like "iPhone" and acronyms. I'm currently getting user input like "ceo" and titleize returns "Ceo", when I'd like "CEO". I'd prefer not to write a list of special capitalizations, especially if there is a good alternative.
Another alternative would be a library of words and the correct capitalization.
Have a look at #titleize in ActiveSupport::Inflector
"man from the boondocks".titleize # => "Man From The Boondocks"
"x-men: the last stand".titleize # => "X Men: The Last Stand"
"TheManWithoutAPast".titleize # => "The Man Without A Past"
"raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
Cut and paste from http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-titleize

Is there a way to skip serialization in Rails 3.1?

In my Rails 3.1 application, I need to read the raw data of a field, without serialization, and then write it down without serialization. Is this possible? How?
By serialization I mean
class Tenant
serialize :profile_template
end
I obviously can access the field like this:
> t.profile_template
=> [{:title=>"Page 1", ....}]
I then also tried with read_attribute_before_type_cast (as per lucapette's suggestion):
> t.read_attribute_before_type_cast(:profile_template)
=> nil
Using a string instead of a symbol had a different but disappointing result:
> t.read_attribute_before_type_cast("profile_template")
=> [{:title=>"Page 1", ...}]
and same with the attribute name:
> t.profile_template_before_type_cast
=> [{:title=>"Page 1", ...}]
Just for the record, what I was expecting is:
"---
- :title: Page 1
...."
In all samples, ... is the rest of a very long structure.
Yes there is a way. You have to use
read_attribute_before_type_cast(:foo)
where :foo is the name of the field. The doc is not that good about that but I remember that there is a good explanation about it in The Rails 3 way.
EDIT
Although you're saying that this way isn't working for you I re-read the piece of information from the above-mentioned book. Well, there's another way of doing that. You can use
bar = foo_before_type_cast
where foo is the name of the field. It works like magic finders, pre-pending the name of the field to _before_type_cast . I can't try it right now but it really should work fine.

Nested model error messages

I am using Ruby on Rails 3.0.9 and I am trying to validate a nested model. Supposing that I run validation for the "main" model and that generates some errors for the nested model I get the following:
#user.valid?
#user.errors.inspect
# => {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
How you can see the RoR framework creates an errors hash having following keys: account.firstname, account.lastname, account. Since I would like to display error messages on the front-end content by handling those error key\value pairs with JavaScript (BTW: I use jQuery) that involves CSS properties I thought to "prepare" that data and to change those keys to account_firstname, account_lastname, account (note: I substitute the . with the _ character).
How can I change key values from, for example, account.firstname to account_firstname?
And, mostly important, how I should handle this situation? Is what I am trying to do a "good" way to handle nested model errors? If no, what is the common\best approach to do that?
I've made a quick Concern which shows full error messages for nested models:
https://gist.github.com/4710856
#1.9.3-p362 :008 > s.all_full_error_messages
# => ["Purchaser can't be blank", "Consumer email can't be blank", "Consumer email is invalid", "Consumer full name can't be blank"]
Some creative patching of the Rails errors hash will let you achieve your aim. Create an initializer in config/initalizers, let call it errors_hash_patch.rb and put the following in it:
ActiveModel::Errors.class_eval do
def [](attribute)
attribute = attribute.to_sym
dotted_attribute = attribute.to_s.gsub("_", ".").to_sym
attribute_result = get(attribute)
dotted_attribute_result = get(dotted_attribute)
if attribute_result
attribute_result
elsif dotted_attribute_result
dotted_attribute_result
else
set(attribute, [])
end
end
end
All you're doing in here is simply overriding the accessor method [] to try a little harder. More specifically, if the key you're looking for has underscores, it will try to look it up as is, but if it can't find anything it will also replace all the underscores with dots and try to look that up as well. Other than that the behaviour is the same as the regular [] method. For example, let's say you have an errors hash like the one from your example:
errors = {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
Here are some of the ways you can access it and the results that come back:
errors[:account] => ["is invalid"]
errors[:"account.lastname"] => ["is too short", "can not be blank"]
errors[:account_lastname] => ["is too short", "can not be blank"]
errors[:blah] => []
We don't change the way the keys are stored in the errors hash, so we won't accidentally break libraries and behaviours that may rely on the format of the the hash. All we're doing is being a little smarter regarding how we access the data in the hash. Of course, if you DO want to change the data in the hash, the pattern is the same you will just need to override the []= method, and every time rails tries to store keys with dots in them, just change the dots to underscores.
As to your second question, even though I have shown you how to do what you're asking, in general it is best to try and comply with the way rails tries to do things, rather than trying to bend rails to your will. In your case, if you want to display the error messages via javascript, presumably your javascript will have access to a hash of error data, so why not tweak this data with javascript to be in the format that you need it to be. Alternatively you may clone the error data inside a controller and tweak it there (before your javascript ever has access to it). It is difficult to give advice without knowing more about your situation (how are you writing your forms, what exactly is your validation JS trying to do etc.), but those are some general guidelines.
I had the same problem with AngularJs, so I decided to overwrite the as_json method for the ActiveModel::Errors class in an initializer called active_model_errors.rb so that it can replace . for _
Here is the initializer code:
module ActiveModel
class Errors
def as_json(options=nil)
hash = {}
to_hash(options && options[:full_messages]).each{ |k,v| hash[k.to_s.sub('.', '_')] = messages[k] }
hash
end
end
end
I hope it can be helpful for someone
I'm not sure but I think you can't change that behavior without pain. But you could give a try to solutions like http://bcardarella.com/post/4211204716/client-side-validations-3-0

Resources