Argghhh!!! I went bananas for a little while getting my head around this. What are we trying to achieve? Well, say let’s we have a model called Consultant which has a many-to-many relationship with Company though a Contract association. The Contract model is basically sitting on topic of a simple join in the database which has consultant_id and and company_id fields.
class Consultant < ActiveRecord::Base
has_many :contracts
has_many :companies, :through => :contracts
accepts_nested_attributes_for :contracts
end
class Company < ActiveRecord::Base
has_many :contracts
has_many :consultants :through => :contracts
end
class Contract < ActiveRecord::Base
belongs_to :consultant
belongs_to :company
validates_uniqueness_of :consultant_id, :scope => :company_id,
:message => "cannot have a contract with the same company more than once"
end
We have a New Consultant page which allows us to associate existing Companies with a Consultant by adding/removing Contracts. A Consultant cannot have a contract with a Company more than once, hence we need a validate_uniqueness_of validation on the Contract association.
But hey, business is booming! We end up needing to reuse the code base for another Rails project. The new project is in the construction domain but it has been decided that the database schema and domain model should remain unchanged. However we are told that as far as the user is concerned they should never see the term Consultant, rather they should see the term Builder. Enter translations!
Though you’re not going to put your shoddy Spanish to the test and you failed French before you left school, translations are a handy way to work around this problem. Simply translate the word Consultant into Builder via the config/locales/en.yml file in your Rails project. At the same time we’ll also change
-
The validation error header that is present when the user submits an invalid record
-
The displayed version of the consultant_name to be Builder Name
-
The displayed version of the consultant model name to be Builder when arising from errors on the Contract association. The way to do this is not immediately obvious - you have to translate the foreign key field (consultant_id) to Builder for the association
Here’s the complete config/locales/en.yml file. Note: Whitespace and indentation is very important.
en:
activerecord:
errors:
messages:
template:
header:
one: "This Builder has just one error but still you gotta fix it..."
other: "This Builder has lots of errors, get your act together..."
attributes:
consultant:
consultant_name: Builder Name # Handles the work of translating this attribute
contract:
consultant_id: Builder # NNB: This the big one! Notice how must translate the foreign key field to Builder for the association!!!
models:
consultant: Builder # This causes the consultant model to be referred to as Builder on the UI
So there you have it. The foreign key is the key, so to speak!
The best guide I encountered on translation was this one by Ian Hecker on Translating ActiveRecord which is a great way to get started. There is also a somewhat overwhelming guide to translations at Rails Internationalization (I18n) API but is comprehensive nonetheless. Also, do look at the en-GB.yml example at Sven Fuchs Locale Examples on GitHub.com which is where I first saw how you can define you locale file as an .rb file or yaml as above. Finally, I came across this entry on how to remove Rails Validation Message Prefixes which I didn’t try but I just mention here in case I need it in future.