Five minutes instant overview - Get on Rails with Globalize! Part 1 of 8
posted: November 10th, 2006 · by: Sven
So you’ve just found out that Globalize is the Rails plugin to bring multilanguage support to your applications? And you’re now looking for an instant overview of what Globalize can do for you?
Then here’s some good news for you. By the end of this article you’ll know:
- how to install and configure Globalize and
- how to use it to translate your Rails applications.
This article is part of my series “Get on Rails with Globalize”. At the end of this article you’ll find a summary of what will be discussed in the next article of this series.
Start your engines, please!
You’ll need to have a working Rails app up and running to check things out. If you start a fresh Rails app from scratch make sure that your conf/database.yml knows about your database, valid credentials etc. and everything else is nice.
Now, make your way to your Rails app directory and install the plugin from the Globalize Subversion repository. (Take care to choose the right version. As of writing there’s a branch for Rails 1.1 while trunk is not compatible with 1.1.)
script/plugin install svn://svn.globalize-rails.org/globalize/ branches/for-1.1
And if you’re keen to keep your stuff straight you’ll probably want to rename the directory:
mv vendor/plugins/for-1.1 vendor/plugins/globalize
Now just do:
rake globalize:setup
... and you’re already done with the setup!
(If you’re curious, check your database! You’ll find three new globalize_* tables with 239 countries, 186 languages and 3420 pre-translated strings … including several languages I’d bet you’ve never heard of like Avar, Manx or Wolof!)
Configuring Globalize
That’s it. You’re globalized now. Welcome to the club :-)
There’s nothing more to do than include the library and set the base_language for your Globalize instance.
In environment.rb this should do the job:
Rails::Initializer.run do |config|
# ...
end
include Globalize # put this here
Locale.set_base_language('en-US') # and here :-)
Don’t forget to restart your server after changing the environment.rb file in case it’s already running.
You’re done.
The meaning of the base_language is that this language is what Globalize treats as the “primary” language – which means that data stored in your model tables is stored in the primary language whereas translations are stored in the globalize_translations table.
Joshua Harvey explains it like this:
[In the database] ”... base language entries are stored in the parent table itself, as opposed to the globalize_translations table. So if you have a table called ‘products’, and your base language is English, the English product name would be stored in products.name, whereas the German translation would be stored in globalize_translations.”
And thus: “It’s important to never change the base language once you’ve started populating the database.” So choose your base_language carefully.
Dates, Time, Numbers: a question of form
Already impatient to check out some working stuff? I were, too …
There’s a nice example in the Globalize wiki using Rails Unit tests and fixtures. For now we’ll check things out on the console. So, fire up your console (i.e., within your Rails app directory, do: script/console).
The api documentation for Time.localize() says that this method “acts the same as strftime, but returns a localized version of the formatted date/time string”.
Ok, let’s check that out:
>> Time.now.localize("%d. %B %Y")
which should give you:
=> "21. June 2006"
You’re probably not overly surprised about this. ;-) But in fact this means that Globalize is already at work here. The string “June” comes from the Globalize database tables and represents the English full name of, well, the month June as requested by %B.
So let’s try to change the current language setting to something else, say: Spanish. I have no idea if this format would actually be used in Spain but you get the point:
>> Locale.set("es-ES")
>> Time.now.localize("%d. %B %Y")
=> "21. Junio 2006"
(Beware that the pre-translated data that Globalize is coming with is not complete. For example when you try the same example with the German locale de-DE than instead of the expected “21. Juni 2006” you’d actually get “21. June 2006” because the German translation is missing so far.)
You can format Integers, Floats, Dates, Times with localize. Just like Time.localize, Date.localize mimics strftime’s behaviour.
When you call localize on an Integer it will return “the integer in String form, according to the rules of the currently active locale” – and the same applies to Floats:
>> Locale.set("de-DE")
>> 123456.localize # format an Integer
=> 123.456
>> 123.456.localize # format a Float
=> 123,456
Also, you can use the alias loc instead of localize for convenience.
ViewTranslations: Time for .t
What’s the most basic purpose of I18n tools? It’s the ability to translate arbitrary text, isn’t it? Text that belongs to the application tier itself and most likely won’t change that often once the application is out of the door: view templates, mail templates, error messages, flash notices, ... hardcoded stuff mostly.
Globalize adds a new method translate to Rubys String and Symbol classes that will do all the heavy lifting for this type of translation (which is called “ViewTranslation” internally). Also, there’s an alias t so that your templates don’t get too cluttered.
Let’s see that in action …
>> Locale.set("de-DE")
>> "Welcome".t
=> "Welcome"
What the heck? We’d expect to see the result “Willkommen” here, wouldn’t we? This doesn’t seem to work, right? Well, actually, it does.
We need to provide Globalize with the translation of “Welcome” first of course (and we’ll do so in short – so stay tuned). You’ll most probably develop your Rails application in one (base) language and add your translations afterwards.
Besides that this perfectly demonstrates Globalize’s behaviour when it encounters a string that it doesn’t know yet:
- Instead of throwing an error it will simply return the original string and
- it will remember the string for you by dumping it into the database (so you can look it up later).
If you’re curious go and look up the globalize_translations table. You’ll find that there’s a new record for the key “Welcome” – waiting for translation. Globalize remembers strings that are used in your application by dumping them in the database:
>> Translation.find(:first, :order => "id DESC")
=> #<Globalize::ViewTranslation:0x275c410 @attributes={ ...
"tr_key"=>"Welcome", "text"=>nil,
"type"=>"ViewTranslation", ...}>
(In case you expected a method _() like that’s common in classic gettext libraries: yes, you can use that one, too. Globalize adds a (although deprecated) method _() to Rubys Object class so you can call it “globally” and in turn it will call Locale.translate then.)
How to add ViewTranslations
Ok, let’s teach Globalize how to welcome people in German. That’s as simple as:
Locale.set("de-DE")
Locale.set_translation('Welcome', 'Willkommen')
Or alternatively:
lang = Language.pick('de-DE')
Locale.set_translation('Welcome', lang, 'Willkommen')
So finally Globalize will come up with:
>> Locale.set("de-DE")
>> "Welcome".t
=> "Willkommen"
Of course in your templates, you’d use something like this:
<%= "Welcome".t -%>
Now, that’s waaay cool … really. Think about it for a moment: Globalize lets you append .t to any string which will transparently lookup and find available translations. In case there’s no translation available it will simply return the original string (which seems to be the default behaviour most t10n/i18n tools show). As soon you’ve added a translation it will be used.
ModelTranslations? Just add hot water …
Now, so far that’s basically about date formats and static strings like those in your templates. What about your models, you wonder?
Globalize comes with the capability to transparently translate any attribute of your ActiveRecords Models … all you have to do is add a translates directive to your model class like this:
def Page < ActiveRecord::Base translates :title end
As soon as there actually is a translation for the title attribute in the database it will be used – otherwise, like above, the base language’s value will be used by default. (Also, an empty record will be saved to the database so that you can check back for strings that need to be translated).
Now how do you tell Globalize about a model translation? Simply by saving the model. Like this:
Locale.set('en-US')
page = Page.create!(:title => 'Welcome to Globalize!')
Locale.set('de-DE')
page.reload # we'd get a Globalize::WrongLanguageError here w/o this
page.title = 'Willkommen bei Globalize!'
page.save
Ok, cool. Let’s sum things up.
You’ll need two fingersnips to get your Rails app globalized: install the Globalize plugin and use it :-). Using it is as simple as 1-2-3, too:
- There’s
.tfor ViewTranslations (arbitrary, static text), - there’s the
translatesdirective for ModelTranslations (ActiveRecord) and - there’s
.locto localize your date, time and number formats.
And of course there’s more …
So much for the basics.
Of course there’s much more to discuss. In Part 2 of the series “Get on Rails with Globalize” we’ll talk about Some common questions on getting started like:
- How to setup your application to use Unicode
- How to select and persist the current user’s locale
- How to translate Rails ActiveRecord messages
- How to localize entire templates
Tim said November 14th, 2006 at 03:48 PM ¶
Great stuff, Sven! Really useful.
Looking forward to your next installment!
MJ said December 5th, 2006 at 06:24 AM ¶
Thanks for this informative write up. I'll be using globalize very shortly and this is the best information I've found yet!
Michael Johnston said January 4th, 2007 at 09:51 PM ¶
In your opinion, why is Globalize a better approach than gettext?
I'm having trouble deciding whether to go with Globalize or gettext.
On the one hand, I don't see how with Globalize I can easily take my application and hand it off to a translation team to localize. However, gettext does not provide a built in way to translate in-database content.
So it looks like either way, I have stuff to build.
Sven said January 10th, 2007 at 08:21 PM ¶
Hi Michael,
I think there's no silver bullet / one, right approach to t10n/i18n of Rails apps.
Of course there are pros/cons though. I've yet to collect/write some up for a detailed comparsion but in a nutshell the main difference of course is that Globalize is completely Rails, while gettext of course is - well - gettext. That means, that Globalize uses the database, while gettext is filebased. Gettext comes with mature, tested and documented tools while Globalize can easily (but also most oftenly: must) be backed up by a CRUD app to allow your team to add translations. Etc.
As often YMMV ... this is really something that completely depends on your project, application, team, server, ...
HTH though
Sorry for the late response.
luc said January 25th, 2007 at 11:37 PM ¶
Hi,
I tried to get started on this globalization road. At the command "rake globalize:setup" I ran into a road block: rake aborted! Mysql::Error: Identifier name 'indexglobalizetranslationson_tablenameanditemid_andlanguageid' is too long: CREATE INDEX
index_globalize_translations_on_table_name_and_item_id_and_language_idON globalizetranslations (table_name,item_id,language_id)I found an answer on http://www.nabble.com/rake-globalize:setup-mysql-error-(identifier-too-long)-t2905189s17045.html by Martin Bernd Schmeil:
I did the first and it worked. Am I the only one who had this problem? Wouldn't it be better to take care of this problem in the package itself?
But it's the last statement that puzzles me most. I'm pretty new in Rails and I don't understand what Martin is trying to say. Can anyone explain what it means?
Thanks a lot!
Sven said January 26th, 2007 at 08:40 AM ¶
Hi luc,
this issue is on my list for a "collection of common gotchas" article that I'm going to add ... but I've yet to further investigate it myself. I haven't been bitten by this yet but I've seen it coming up once in a while, so it's definitely worth being covered.
I believe that this behaviour of using concatenated column names as identifiers that sometimes simply get way too long for MySQL is a behaviour of Rails itself, not just Globalize.
Regarding the last statement - what is it that's puzzling you? I think by "migration plus" Martin refers to a plugin that can be found at:
http://dppruby.com/dppsrubyplayground/show/Migrate+Plus
But ... basically I think the advice is: watch out for indexes being created in your migrations and add an :index => false (if you don't want it) or :name => 'my_short_index_name' to the migration.
HTH :)
I'm going to forward your question to the Globalize mailinglist and hope to be able to cover it more deeply in an upcoming article. Thanks for asking!
Sven said January 28th, 2007 at 04:13 PM ¶
luc,
could you tell me something about your environment? I can't reproduce your error under neither Mac OSX nor Gentoo.
Paolo Dona said January 28th, 2007 at 11:34 PM ¶
Great post, it's very clear and informative! Just fix the 'Some common questions on getting started' link that still points to a javascript alert :-) keep rocking!
Sven said January 29th, 2007 at 04:54 PM ¶
Thanks Paolo, for the catch. I've fixed that. Keep rolling! :-)
Greg said January 31st, 2007 at 07:42 AM ¶
Sven, got the same error as luc, have you been able to find out anything else regarding it? I'm running Rails 1.2, the same globalize branch you mentioned in the tut, MySQL 5.0.27, and the database as utf8.
If "index_ globalize_ translations_ on_ table_ name_ and_ item_ id_ and_ language_ id" (spaces intended so it doesn't overflow the layout) is supposed to be a column name I think that's too long for MySQL. Are you testing on SQlite? Anyway, when I find out anything more will post back here.
Sven said January 31st, 2007 at 06:24 PM ¶
Hi Greg!
*LOL* Now, that's amazing. You made my day :)
You can find a new ticket #143 in the Globalize Trac (see http://trac.globalize-rails.org/trac/globalize/ticket/143) dealing with this. I've not been able to reproduce this error in any way yet.
I've checked this with MySQL 4.0.27, 4.1.20 and 5.0.33 on Mac OS X, Rails 1.1.6 and a fresh Globalize for-1.1 checkout. I've never got this error. Instead, the index is named "globalizetranslationstablenameindex" and everything works like a charm.
Like you can see from my comment on that ticket my last suspicion was that some outdated Rails version between 1.1.x and 1.1.6 could be the culprit.
But with now you mentioning that you're using Rails 1.2 I'm totally at a loss myself.
So, if anyone can cast some light on this, that would be a relief :)
Sven said January 31st, 2007 at 06:32 PM ¶
Oops, sorry, Greg.
I forgot to mention that the solution to this problem (whatever it's reproducable cause(s) might be) seems to be to:
E.g.
ActiveRecord::Base.connection.add_index :globalize_translations, [ :table_name, :item_id, :language_id ], :name => 'table_item_lang'
Greg said February 1st, 2007 at 06:37 AM ¶
Cool, that worked like a charm, thanks Sven (and yea, it is rails 1.2)!
Cheers, Greg
Mike said February 14th, 2007 at 02:41 PM ¶
Where is the rest of this series?
cheers
Sven said February 14th, 2007 at 02:54 PM ¶
Mike,
the article got truncated somehow ... don't know how that could happen. :-(
I've restored it from a backup for now.
Babybel said May 14th, 2007 at 10:17 AM ¶
hey guys.
I have an error when I try to connect to my first page after installing and setting Globalize in the environment.rb.
Google doesn’t give me any clue for this. Maybe you can help me ?
Here is the error :
uninitialized constant Globalize::DbTranslate
(I precise that I just followed your first installation steps … and done it several times)
Thanks for your help
Sven said May 16th, 2007 at 05:04 PM ¶
Hey Babyel,
I think the error means that the Globalize plugin has not been loaded yet. The best advice I can give you here is to register to the Globalize users mailing list and re-post your problem there. Probably including a full trace would make sense, too.
sunil said October 12th, 2007 at 08:38 AM ¶
Hi please help me with the problem
C:\rubyonrails\globalize>rake globalize:setup (in C:/rubyonrails/globalize) rake aborted! Mysql::Error: #42000Identifier name ‘indexglobalizetranslationson_tablename_ anditemidandlanguage_id’ is too long: CREATE INDEX
index_globalize_transla tions_on_table_name_and_item_id_and_language_idON globalize_translations (tab le_name,item_id,language_id)(See full trace by running task with –trace)dy please give a solution of this problem
Sven said October 12th, 2007 at 09:16 AM ¶
Hi Sunil,
please use Google. You probably would have found that the same question had been asked answered in the comments above.
You can most probably fix the error by adding
to your data.rake file.
Also, check your Rails version - if you are running something < 1.2 that’s probably not supported any more: http://trac.globalize-rails.org/trac/globalize/ticket/143
JBH said October 15th, 2007 at 10:44 PM ¶
I have followed all the steps for getting globalize working (a newbie to all of this!). The problem I am encountering is that I am not getting values into the globalize_translations table (they are getting there if I set translations through script/console). I have a form with labels using the .t, but after loading, the table does not reflect the new text required for translation. (I know that the translation view works fine because I can read in the existing values from the table and edit the changes back into the table with no problems.) Any ideas what I am missing?
JBH said October 15th, 2007 at 10:58 PM ¶
Please ignore my plea for help…I will just shoot the other developers on this project for inserting a custom def t function that ignored globalize code!
Mitja said March 27th, 2008 at 01:42 PM ¶
Hello,
i have problems with translating the model data. I created a page like described on this page of tutorial. Set new local and reloaded page. Changed the title and body attributes of the model.
Than, when i try to save the page, i get: ArgumentError: wrong number of arguments (2 for 1)……and lots of stuff onward…
What am i missing here?
Thank you for any information.
Mitja said March 28th, 2008 at 06:30 PM ¶
Hello again,
i have found a patch to my problem… But, i have another question.
How does one deploy development translations to production.
thank you.
srinath said April 1st, 2008 at 08:37 PM ¶
Hi Mirja,
You mentioned that you found a patch. Would you mind adding the solution to the forum please. I am pulling my hair out trying to solve this “ArgumentError”
Dav said May 1st, 2008 at 09:12 PM ¶
Thanks for the great introduction. I just wanted to point out that in your example:
<globalize::viewtranslation:0x275c410>
“tr\_key”=>”Willkommen”, “text”=>nil, “type”=>”ViewTranslation”, …}>
seems to have the wrong tr_key. Wouldn’t it be “Welcome” that was inserted into the database?
Sven said May 11th, 2008 at 05:59 PM ¶
Hi Dav!
wow, yeah :) Thanks for the catch! I’ve corrected that.
ashritha said June 12th, 2008 at 05:06 PM ¶
hi
i m new to globalization i m not understandin how do i translate few statements to a different language in one page of my application…i.e in the view…
where do i set the locale??
chat said March 31st, 2011 at 08:07 PM ¶
The following cleaned up the issue:
Dependencies.loadoncepaths -= Dependencies.loadoncepaths.select{|path| \ path =~ %r(^#{File.dirname(FILE)}) }
Okey oyunu said May 12th, 2011 at 04:14 PM ¶
Thanks for this article. Tüm dünya artik okey oyunu oynuyor. Yillardir bir çok oyun programi olmasina ragmen, içlerinden en güzeli olarak nitelendirebilecegimiz tek bir site göze çarpmaktadir. Diger tüm okey oyunu programlarinin aksine ücretsiz olmasi ve 3 boyutlu olarak hizmet vermesi mükemmel bir gelismedir. Sizlerde www.okey-oyunu.com adresinden bu essiz okey oyununu indirebilirsiniz. Kullanimi çok basit ve Türkçe dil seçenegi ile kolaylikla oyuna baslayabilirsiniz. Ister kendi ülkenizden, isterseniz dünyanin tüm farkli bölgelerinden dilediginiz oyun odalarini seçerek, oyuna hemen baslayabilirsiniz. Okey oyunu oynamak için artik arkadas bile aramaniza gerek kalmadan, bilgisayarinizdan 100 binlerce üye ile online olarak okey oyununu oynamanin zevkine varabilirsiniz.
porno said May 22nd, 2011 at 01:07 PM ¶
I do agree with all of the ideas you have presented in your post. They’re really convincing and will definitely work. Still, the posts are too short for newbies. Could you please extend them a bit from next time? Thanks for the post.
porno said May 22nd, 2011 at 01:10 PM ¶
very nice sharing, thank you manage your site
I am grateful to the webmaster
Thanks for the great informations,love this site!
kad?n said May 22nd, 2011 at 01:11 PM ¶
good comment. thanks you friends.
I’ve surfed the net more than three hours today, however, I haven’t found such useful information. Thanks a lot, it is really useful to me