Archive for October, 2008

I18n: An Overview

Wednesday, October 29th, 2008

Welcome to yet another overview, this time it’s on the new I18n features in Rails 2.2 which you can install by typing gem install rails -s http://gems.rubyonrails.org -v 2.2.0.

Any mention of the t method in this guide are also spots where you can use translate too, they are just aliased methods (t is aliased to translate), it’s just much easier (for me) to type t than it is to type translate. Please excuse my laziness.

I’ve begun adding in the translation calls for rboard in my personal branch on GitHub and today I would like to show you how I’ve done it.

Firstly I have added these two lines to my config/environment.rb:

config/environment.rb

I18n.loadpath = Dir.glob("#{RAILSROOT}/locales/*.rb")
I18n.default_locale = "en-AU"

This tells rails to load the translation files (aka locale files) from the locales directory in the root of my rails app, and they are in a ruby format. Alternatively you could load yaml files. this also tells it to set the default locale as “en-AU”, which will load locales/en-AU.rb by default.

My (incomplete) translation file looks like this: locales/en-AU.rb

{
  :'en-AU' => {
   :forumheading => "Forum",
   :topicsheading => "Topics",
   :postsheading => "Posts",
   :lastpostheading => "Last Post",
   :noforums => "There are no forums.", 
   :administratorshouldcreateforum => "Maybe an administrator should create one.",
   :youshouldcreateforum => "Maybe you should create a forum.",
   :forumstatistics => "Forum Statistics",
   :postspertopic => "Posts per topic",
   :recentusers => "Users on in the last 15 minutes",
   :registeredusers => "Registered Users",
   :home => "Home",
   :editprofile => "Edit Profile",
   :memberlist => "Member List",
   :search => "Search",
   :newmessage => "new message",
   :logout => "Logout",
   :timenow => "The time is now",
   :viewingforum => "Viewing forum",
   :newtopic => "New Topic",
   :moderationheading => "Moderation",
   :topicheading => "Topic",
   :repliesheading => "Replies",
   :viewsheading => "Views",
   :authorheading => "Author",
   :ago => "ago",
   :by => "by"
  }
}

When I have a string I want translated in my app I will simply call stuff like t(:author_heading) and Rails will look up the correct translation for it, which in this case is just “Author”.

Now if I had another translation file, say locales/es.rb and I had Spanish users on rboard they could select a locale from their profile page and that would store it as a string on their user record. To translate this, we can use a before_filter on the application controller:

app/controllers/application.rb

class ApplicationController < ActionController::Base
  beforefilter :setlocale
  def setlocale
    I18n.locale = currentuser.locale if logged_in?
  end
end

This will set the locale to whatever the user has set, providing that they are logged in.

Interpolation

If you wish to insert a value into a translation you can use interpolation. To do this you can specify the t method call like this:

in a i18n-friendly file somewhere

<%= t(:welcome, :user => current_user.login) %>

And then in your locales file specify this:

locales/en-AU.rb

:welcome => "welcome {{user}}!"

And the output of the translation will now be “welcome Ryan!” or whatever the user login was.

Counting

If you have a translation such as :xnewmessages in your translation file and you want the output of this translation to be correctly pluralized you can pass the count option to this:

In any t method supporting files

<%= t(:x_new_messages, :count => current_user.messages.size) %>

The x_ prefix to our translation is not important, it’s just there to show us that this translation may return different results depending on the count that is passed to it.

Then in your translation file you can do:

locales/en-AU.rb

:xnewmessages => {:zero => 'No new messages', :one => 'One new message', :other => '{{count}} new messages'}

And depending on the value of count it will return one of those three outcomes.

Forcing a Locale

If you want to force a locale on a single translation you can do this by specifying the :locale to the t method call like so:

<%= t(:english, :locale => "en-AU") %>

And this will always show the en-AU translation of the english key in the en-AU.rb locale file.

Alternative Translations

If one of your translations does not match like:

<%= t(:norsk) %>

You can have I18n fall back to any number of other translations:

<%= t(:norsk, :default => [:norwegian, :up_north, :northwards, "norway"]) %>

I18n will attempt to get a default translation from the options specified and will select the first one. If all translations failed then the string version, “norway” will be outputted.

Retrieving Multiple Translations

To get multiple translations back at the same time you can specify an array as the first argument to the t method.

<%= t(:forums, :topics) %>

Assuming you have correct translations for forums and topics you will get the translated versions returned in an array. Assuming you don’t have the correct translations for forums OR topics you will get back a string version of whatever translation is missing, possibly wrapped in a <span class=’translation_missing’></span>.

Further translation files can be found at Sven Fuch’s Github Repository

This is genius!

Friday, October 24th, 2008

I read Mike Gunderloy’s blog post today and afterwards thought he was crazy! I thought you couldn’t do Blog.find(“1-title”).

But you can.

If you pass in 1-title as params[:id] for any action and then pass it to find, find will do its “thing” and just execute find(1), giving you pretty URLs AND pretty code.

I later found out this was because find just calls to_i when it gets given strings, and calling it on “1-title” will give you “1″.

Ruby Quiz – Matricies

Wednesday, October 22nd, 2008
nb = 5
distance = Array.new(nb, Array.new(nb))
k = 0
(0...nb).each do |i|
  (0...nb).each do |j|
    distance[j][i] = k +=1
  end
end

puts distance.inspect

This code should generate: [[1,6,11,16,21], [2,7,12,17,22], [3,8,13,18,23], [4,9,13,19,25], [5,10,15,20,25]]

But generates: [[5,10,15,20,25], [5,10,15,20,25], [5,10,15,20,25], [5,10,15,20,25], [5,10,15,20,25]]

I learned why today, and I wonder if anyone else knows.

Did you (Rails) Rumble?

Monday, October 20th, 2008

NO

I spent the weekend learning (read: having an affair with) Merb. I’m porting rboard to mboard whenever I feel like doing some merb. This is in the interest of seeing how much merb actually kicks Rails’ ass and a learning experience for me.

Also, Merb’s source code is very, very pretty.

Throat Cramps

Thursday, October 16th, 2008

Occasionally when I’m moving my head I get these throat cramps just on the right side of my throat. During these cramping phases the muscle (?) that moves up and down when you swallow (have a look in the mirror and watch your throat as you swallow, you’ll know what I mean) is what cramps. This can be extremely painful and I know of nobody else who gets these. I can only describe it as a cramp. There is a large sharp pain (kind of like when you get hit in the throat) when swallowing and a slightly lesser pain when I tilt my head backwards or to the left. Again, this only happens on one side of my throat.

I was prescribed anti-histamine tablets last year for this problem and it’s only manifested itself 4 times since then, the last 2 times have been within a month of each other. Generally, tilting my head back and drinking about a litre of water helps, but not the last two times. The last two times I’ve had to wait it out, and it’s been 3 hours and 1 hour respectively.

Has anybody out there in the blogosphere heard of this before?

Two Guys Break Into A Car…

Thursday, October 9th, 2008

Well, I’ll assume it was two. Some idiot(s) in the middle of the night smashed the corner of my back-right window and rifled through my car looking for something. Obviously they didn’t find it because the car is still there. They changed the gearstick to reverse, took off the handbrake and moved an American flag I had in the boot into the backseat (I know! The outrage!). Everything’s still in the car, just in more disarray than usual.

Orderly Code is Readable Code

Tuesday, October 7th, 2008

We were talking in #offrails today about coding standards and a few points came up on how to lay out “good” rails code. They were:

  • Controllers laid out in this order: index, show, new, create, edit, update, destroy, custom actions
  • Models laid out in this order:  Associations, Validations, Callbacks, Class Methods, Instance Methods
For controllers I would put filters first, then the 7 restful actions (index, show, new, create, edit, update and destroy) and then the custom actions and then if I needed to find a parent resource (for example /forums/1/topics) I would call private and then put a find_forum method there.