<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <title>Simple thoughts - articles</title>
  <id>tag:www.saimonmoore.net,2007:mephisto/blog</id>
  <generator version="0.7.3" uri="http://mephistoblog.com">Mephisto Noh-Varr</generator>
  
  <link href="http://www.saimonmoore.net/blog" rel="alternate" type="text/html" />
  <updated>2007-11-02T15:32:47Z</updated>
  <link rel="self" href="http://feeds.feedburner.com/saimonmoore" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-11-02:1922</id>
    <published>2007-11-02T15:23:00Z</published>
    <updated>2007-11-02T15:32:47Z</updated>
    <category term="articles" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/GeHUsQfzTcQ/mephisto_i18n-update" rel="alternate" type="text/html" />
    <title>Mephisto_i18n update</title>
<content type="html">
            &lt;p&gt;Just a quick heads up on the status of this for those who are interested.&lt;/p&gt;


	&lt;p&gt;Though it may seem that this project has died from lack of activity, I am still very much maintaining this project and have been adding fixes and tweaks on and off during the last few months.&lt;/p&gt;


	&lt;p&gt;In fact, I’ve just committed a couple of fixes:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Correct subdomain parsing for non-multisite mephisto instances (Bug was introduced when adding multisite support)&lt;/li&gt;
		&lt;li&gt;Making the dynamic i18n link switcher drop method handle paged articles whose section is the home section.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The future status of this project is basically pending a rewrite of globalize. I had already written the next version of globalize (currently stored in a branch) with support for rfc4646 but with the new rails i18n api talks going on, I’m waiting for these to conclude and get the rewrite for globalize 2 done before updating this plugin for that new version.&lt;/p&gt;


	&lt;p&gt;So….&lt;/p&gt;


	&lt;p&gt;To all those currently/thinking of using the plugin, feel free to bug me with questions, bug reports and help. I’m still commited to maintaining this.&lt;/p&gt;


	&lt;p&gt;Btw, the latest version in trunk also has support for plugin template handlers, so you can now use erb, haml or whatever else takes your fancy.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/11/2/mephisto_i18n-update</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-10-23:1921</id>
    <published>2007-10-23T09:14:00Z</published>
    <updated>2007-10-23T10:00:03Z</updated>
    <category term="articles" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/CrwLN3U7eFc/more-validations" rel="alternate" type="text/html" />
    <title>more validations</title>
<content type="html">
            &lt;p&gt;I wrote up a validates_condition_between validations class method the other day. It  basically validates that the values of two attributes matches the condition supplied. Only one condition can be used at a time. I searched around for something like this but couldn’t find anything so I wrote it.&lt;/p&gt;


	&lt;p&gt;Usage:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;   &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Person&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;     validates_condition_between &lt;span class="sy"&gt;:start_time&lt;/span&gt;, &lt;span class="sy"&gt;:end_time&lt;/span&gt;, &lt;span class="sy"&gt;:condition&lt;/span&gt;=&amp;gt;&lt;span class="sy"&gt;:greater_than&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;     validates_condition_between &lt;span class="sy"&gt;:credit&lt;/span&gt;, &lt;span class="sy"&gt;:max_credit&lt;/span&gt;, &lt;span class="sy"&gt;:condition&lt;/span&gt;=&amp;gt;&lt;span class="sy"&gt;:less_than_or_equal_to&lt;/span&gt;, &lt;span class="sy"&gt;:allow_blank&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;     validates_condition_between &lt;span class="sy"&gt;:key&lt;/span&gt;, &lt;span class="sy"&gt;:keys&lt;/span&gt;, &lt;span class="sy"&gt;:condition&lt;/span&gt;=&amp;gt;&lt;span class="sy"&gt;:inclusion&lt;/span&gt;, &lt;span class="sy"&gt;:allow_nil&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;   &lt;span class="r"&gt;end&lt;/span&gt;  &lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;I’ve stuck it in the following plugin (just the one validation for now):&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;script/plugin install svn://rubyforge.org/var/svn/sidirodromos/plugins/more_validations&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/10/23/more-validations</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-07-04:1881</id>
    <published>2007-07-04T06:20:00Z</published>
    <updated>2007-07-04T06:21:34Z</updated>
    <category term="articles" />
    <category term="conferences" />
    <category term="ostrava" />
    <category term="rails,globalize" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/V4TnOyRHGhs/after-ostrava" rel="alternate" type="text/html" />
    <title>After Ostrava</title>
<content type="html">
            &lt;p&gt;Well it’s been a couple of weeks since my weekend escapade to &lt;a href="http://ostrava.rails.cz/en"&gt;Ostrava on Rails&lt;/a&gt; in the Czech Republic.&lt;/p&gt;


	&lt;p&gt;I had a thoroughly good time. My trip out was as uneventful as &lt;a href="http://blog.leetsoft.com/2007/6/26/ostrava-on-rails"&gt;Toby’s&lt;/a&gt; was eventful. I had about an hour of time to kill at the Holešovice station and as I hadn’t eaten I decided to try some Czech cuisine. Granted, I probably didn’t choose the best representation of Czech restauranteurs but my fried cheese with deeply fried chips wasn’t too bad at all. I actually managed to order ‘ketchup’ without having to point to it.&lt;/p&gt;


	&lt;p&gt;When the train finally arrived I met up with &lt;a href="http://weblog.jamisbuck.org/2007/6/30/ostrava-on-rails"&gt;Jamis&lt;/a&gt; , Toby and &lt;a href="http://filip.hajny.net/"&gt;Filip&lt;/a&gt;. They were all really nice guys and I hardly noticed the 3½ hours the (very comfy might I add) pendolino train took to take us to Ostrava.&lt;/p&gt;


	&lt;p&gt;When we actually got there, it was pissing down with rain. Thankfully, Lucie was there to pick us up and though her car’s demisting system decided to go on holiday and the trip to the hotel was a bit harrowing (at least from where I was sitting :), it was a short trip and we were soon settled in. Lucie kindly pointed us to the local diners and in the end we chose a pizza place, whose name I forgot but which was really very nice. Though I didn’t try one myself the pizza’s were huge and looked very tasty.&lt;/p&gt;


	&lt;p&gt;Day 1 of the conference, and Lucie was there again bright and early to pick us up. I really enjoyed the nice relaxing walk over to the conference center. That really made the world of difference and helped me calm my nerves.&lt;/p&gt;


	&lt;p&gt;Jamis was up first and talking about capistrano was inevitably a hot topic and very interesting. I got a few questions in as regards deprec and cap2 which was of especial interest to me as I want to see when will be the right moment to integrate &lt;a href="http://webtypes.com/2007/3/15/announcing-capistrano-server-extensions"&gt;capservext&lt;/a&gt; with deprec. I’d rather not have to repeat myself.&lt;/p&gt;


	&lt;p&gt;I was up next and I felt I did a good job mostly though I couldn’t completely shake off the nerves. There certainly was a lot of interest from the public judging from the questions I got which I thought was a very good sign for globalize and i18n on rails in general. You can see the videos of the talks &lt;a href="http://www.rails.cz/articles/2007/06/27/oor-videos"&gt;here&lt;/a&gt;. The slides I used are available &lt;a href="http://saimonmoore.net/assets/2007/7/3/globalize_ostrava_2007.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;We then had an interesting talk about possible rails networking initiatives in the Czech republic and the chance to talk to an existing java user group in Luxembourg. I must say that I agree with Toby and Jamis when they say that these things are better off started on a smaller more local scale via occasional meet ups and communication via interested developers and then scale up. Nevertheless, the Ostrava conference showed that there was plenty of interest in the Czech republic (and in the greater eastern european area) for rails and I think it was a great platform to bootstrap the kind of rails communites required to build up a solid rails presence.&lt;/p&gt;


	&lt;p&gt;Certainly, the efficiency with which the whole conference was handled is a great testiment to their commitment for promoting rails. I am thoroughly looking forward to next years edition, and though it would make more sense to do it in Prague, I felt right at home in Ostrava, thanks to people like Robert Cigán, Jiří Kubica and Lucie Maliková.&lt;/p&gt;


	&lt;p&gt;After the video conference, Tobias Luetke was up talking about how he got &lt;a href="http://shopify.com"&gt;shopify&lt;/a&gt; to where it is today. A really interesting talk and very eye opening. I smiled when he said ”....if in doubt the designer is always right” (or something like that). I’ve had a number of ‘disputes’ shall we say with my ‘designer’ (Thomas Maas) and inevitably he ends up being right.&lt;/p&gt;


	&lt;p&gt;Luke Francl, talked up using sms gateways to send/receive sms/mms and showed us how he came up with a nice simple way to automate this. I might have use for that some time soon but seems like spanish carriers don’t like to play ball in this respect.&lt;/p&gt;


	&lt;p&gt;I really enjoyed Tim  Lossen’s talk on setting up a rails cluster for 100.000 &lt;span class="caps"&gt;CZK&lt;/span&gt;. He suggested  virtualizing a physical server into separate ‘logical’ servers for the app servers and db and then expanded on this. He mentioned switfiplied mongrel which I’ve been meaning to test. He also showed a site where you can find good deals on dedicated servers.  All in all an excellent talk. Check out &lt;a href="http://www.nuditybuffer.tv/"&gt;his&lt;/a&gt; slides when they come out.&lt;/p&gt;


	&lt;p&gt;We finished off the day with a final discussion. A few questions were heard then but mostly people were ready for the bar :)&lt;/p&gt;


	&lt;p&gt;After the Day 1 talks were over we were taken over to the downtown area on Stodoiny street. We had an excellant dinner at a cocktail bar and then some really nice czech beer at theatre-themed bar which we had practically to ourselves.&lt;/p&gt;


	&lt;p&gt;Day 2, despite having the slides in english was all in czech and though I’ve an ear for languages, I really need an extra one for czech :) Most of the talks were introductory ruby/rails aimed at students. Robert Cigán gave a talk on the porting of Skvely.cz main product to rails. From the slides I understood he was using a separate database for each customer and had come up with a neat technique for how to switch database connections for each client. Jiří Kubíček talk on rails deployment was also very interesting. He works for &lt;a href="http://railshosting.cz"&gt;railshosting.cz&lt;/a&gt; which is offering free accounts for developers to try deploying their rails apps. When you sign up they actually send you a preconfigured cap deploy recipe as an attachment in the welcome mail so all you need to be up and running is modify the repo url and cap deploy. Very nice. We spent the rest of Saturday after the talks in a bar decorated in american 60s style. That was our last evening in Ostrava.&lt;/p&gt;


	&lt;p&gt;All in all I had a great time. I met some lovely people, listened to some interesting talks, had interesting discussions and hopefully made one or two friends. I found myself connecting with and discovering that I had many things in common with others that live on the other side of the world. And all this was thanks to the great success that was the Ostrava on Rails conference.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/7/4/after-ostrava</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-06-20:1878</id>
    <published>2007-06-20T19:07:00Z</published>
    <updated>2007-06-20T19:10:23Z</updated>
    <category term="articles" />
    <category term="rails,globalize" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/OP0EybkR5PQ/talking-about-globalize-in-ostrava" rel="alternate" type="text/html" />
    <title>Talking about Globalize in Ostrava</title>
<content type="html">
            &lt;p&gt;I’m off tomorrow to Prague in the Czech republic and will then make my way to &lt;a href="http://ostrava.rails.cz/en/about"&gt;Ostrava&lt;/a&gt; where I’ll be giving a talk about Globalize and it’s future.&lt;/p&gt;


	&lt;p&gt;I’ll be posting the slides here on my blog once I’m back.&lt;/p&gt;


	&lt;p&gt;Say hello if you’re there :)&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/6/20/talking-about-globalize-in-ostrava</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-05-28:1856</id>
    <published>2007-05-28T13:56:00Z</published>
    <updated>2007-05-28T14:28:10Z</updated>
    <category term="articles" />
    <category term="site" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/VKEk-cPIN1c/saimonmoore-net-now-runs-multiple-globalized-sites" rel="alternate" type="text/html" />
    <title>saimonmoore.net now runs multiple globalized sites</title>
<content type="html">
            &lt;p&gt;I recently upgraded my &lt;a href="http://webtypes.com/2007/3/18/globalizing-mephisto"&gt;mephisto_i18n plugin&lt;/a&gt; to add complete support for multiple sites.&lt;/p&gt;


	&lt;p&gt;In the process, I converted the mephisto instance running http://saimonmoore.net into a multisite instance. Currently, the other &lt;em&gt;instance&lt;/em&gt; is our companie’s site &lt;a href="http://webtypes.com"&gt;webtypes.com&lt;/a&gt; which I ported over from &lt;a href="http://groups.google.com/group/MephistoBlog/browse_thread/thread/5cbe5557c2aacf3d"&gt;textpattern&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;You can read more about how I did it in the &lt;a href="http://webtypes.com/2007/5/15/globalizing-mephisto-part-3"&gt;third installment&lt;/a&gt; of my globalizing mephisto series.&lt;/p&gt;


	&lt;p&gt;Other improvements to the site have been:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Added better spam protection via Sven’s &lt;a href="http://www.artweb-design.de/2007/5/8/report-30-days-with-no-blog-spam-on-mephisto"&gt;article&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;Added an email-based &lt;a href="/contact"&gt;contact form&lt;/a&gt; so I’ll hopefully not miss any feedback this time.&lt;/li&gt;
	&lt;/ul&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/5/28/saimonmoore-net-now-runs-multiple-globalized-sites</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-05-17:1354</id>
    <published>2007-05-17T09:58:00Z</published>
    <updated>2007-05-17T10:00:55Z</updated>
    <category term="articles" />
    <category term="plugins" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/nSfXhjZjYLw/active-event-plugin" rel="alternate" type="text/html" />
    <title>ActiveEvent: My latest plugin</title>
<content type="html">
            &lt;p&gt;For one of my current applications I had a need for an event handling system.&lt;/p&gt;


	&lt;p&gt;I needed to able to dispatch events during the application’s lifecycle, and have observers respond to them appropriately. I also needed to keep an audit trail of the user’s interaction with the application.&lt;/p&gt;


	&lt;p&gt;Rails has the basis of an event handling system for ActiveRecord models in the form of  it’s callbacks that ActiveRecord::Observer’s can react upon. However, this is limited to a set amount of callbacks and doesn’t support a wide variety of events.&lt;/p&gt;


	&lt;p&gt;So I wrote ActiveEvent to do this.&lt;/p&gt;


	&lt;p&gt;I actually wrote this code about a year ago for Rails 1.0 when I was developing 
&lt;a href="/portfolio"&gt;ocium.com&lt;/a&gt; but I’ve since needed this for one of &lt;a href="http://webtypes.com"&gt;our&lt;/a&gt; current internal applications and I’ve refactored and packaged into a plugin that others can use.&lt;/p&gt;


	&lt;p&gt;“What do I use this for and how do I use it?” – A very good question.&lt;/p&gt;


	&lt;p&gt;Use it for:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;Creating an synchronous order event handling system with events like OrderPlacedEvent, OrderShippedEvent, OrderRefusedEvent. Have observers that react to each event in a distinct and customised way. Perhaps, you want to send an email informing the client of the fact that their order has been shipped (very simple use case). Your imagination is the limit.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;(NOTE: ActiveEvent in combination with the ActsAsStatemachine plugin makes for a very powerful way of controlling your application’s lifecycle.)&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;Want to be able to easily track a users actions within your application? This plugin makes this a piece of cake.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;Let’s see it in action&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Order&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  has_active_events    &lt;span class="c"&gt;#Assumes an existing User model class but is customizable&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;place_order&lt;/span&gt;(msg)&lt;tt&gt;
&lt;/tt&gt;    send_event(&lt;span class="co"&gt;ActiveEvent&lt;/span&gt;::&lt;span class="co"&gt;OrderPlacedEvent&lt;/span&gt;.new(&lt;span class="pc"&gt;self&lt;/span&gt;, &lt;span class="pc"&gt;nil&lt;/span&gt;, msg)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;OrderObserver&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Observer&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;after_save&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="c"&gt;#do something here&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;on_event&lt;/span&gt;(order, event)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="c"&gt;#react to the event&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="c"&gt;#e.g.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;case&lt;/span&gt; &lt;span class="co"&gt;Event&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="co"&gt;OrderShippedEvent&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="co"&gt;OrderNotifier&lt;/span&gt;.deliver_order_shipped(order, event)&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="co"&gt;OrderCancelledEvent&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        order.cancel! &lt;span class="c"&gt;#Change model state via ActsAsStatemachine plugin&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="c"&gt;#Add the event to the audit system&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;ActiveEvent&lt;/span&gt;::&lt;span class="co"&gt;AuditTrail&lt;/span&gt;.add_event(event) &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Get it installed:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;script/plugin install svn://rubyforge.org/var/svn/sidirodromos/plugins/active_event/trunk&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;script/generate active_event migrations&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Change coming soon:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Add an option to has_active_events method to allow transparent event tracking. i.e. In above example, you’d no longer need to add the  &lt;em&gt;ActiveEvent::AuditTrail.add_event(event)&lt;/em&gt; call.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;e.g.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Order&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  has_active_events &lt;span class="sy"&gt;:audit&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;ul&gt;
	&lt;li&gt;Make the auditing system completely optional (No need to create its tables unless you want it)&lt;/li&gt;
		&lt;li&gt;Add asynchronous notifications support  via the backgroundrb plugin&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Actually I thought these todo’s were so important I added them in already :)&lt;/p&gt;


	&lt;p&gt;Have a look at the plugins &lt;span class="caps"&gt;README&lt;/span&gt; for details and examples. Then have a look at it’s tests.&lt;/p&gt;


	&lt;p&gt;P.S. I’m thinking about adding support for the &lt;a href="http://en.wikipedia.org/wiki/Memento_pattern"&gt;Memento pattern&lt;/a&gt; to provide granularized undo possibilities…&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/5/17/active-event-plugin</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-05-17:1854</id>
    <published>2007-05-17T09:57:00Z</published>
    <updated>2007-05-18T21:00:41Z</updated>
    <category term="articles" />
    <category term="rails,globalize" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/SQCQuNJTks0/localized-urls" rel="alternate" type="text/html" />
    <title>Localized urls</title>
<content type="html">
            &lt;p&gt;In this article, I’m going to describe a simple way to localize your application’s urls using globalize.&lt;/p&gt;


	&lt;h3&gt;Localize your urls&lt;/h3&gt;


What do I mean by “localize your urls” exactly? For example, you browse to :
&lt;pre&gt;&lt;code&gt;
   http://ocium.com/artists/millivanilli
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;and are shown the “Milli Vanilli” artist info page in the base locale (which is ‘english’ for arguments sake). You click a link to switch locale within the page and you’re transported to:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
    http://es.ocium.com/artistas/millivanilli
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;As you can see, the whole url has been translated into the language of the currently active locale.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Locale defined as a path parameter&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;In this example, I’m using subdomains to indicate the locale, but you can easily use path/query parameters to define the locale.&lt;/p&gt;


e.g.
&lt;pre&gt;&lt;code&gt;
   http://ocium.com/en/artists/millivanilli
   =&amp;gt;
   http://ocium.com/es/artistas/millivanilli
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;The plugin&lt;/h3&gt;


	&lt;p&gt;I’ve wrapped up the code to do that within the &lt;a href="http://sidirodromos.rubyforge.org/"&gt;LocalizedRoutes&lt;/a&gt; plugin:&lt;/p&gt;


	&lt;h4&gt;Installation&lt;/h4&gt;


	&lt;ul&gt;
	&lt;li&gt;Assuming you have the latest version of globalize installed (at least for-1.2),&lt;/li&gt;
		&lt;li&gt;Install the plugin via :&lt;pre&gt;&lt;code&gt;
script/plugin install svn://rubyforge.org/var/svn/sidirodromos/plugins/localized_routes/trunk
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Usage&lt;/h3&gt;


	&lt;p&gt;You can now translate the urls in your routes via:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.draw &lt;span class="r"&gt;do&lt;/span&gt; |map|&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="c"&gt;#Will translate 'list' to the default namespace&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;.t, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="c"&gt;#Will translate 'list' to the 'urls' namespace&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;&lt;b&gt;Note&lt;/b&gt;: The use of the &lt;em&gt;‘urls’ namespace&lt;/em&gt; when translating routes. This is recommended as it’s then easy to separate url translations from other translations.&lt;/p&gt;


	&lt;p&gt;The &lt;strong&gt;localized_routes&lt;/strong&gt; plugin looks at the incoming request and calculates which locale is to be set as active from one of three possible methods in the following order:&lt;/p&gt;


&lt;ol&gt;
&lt;li&gt;&lt;em&gt;“Query parameter”&lt;/em&gt; named &lt;strong&gt;“locale”&lt;/strong&gt; e.g
&lt;pre&gt;&lt;code&gt;
http://saimonmoore.net/listar?locale=es
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;“Path parameter”&lt;/em&gt; extracted by matching request path against Globalize::LocalizedRoutes::LOCALE_PATH_MATCHING_RE e.g.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt; &lt;span class="co"&gt;Globalize&lt;/span&gt;::&lt;span class="co"&gt;LocalizedRoutes&lt;/span&gt;::&lt;span class="co"&gt;LOCALE_PATH_MATCHING_RE&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt; =&amp;gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;^&lt;/span&gt;&lt;span class="ch"&gt;\/&lt;/span&gt;&lt;span class="k"&gt;([&lt;/span&gt;&lt;span class="ch"&gt;\w&lt;/span&gt;&lt;span class="k"&gt;-]+)|&lt;/span&gt;&lt;span class="ch"&gt;\/&lt;/span&gt;&lt;span class="k"&gt;([&lt;/span&gt;&lt;span class="ch"&gt;\w&lt;/span&gt;&lt;span class="k"&gt;-]+)&lt;/span&gt;&lt;span class="ch"&gt;\/&lt;/span&gt;&lt;span class="k"&gt;.*$&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;
 http://saimonmoore.net/es/listar
 or
 http://saimonmoore.net/es
&lt;/code&gt;&lt;/pre&gt;

&lt;b&gt;Note&lt;/b&gt;: &lt;em&gt;If you want to define the locale in the path differently, then just override the regexp&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;“Subdomain”&lt;/em&gt; e.g.
&lt;pre&gt;&lt;code&gt;
http://es.saimonmoore.net/listar
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

	&lt;p&gt;If you’re using the path parameter to define your locale then it’s a good idea to specify it in the route like so:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.draw &lt;span class="r"&gt;do&lt;/span&gt; |map|&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Then you’ll have access to the &lt;strong&gt;“locale”&lt;/strong&gt; parameter in the controller and it’s useful for url helper methods.&lt;/p&gt;


	&lt;h3&gt;Some examples:&lt;/h3&gt;


	&lt;h4&gt;You want to define the “locale via a path parameter”.&lt;/h4&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.draw &lt;span class="r"&gt;do&lt;/span&gt; |map|&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;You translate the urls for each language you support:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_base_language(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;en-GB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_translation_with_namespace(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/listar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;fr-FR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_translation_with_namespace(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/enumerer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;You can now access:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
 http://saimonmoore.net/en/list
 http://saimonmoore.net/es/listar
 http://saimonmoore.net/fr/enumerer
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;To access:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;http://saimonmoore.net/list&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;you’d need to additionally define:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;&lt;strong&gt;If, on the other hand, you’d like to define the “locale via subdomains”, then:&lt;/strong&gt;&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.draw &lt;span class="r"&gt;do&lt;/span&gt; |map|&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Again you translate the urls for each language you support:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_base_language(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;en-GB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_translation_with_namespace(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/listar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;fr-FR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;Locale&lt;/span&gt;.set_translation_with_namespace(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/enumerer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;You can now access:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
http://saimonmoore.net/list
http://es.saimonmoore.net/listar
http://fr.saimonmoore.net/enumerer
&lt;/code&gt;&lt;/pre&gt;

	&lt;h3&gt;Switch Locale Links&lt;/h3&gt;


	&lt;p&gt;Here are examples of how you’d create a list of links to switch the current locale using all three methods for defining the locale:&lt;/p&gt;


Assuming routes defined as:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.draw &lt;span class="r"&gt;do&lt;/span&gt; |map|&lt;tt&gt;
&lt;/tt&gt;  map.list_with_locale &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:locale/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  map.list &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="sy"&gt;:urls&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  map.connect &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;:controller/:action/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;For this example I’ve already translated the urls for each of the supported languages.&lt;/em&gt;&lt;/p&gt;


	&lt;p&gt;Then in your partial (or extract to a helper):&lt;/p&gt;


&lt;ul&gt;
  &lt;li&gt;
    &lt;strong&gt;path parameter&lt;/strong&gt;
    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;SupportedLocales&lt;/span&gt;.supported_language_codes.each &lt;span class="r"&gt;do&lt;/span&gt; |code| &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;Locale&lt;/span&gt;.switch_locale(code) &lt;span class="r"&gt;do&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.reload &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class="co"&gt;Locale&lt;/span&gt;.language.native_name, list_with_locale_url(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; code) &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


&lt;b&gt;Note&lt;/b&gt;:&lt;em&gt;To make it even simpler you could use one of these &lt;a href="http://www.artweb-design.de/2007/5/13/concise-localized-rails-url-helpers-solved-twice"&gt;techniques&lt;/a&gt; to only specify &lt;strong&gt;“list_with_locale_url”&lt;/strong&gt;&lt;/em&gt;

    &lt;br /&gt;
Browsing to http://saimonmoore.net/list generates:

    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/en/list&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;English&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/el/katalago&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Ελληνικά&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/es/listar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Español&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/ca/listar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Català&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

  &lt;/li&gt;

  &lt;li&gt;
    &lt;strong&gt;query parameter&lt;/strong&gt;
    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;SupportedLocales&lt;/span&gt;.supported_language_codes.each &lt;span class="r"&gt;do&lt;/span&gt; |code| &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;Locale&lt;/span&gt;.switch_locale(code) &lt;span class="r"&gt;do&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.reload &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class="co"&gt;Locale&lt;/span&gt;.language.native_name, list_url(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; code) &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


    Browsing to http://saimonmoore.net/list generates:

    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/list?locale=en&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;English&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/katalogo?locale=el&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Ελληνικά&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/listar?locale=es&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Español&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/listar?locale=ca&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Català&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

  &lt;/li&gt;

  &lt;li&gt;
    &lt;strong&gt;subdomain&lt;/strong&gt;
    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;SupportedLocales&lt;/span&gt;.supported_language_codes.each &lt;span class="r"&gt;do&lt;/span&gt; |code| &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;Locale&lt;/span&gt;.switch_locale(code) &lt;span class="r"&gt;do&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.reload &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class="co"&gt;Locale&lt;/span&gt;.language.native_name, subdomain_for(&lt;span class="sy"&gt;:list_url&lt;/span&gt;) &lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="il"&gt;&lt;span class="dl"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; &lt;span class="dl"&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


    Browsing to http://saimonmoore.net/list generates:

    &lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://saimonmoore.net/list&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;English&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://el.saimonmoore.net/katalogo&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Ελληνικά&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://es.saimonmoore.net/listar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Español&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://ca.saimonmoore.net/listar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Català&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

  &lt;/li&gt;
&lt;/ul&gt;

	&lt;h4&gt;Test application&lt;/h4&gt;


	&lt;p&gt;If you’d like to experiment with this, I’ve bundled up a &lt;a href="/files/testapp.tar.gz"&gt;test application&lt;/a&gt; which has everything you need to play around with this. Just set up a db &amp; run rake globalize:setup. In the db directory you can find an sql file, with all the translations used in this example.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;In the last example I used a subdomain_for() helper to correctly generate the right host for each locale. This is very simple and you can see the code in the example app.&lt;/em&gt;&lt;/p&gt;


	&lt;h3&gt;The code&lt;/h3&gt;


	&lt;p&gt;I’ve babbled a lot about how you can use this plugin but let’s take a look at the actual code. It’s really quite simple.&lt;/p&gt;


	&lt;p&gt;In essence, I realized that for each request that goes through rails, the dispatcher takes care of running it through the defined routes in order to recognize the appropriate controller and action for the request and then passes it along to the controller for processing.&lt;/p&gt;


	&lt;p&gt;What I needed then is to ensure that the locale is set before routing recognition occurs&lt;/p&gt;


	&lt;p&gt;So I simple overrode Dispatcher.dispatch() to add in a call to a set_locale() method &lt;strong&gt;before&lt;/strong&gt; the call to ActionController::Routing::Routes.recognize. This means Globalize::Locale is now set when the routing code does it’s recognition which means you can then translate strings within the route definitions.&lt;/p&gt;


	&lt;p&gt;i.e.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;dispatch_with_locale&lt;/span&gt;(cgi = &lt;span class="pc"&gt;nil&lt;/span&gt;, session_options = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;CgiRequest&lt;/span&gt;::&lt;span class="co"&gt;DEFAULT_SESSION_OPTIONS&lt;/span&gt;, output = &lt;span class="gv"&gt;$stdout&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;        controller = &lt;span class="pc"&gt;nil&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="r"&gt;if&lt;/span&gt; cgi ||= new_cgi(output)&lt;tt&gt;
&lt;/tt&gt;          request, response = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;CgiRequest&lt;/span&gt;.new(cgi, session_options), &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;CgiResponse&lt;/span&gt;.new(cgi)&lt;tt&gt;
&lt;/tt&gt;=&amp;gt;        set_locale(request)  &lt;span class="c"&gt;#Set the locale before route recognition&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          prepare_application&lt;tt&gt;
&lt;/tt&gt;          controller = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Routing&lt;/span&gt;::&lt;span class="co"&gt;Routes&lt;/span&gt;.recognize(request)&lt;tt&gt;
&lt;/tt&gt;          controller.process(request, response).out(output)&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      ....&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;I hadn’t seen Dispatcher.to_prepare before and Josh Sierles brought it to my attention however, this only executes the callback on the &lt;strong&gt;first&lt;/strong&gt; request in production mode so it’s not really usefull in this case. Another problem is that set_locale needs access to the request. I’m thinking of sending in a patch to rails to make it configurable and will then convert the code to using this.&lt;/em&gt;&lt;/p&gt;


	&lt;p&gt;Take a look at the &lt;a href="http://viewvc.rubyforge.mmmultiworks.com/cgi/viewvc.cgi/plugins/localized_routes/?root=sidirodromos"&gt;code&lt;/a&gt; to see what set_locale does (though it’s nothing fancy).&lt;/p&gt;


	&lt;p&gt;Hope that you find this handy. I certainly have.&lt;/p&gt;


	&lt;p&gt;Saimon&lt;/p&gt;


	&lt;p&gt;P.S. I’ve incorporated this functionality directly into my &lt;a href="http://webtypes.com/2007/3/18/globalizing-mephisto"&gt;mephisto_i18n plugin&lt;/a&gt; which makes it handy if you need to extend mephisto with custom routes.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I’ve refactored the code slightly to use a patched Dispatcher which adds support for request_callbacks (similar to preparation_callbacks but which are executed on every request and which have access to the request object).&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Problem?&lt;/strong&gt;: The code now reloads the routes for the current locale &lt;strong&gt;on every request&lt;/strong&gt;. This is so that you can reload routes within Locale.switch_locale blocks and generate the localized url correctly. I realize this may have a performance hit but initiall tests show this to minimal.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/5/17/localized-urls</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-21:1350</id>
    <published>2007-03-21T17:59:00Z</published>
    <updated>2007-03-21T18:32:19Z</updated>
    <category term="articles" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/_BuUjWBDs_s/saimonmoore-net-is-now-globalized" rel="alternate" type="text/html" />
    <title>saimonmoore.net is now Globalized!</title>
<content type="html">
            &lt;p&gt;Back up again…&lt;/p&gt;


	&lt;p&gt;The process of rebuilding my &lt;a href="https://manage.slicehost.com/customers/signup?referrer=10144130"&gt;slice&lt;/a&gt; was relatively painless. It took me all of four hours to go from my original mephisto installation, to a completely new &lt;a href="https://manage.slicehost.com/customers/signup?referrer=10144130"&gt;slice&lt;/a&gt; with a &lt;a href="http://www.webtypes.com/2007/03/18/globalizing-mephisto"&gt;globalized&lt;/a&gt; mephisto trunk, restored subversion repository.&lt;/p&gt;


	&lt;p&gt;Twice! As I also host a veterinary clinic &lt;a href="http://ktiniatreio.gr"&gt;site&lt;/a&gt; for a friend.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/21/saimonmoore-net-is-now-globalized</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-18:1349</id>
    <published>2007-03-18T13:16:00Z</published>
    <updated>2007-03-21T13:57:43Z</updated>
    <category term="articles" />
    <category term="site" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/-fWOveNncy0/saimonmoore-net-down-for-maintenance" rel="alternate" type="text/html" />
    <title>saimonmoore.net down for maintenance</title>
<content type="html">
            &lt;p&gt;To all my  loyal readers (all 6 of you :).&lt;/p&gt;


	&lt;p&gt;I’m taking the site down temporarily as I want to rebuild it using &lt;a href="http://www.webtypes.com/2007/03/15/announcing-capistrano-server-extensions"&gt;Capistrano Server Extensions&lt;/a&gt; and the latest &lt;a href="http://mephistoblog.com"&gt;Mephisto&lt;/a&gt; trunk with my &lt;a&gt;Mephisto I18n plugin&lt;/a&gt; (An article about this plugin will be coming &lt;a href="#"&gt;forthwith&lt;/a&gt; ). To eat my own dog food as they say.&lt;/p&gt;


	&lt;p&gt;Also I’m not very happy about the site’s styling so I’m going to try and change that soon as well.&lt;/p&gt;


	&lt;p&gt;Back soon…&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/18/saimonmoore-net-down-for-maintenance</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-18:1343</id>
    <published>2007-03-18T13:11:00Z</published>
    <updated>2007-04-01T23:50:51Z</updated>
    <category term="articles" />
    <category term="globalize" />
    <category term="i18n" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/9GzptpfmvsE/namespaced-view-translations" rel="alternate" type="text/html" />
    <title>Namespaced View Translations</title>
<content type="html">
            &lt;p&gt;ViewTranslations are a very handy way of translating all of your content that isn’t represented by an ActiveRecord model, which is actually quite a lot of content.&lt;/p&gt;


	&lt;p&gt;And they work great:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#In es locale

"translate me".t =&amp;gt; "Traduceme"

"%s, can you translate this?" / 'saimon' =&amp;gt; '¿Saimon, puedes traducir esto?'

"%d translations" / 0 =&amp;gt; "No hay traducciónes"
"%d translations" / 1 =&amp;gt; "1 traducción"
"%d translations" / 10 =&amp;gt; "10 traducciónes"
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;But imagine you’re using view translations all over the place in your application.&lt;/p&gt;


	&lt;p&gt;You use them to translate the static content of your view templates.
You use them to translate the urls in your links.
You use them to translate your administration interface.&lt;/p&gt;


	&lt;p&gt;After a while the list of translations starts to become a bit too unwieldy.&lt;/p&gt;


	&lt;p&gt;Let’s say that you decide to bring in professional translators, who may be decidedly non technical, to translate your site’s content.&lt;/p&gt;


	&lt;p&gt;You point them to your new spiffy, paginated, searchable list of words/phrases to be translated and leave them to it.&lt;/p&gt;


	&lt;p&gt;The day after your boss phones you up and bellows:&lt;/p&gt;


	&lt;p&gt;- None of the site’s links are working in Slovenian?&lt;/p&gt;


	&lt;p&gt;Oops! You start to investigate and find that the stupid translators, went ahead and translated:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
'section' =&amp;gt; 'sección' (In Spanish)
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;which you were using to translate the url:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
http://example.com/section =&amp;gt; http://example.com/seccion
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;- Jeez, don’t translators know you can’t use non-ascii characters in urls?&lt;/p&gt;


	&lt;p&gt;- Umm, well no. They’re translators. That’s your job.&lt;/p&gt;


	&lt;p&gt;- Ah, well, ok I’ll just give them a list of words that are urls and should be translated without accents.&lt;/p&gt;


	&lt;p&gt;- But we use the word ‘section’ in the view as well…&lt;/p&gt;


	&lt;p&gt;- ummm… Let me think….. (time passes)&lt;/p&gt;


	&lt;p&gt;- Also we need the site in Greek which has a lot of strange characters. And come to think of it, this list of urls is getting pretty hard to maintain.&lt;/p&gt;


	&lt;p&gt;-Well, what we need is to be able to have two sorts of translation of the word ‘section’, one for the front end and one for the urls.&lt;/p&gt;


	&lt;h5&gt;Using namespaced view translations&lt;/h5&gt;


	&lt;p&gt;And that’s what &lt;em&gt;&lt;strong&gt;namespaced&lt;/strong&gt; view translations&lt;/em&gt; are all about:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#In es locale

"section".t         =&amp;gt; "sección"  #The default translation with no namespace

"section" &amp;gt;&amp;gt; 'urls' =&amp;gt; "seccion"  #The translation in the 'urls' namespace
or
"section".tn(:urls)
or
"draw".translate_with_namespace(:urls)

#The translation in the 'admin' namespace
"section" &amp;gt;&amp;gt; 'admin' =&amp;gt; "sección"

# Text interpolation of the translation in the 'admin' namespace
"%s, can you translate this?".tn(:admin, 'saimon') =&amp;gt; '¿Saimon, puedes traducir esto?'

#Numeric interpolation of the translation in the 'admin' namespace with a zero value
"%d translations".tn(:admin, 0) =&amp;gt; "No hay traducciónes"

#Numeric interpolation of the translation in the 'admin' namespace with non-zero values
"%d translations".tn(:admin, 1) =&amp;gt; "1 traducción"
"%d translations".tn(:admin, 10) =&amp;gt; "10 traducciónes"

#Translation in the 'admin' namespace with a default value if no
#translation is available
"should be translated".tn(:admin,nil, 'untranslated') =&amp;gt; "untranslated"
&lt;/code&gt;
&lt;/pre&gt;

	&lt;h5&gt;Adding namespaced view translations to the database&lt;/h5&gt;


	&lt;p&gt;Adding translations with namespaces to the database uses a differently named method but
is basically the same syntax as Locale.set_translation:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
Locale.set('es-ES')

#No namespace
Locale.set_translation("section", "sección")

#With namespace 'urls'
Locale.set_translation_with_namespace("section", "urls", "seccion")
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;or adding translations with plural forms:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
Locale.set('es-ES')

#No namespace
Locale.set_translation("draw %d times", "dibujar una vez", "dibujar %d veces")

#With namespace 'lottery'
Locale.set_translation_with_namespace("draw %d times", "lottery", "seleccionar una vez", "seleccionar %d veces")
&lt;/code&gt;
&lt;/pre&gt;

	&lt;h5&gt;Filtering translations by namespace&lt;/h5&gt;


	&lt;p&gt;Once you have view translations categorised under different namespaces, you’ll most likely want to create a friendly interface for your translators so that they don’t translate phrases in the ‘urls’ namespace for example and can concentrate on the translations of the front-end.&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
url_translations = ViewTranslations.find(:all, :conditions =&amp;gt; {:namespace =&amp;gt; 'urls', :language =&amp;gt; spanish})
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Of course that’s very simplistic and you’d probably want to also distinguish between translations with plural forms, paginate etc but you get the idea.&lt;/p&gt;


	&lt;h5&gt;Upgrading your schema&lt;/h5&gt;


	&lt;p&gt;I needed to change the globalize view_translations table and add an extra varchar column to support namespaced view translations. So in order to use the for-1.2 release with your application, you need to upgrade your schema.&lt;/p&gt;


	&lt;p&gt;If you install the &lt;strong&gt;for-1.2&lt;/strong&gt; release via:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
script/plugin install http://svn.globalize-rails.org/svn/globalize/branches/for-1.2
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;then the plugin’s install hook will automatically update your globalize tables for you  if they already exist.&lt;/p&gt;


	&lt;p&gt;If you don’t use the &lt;em&gt;script/plugin install&lt;/em&gt; method, then you can simply run the following rake task which does the same thing:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
rake globalize:upgrade_schema_to_1_dot_2
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;If this is the first time you’re installing Globalize then just run the default globalize setup rake task:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
rake globalize:setup
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;That concludes this article. Watch out for the next &lt;a href="#"&gt;article&lt;/a&gt; in the series where I’ll be talking about &lt;strong&gt;Globalize::SupportedLocales&lt;/strong&gt;, a class that encapsulates the idea of an application’s supported locales.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/18/namespaced-view-translations</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-17:1333</id>
    <published>2007-03-17T19:56:00Z</published>
    <updated>2007-03-21T12:17:05Z</updated>
    <category term="articles" />
    <category term="globalize" />
    <category term="i18n" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/zgK0O4Wfi_Q/tools-for-migrating-to-internal-storage" rel="alternate" type="text/html" />
    <title>Making migrating to Globalize's internal storage mechanism easy</title>
<content type="html">
            &lt;p&gt;As I mentioned in my &lt;a href="http://saimonmoore.net/2007/3/17/globalize-internal-storage-mechanism"&gt;previous article&lt;/a&gt;, the &lt;a href="http://saimonmoore.net/2007/3/17/globalize-internal-storage-mechanism#troublespots"&gt;week spot&lt;/a&gt; of the internal storage mechanism, is taking care of adding the extra columns to the schema.&lt;/p&gt;


	&lt;p&gt;I also mentioned that using ActiveRecord::Migrations would make your life a lot easier and, really, if you aren’t already using them as standard in your rails development, your missing out on a lot of automation.&lt;/p&gt;


	&lt;p&gt;However, you’ve still got to mundanely type these things out, and we all know what us humans are like (We’ll I know myself at least :).&lt;/p&gt;


	&lt;p&gt;So, currently in the &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 branch&lt;/a&gt;  you can find a rails generator that will automate this procedure for you.&lt;/p&gt;


	&lt;h5&gt;How does this work?&lt;/h5&gt;


	&lt;p&gt;Imagine we have the following models:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#Assuming:

Globalize::DbTranslate.keep_translations_in_model = false

#or unset

class Noddy &amp;lt; ActiveRecord::Base; end

class Dummy &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  translates :name, :base_as_default =&amp;gt; true
end

class Epi &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  set_table_name :epifanias

  translates :name, :surnames
end

class Blas &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  translates :hobby
end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Let’s open up a shell and see&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
$ cd myapp
$ script/generate globalize internal es,fr
      exists  db/migrate
      exists  db/migrate
      create  db/migrate/006_globalize_add_translated_fields_for_dummy_epi_blas.rb
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;The script has created a migration for us…Let’s have a look see:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
class GlobalizeAddTranslatedFieldsForDummyEpiBlas &amp;lt; ActiveRecord::Migration
  def self.up
    #Fields for Dummy
    add_column :dummies, :name_es, :string
    add_column :dummies, :name_fr, :string
    #Fields for Epi
    add_column :epifanias, :name_es, :string
    add_column :epifanias, :surnames_es, :string
    add_column :epifanias, :name_fr, :string
    add_column :epifanias, :surnames_fr, :string
    #Fields for Blas
    add_column :blas, :hobby_es, :string
    add_column :blas, :hobby_fr, :string
  end

  def self.down
    #Fields for Dummy
    remove_column :dummies, :name_es
    remove_column :dummies, :name_fr
    #Fields for Epi
    remove_column :epifanias, :name_es
    remove_column :epifanias, :surnames_es
    remove_column :epifanias, :name_fr
    remove_column :epifanias, :surnames_fr
    #Fields for Blas
    remove_column :blas, :hobby_es
    remove_column :blas, :hobby_fr
  end
end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Cool! The migration contains all the missing columns required to support es (Spanish) and fr (French) apart from the base locale.&lt;/p&gt;


	&lt;p&gt;As it’s a generator we can also tell it to rollback that change:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
$ script/destroy globalize internal es,fr
    notempty  db/migrate
    notempty  db
    rm  db/migrate/006_globalize_add_translated_fields_for_dummy_epi_blas.rb
    notempty  db/migrate
    notempty  db
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;So once you’ve created your application, this generator makes it easy to maintain your schema if you decide to you use the internal storage mechanism.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Note for the future&lt;/strong&gt;: Currently you need to specify the languages your application supports on the command line. I’ve commited some code to Globalize trunk which encapsulates the idea of an application’s supported locales into a dedicated class. So for trunk I’ll be updating this generator to first check whether the SupportLocales.supported_locales attribute has been defined, so you won’t need to type in the locales on the command line (less chance for errors).&lt;/p&gt;


	&lt;p&gt;That’s what’s available to you in the for-1.2 release to help out with migrating to the new storage mechanism.&lt;/p&gt;


	&lt;p&gt;However, I’ve gone a step further and written a migration tool that more or less does all the work required for you to convert an existing application using the external storage system into one using the internal storage mechanism.&lt;/p&gt;


	&lt;p&gt;Let’s look at the class definitions again. This time we’re assuming this application is using the external storage mechanism already:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#Assuming:
Globalize::DbTranslate.keep_translations_in_model = false       #or unset
&lt;/code&gt;
&lt;/pre&gt;

&lt;pre&gt;
&lt;code&gt;
class Blas &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = false
  translates :hobby
end

class Dummy &amp;lt; ActiveRecord::Base
  translates :name, :base_as_default =&amp;gt; true
end

class Epi &amp;lt; ActiveRecord::Base
  set_table_name :epifanias

  translates :name, :surnames
end

class Noddy &amp;lt; ActiveRecord::Base; end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Let’s also look at the current contents of the database tables these models represent:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#_blas_
id  name    hobby
1   Tony    skiing
2   George  football
3   Sandra  tennis

#_dummies_
id  name
1   Epiphany
2   Socks

#_epifanias_
id  name    surnames
1  Saimon    Moore
2  Thomas    Maas
3  Maria     Perez

#_noddies_ has no data
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Let’s imagine that this is an application that already has some model translations using the external storage mechanism i.e. in the &lt;em&gt;globalize_translations&lt;/em&gt; table.&lt;/p&gt;


These are the current translations for these models (supports spanish (es) and (polish):
&lt;pre&gt;
&lt;code&gt;
id  item_id  table_name     facet       text          language_id
103     1       blas        hobby       esquipl         4
100     1       blas        hobby       esqui           7
104     2       blas        hobby       futbolpl        4
101     2       blas        hobby       futbol          7
105     3       blas        hobby       tenispl         4
102     3       blas        hobby       tenis           7
120     1       dummies     name        Saimon (pl)     4
118     1       dummies     name        Saimon (es)     7
121     2       dummies     name        Epifania (pl)   4
119     2       dummies     name        Epifania (es)   7
109     1       epifanias   name        Saimon pl       4
115     1       epifanias   surnames    Moore pl        4
106     1       epifanias   name        Saimon es       7
112     1       epifanias   surnames    Moore es        7
110     2       epifanias   name        Thomas pl       4
116     2       epifanias   surnames    Maas pl         4
107     2       epifanias   name        Thomas es       7
113     2       epifanias   surnames    Maas es         7
111     3       epifanias   name        Maria pl        4
117     3       epifanias   surnames    Perez pl        4
108     3       epifanias   name        Maria es        7
114     3       epifanias   surnames    Perez es        7
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;So let’s open up a console and type:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
$ rake globalize:migrate_to_internal_storage LANGS=es,pl
Logging to /home/saimon/dev/projects/localize_for-1.2/config/../log/internal_storage_migration_development.log
Migrate source for 'Blas'? (Yes/No/All)
Y
Migrating ruby source for: /home/saimon/dev/projects/localize_for-1.2/config/../app/models/blas.rb
Migrate source for 'Dummy'? (Yes/No/All)
Y
Migrating ruby source for: /home/saimon/dev/projects/localize_for-1.2/config/../app/models/dummy.rb
Migrate source for 'Epi'? (Yes/No/All)
Y
Migrating ruby source for: /home/saimon/dev/projects/localize_for-1.2/config/../app/models/epi.rb
Generate &amp; execute db migrations? (Yes/No)
Y
Generating db migrations...
      exists  db/migrate
      exists  db/migrate
      create  db/migrate/006_globalize_add_translated_fields_for_dummy_epi_blas.rb
Executing db migrations...
(in /home/saimon/dev/projects/localize_for-1.2)
== GlobalizeAddTranslatedFieldsForDummyEpiBlas: migrating =====================
-- add_column(:dummies, :name_es, :string)
   -&amp;gt; 0.6783s
-- add_column(:dummies, :name_pl, :string)
   -&amp;gt; 0.1232s
-- add_column(:epifanias, :name_es, :string)
   -&amp;gt; 0.1216s
-- add_column(:epifanias, :surnames_es, :string)
   -&amp;gt; 0.1105s
-- add_column(:epifanias, :name_pl, :string)
   -&amp;gt; 0.1438s
-- add_column(:epifanias, :surnames_pl, :string)
   -&amp;gt; 0.0994s
-- add_column(:blas, :hobby_es, :string)
   -&amp;gt; 0.1217s
-- add_column(:blas, :hobby_pl, :string)
   -&amp;gt; 0.1217s
== GlobalizeAddTranslatedFieldsForDummyEpiBlas: migrated (1.5246s) ============

Migrate translations for 'Blas'? (Yes/No/All)
Y
Also delete old external translations for 'Blas'? (Yes/No default: Y)
Y
Migrated translations for Blas
Migrate translations for 'Dummy'? (Yes/No/All)
Y
Also delete old external translations for 'Dummy'? (Yes/No default: Y)
Y
Migrated translations for Dummy
Migrate translations for 'Epi'? (Yes/No/All)
Y
Also delete old external translations for 'Epi'? (Yes/No default: Y)
Y
Migrated translations for Epi
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Whoa! What was all that?&lt;/p&gt;


	&lt;p&gt;Well, let’s have a look at what happened:&lt;/p&gt;


	&lt;p&gt;The class definitions have been modified:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
class Blas &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  translates :hobby
end

class Dummy &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  translates :name, :base_as_default =&amp;gt; true
end

class Epi &amp;lt; ActiveRecord::Base
  set_table_name :epifanias

  self.keep_translations_in_model = true
  translates :name, :surnames
end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;It has modified our model class definitions to mark them to use the internal storage mechanism.&lt;/p&gt;


	&lt;p&gt;Ah but what about the database?&lt;/p&gt;


	&lt;p&gt;Well let’s have a look if anything changed?&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#_blas_
id  name  hobby       hobby_es  hobby_pl
1  Tony    skiing     esqui     esquipl
2  George  football   futbol    futbolpl
3  Sandra  tennis     tenis     tenispl

#_dummies_
id  name     name_es        name_pl
1  Epiphany  Saimon (es)    Saimon (pl)
2  Socks     Epifania (es)  Epifania (pl)

#_epifanias_
id  name  surnames  name_es      surnames_es  name_pl       surnames_pl
1  Simon  Moore     Saimon es    Moore es     Saimon pl     Moore pl
2  Thomas Maas      Thomas es    Maas es      Thomas pl     Maas pl
3  Maria  Perez     Maria es     Perez es     Maria pl      Perez pl
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;That’s nice. The script has automatically created a migration to add all the extra language columns to the model tables &lt;span class="caps"&gt;AND&lt;/span&gt; has migrated the actual translation data that was previously in the &lt;em&gt;globalize_translations&lt;/em&gt; table to their respective places in the model tables.&lt;/p&gt;


	&lt;p&gt;Let’s have a look at the migration it created for us:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
class GlobalizeAddTranslatedFieldsForDummyEpiBlas &amp;lt; ActiveRecord::Migration
  def self.up
    #Fields for Dummy
    add_column :dummies, :name_es, :string
    add_column :dummies, :name_pl, :string
    #Fields for Epi
    add_column :epifanias, :name_es, :string
    add_column :epifanias, :surnames_es, :string
    add_column :epifanias, :name_pl, :string
    add_column :epifanias, :surnames_pl, :string
    #Fields for Blas
    add_column :blas, :hobby_es, :string
    add_column :blas, :hobby_pl, :string
  end

  def self.down
    #Fields for Dummy
    remove_column :dummies, :name_es
    remove_column :dummies, :name_pl
    #Fields for Epi
    remove_column :epifanias, :name_es
    remove_column :epifanias, :surnames_es
    remove_column :epifanias, :name_pl
    remove_column :epifanias, :surnames_pl
    #Fields for Blas
    remove_column :blas, :hobby_es
    remove_column :blas, :hobby_pl
  end
end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Also notice that the script is very wary and asks you at every stage for consent before any action. You can also decide to let it automatically migrate all models it has determined should be migrated.&lt;/p&gt;


	&lt;p&gt;And finally, it even allows you to remove the old translations from &lt;em&gt;globalize_translations&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;This script isn’t currently in trunk, as we’re testing it a bit but if you’d like to try it out you can get it by checking it out from &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/saimon/for-1.2/features"&gt;here&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;These tools should make the migration to the internal storage a lot less painful for you.&lt;/p&gt;


	&lt;p&gt;Well, that’s it for now.&lt;/p&gt;


	&lt;p&gt;My next article will cover &lt;a href="#"&gt;namespaced view translations&lt;/a&gt;&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/17/tools-for-migrating-to-internal-storage</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-17:1330</id>
    <published>2007-03-17T06:41:00Z</published>
    <updated>2007-03-21T12:18:21Z</updated>
    <category term="articles" />
    <category term="globalize" />
    <category term="i18n" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/DziTWpWbeQc/globalize-internal-storage-mechanism" rel="alternate" type="text/html" />
    <title>The Internal Storage Mechanism For Globalize for Rails Model Translations</title>
<content type="html">
            &lt;p&gt;The &lt;a href="globalize-rails.org"&gt;Globalize for Rails&lt;/a&gt; &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 release&lt;/a&gt; introduced a new storage mechanism for Model Translations.&lt;/p&gt;


	&lt;p&gt;If you’re new to Globalize, let me give you &lt;a href="#"&gt;a bit of background to model translations (Click to view)&lt;/a&gt; in Globalize. Those of you reading this in a feed reader and are familiar with globalize feel free to just read ahead to the &lt;a href="#before12"&gt;Before for-1.2&lt;/a&gt; section.&lt;/p&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href="#globalize-intro"&gt;Introduction To Model Translations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#before12"&gt;Before for-1.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#intro_internal"&gt;Introducing the “Internal Storage Mechanism”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#explore_internal"&gt;Exploring the “Internal Storage Mechanism”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#troublespots"&gt;Trouble spots?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;
&lt;h4&gt;&lt;a&gt;Intro to Model Translations for Globalize beginners&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;By &lt;a href="http://www.globalize-rails.org/globalize/"&gt;installing&lt;/a&gt; the Globalize plugin in your rails application you have the possibility of localising your ActiveRecord models.&lt;/p&gt;


	&lt;p&gt;e.g. by defining your class like this:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
class Product &amp;lt; ActiveRecord::Base

  translates :name

end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;the Product model will automatically and transparently save the value of the ‘name’ attribute distinctly depending on the current locale.&lt;/p&gt;


	&lt;p&gt;An example of what this means in code follows:&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;&lt;em&gt;Assumption&lt;/em&gt;&lt;/strong&gt;
Let’s assume that throughout this article our base &lt;a href="http://en.wikipedia.org/wiki/Locale"&gt;locale&lt;/a&gt; (By &lt;em&gt;base locale&lt;/em&gt; I mean the default locale/language from which content in other languages will be translated from) is ‘en-US’ and the following code has been executed (Usually this is added to your environment.rb file):&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
include Globalize                                   #Include the Globalize module to save on typing
Locale.set_base_language('en-US')
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Let’s explore what that code definition has done for us:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
$ script/console
Loading development environment
&amp;gt;&amp;gt;Locale.set('en-US')                                         #We set the current locale to en-US (English)
&amp;gt;&amp;gt;product = Product.create(:name =&amp;gt; 'Socks')                  #Create a new product
&amp;gt;&amp;gt;product.name                                                #Asking for the value of the 'name' attribute
=&amp;gt; "Socks"                                                    #has correctly shown 'Socks'

&amp;gt;&amp;gt;Locale.set('es-ES')                                         #We change the current locale to es-ES (Spanish)
&amp;gt;&amp;gt;product.reload                                              #Reload the product instance (Required when current locale changes)
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"                                                    #We're still getting 'Socks'
&amp;gt;&amp;gt; product.name = 'Calcetines'                                #In Spanish, 'Socks' are called 'Calcetines'
&amp;gt;&amp;gt; product.save                                               #Save the product instance
&amp;gt;&amp;gt; product.name
=&amp;gt; "Calcetines"                                               #We now get the correctly translated value

&amp;gt;&amp;gt;Locale.set('en-US')                                         #We change the current locale back to english
&amp;gt;&amp;gt;product.reload                                              #Reload the product instance
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"                                                    #We correctly get back 'Socks'

&amp;gt;&amp;gt;Locale.set('es-ES')                                         #Once more we change the current locale
&amp;gt;&amp;gt;product.reload                                              #Reload the product instance
&amp;gt;&amp;gt; product.name
=&amp;gt; "Calcetines"                                               #We correctly get back 'Calcetines'
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;So that little bit of code has afforded you quite some functionality. But what’s happening under the covers? With Globalized installed, it automatically overrides ActiveRecord::Base and ensures that when saving your product instance:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;if the currently active locale is the same as the base locale then the product data is saved as normal to the ‘products’ table&lt;/li&gt;
		&lt;li&gt;but if the currently active locale is any other locale, then globalize will ensure that the data for this locale is saved to an external table (globalize_translations).&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Then when you reload a model instance in a particular locale and ask it for the value of an attribute marked as being translatable, globalize is intelligent enough to correctly look for the data either in the products own table (‘products’) or in the external globalize_translations table.&lt;/p&gt;


The actual dynamics of this process is slightly different but you get the gist.
&lt;/div&gt;

&lt;h4&gt;&lt;a&gt;Before for-1.2&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;Before the &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 release&lt;/a&gt;, Globalize used an external table (&lt;em&gt;globalize_translations&lt;/em&gt;) to store translations.
To achieve this, it need to override the &lt;em&gt;ActiveRecord::Base.find_every&lt;/em&gt; method to rewrite the query sent to the database and coalesce the model table’s columns with the globalize_translations table.&lt;/p&gt;


	&lt;p&gt;This rewriting of the sql query implicitly sets up a number of limitations, the most obvious of which is that it disallows overriding the :select option to the &lt;em&gt;find&lt;/em&gt; method because in effect that’s exactly what globalize is already doing.&lt;/p&gt;


	&lt;p&gt;This leads to other limitations like disallowing the :include option as well. (Why? Same reason as before)&lt;/p&gt;


	&lt;p&gt;There are workarounds for most of these limitations (For example, you can replace :include with :include_translated)
especially if you’re writing your application from scratch.&lt;/p&gt;


	&lt;p&gt;But if you’re trying to globalize an existing application you’ve got to go through quite a number of hoops.&lt;/p&gt;


&lt;h4&gt;&lt;a&gt;Introducing the “Internal Storage Mechanism”&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;The new internal storage mechanism gets rid of these limitations. Because it stores the translations within the models own table, all the translations are loaded every time the instance is loaded. This means that there is no need to override the sql query, which consequently means you no longer have any limitation on &lt;em&gt;ActiveRecord::Base.find&lt;/em&gt;. Only a little bit of ruby magic is required to get things to work.&lt;/p&gt;


	&lt;p&gt;So let’s see an example of it in action:&lt;/p&gt;


	&lt;p&gt;If you read the intro to globalize, you’d have seen we used a Product model and assumed the base locale was set to ‘en-US’.&lt;/p&gt;


	&lt;p&gt;The only slight change you need to do to your ActiveRecord class definitions is switch on the internal storage mechanism like so:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
class Product &amp;lt; ActiveRecord::Base

  self.keep_translations_in_model = true
  translates :name

end
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Note: This use of “self.keep_translations_in_model = true” in the class definition allows activation of this mechanism on a per class level since the normal external storage mechanism is active by default.
However, you can also set the internal storage to be the default mechanism on an application-wide level by setting the following in your environment.rb:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
Globalize::DbTranslate.keep_translations_in_model = true
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;As this method depends on storing the translations within the  model’s own table we need to add in extra columns for all the attributes that are to be localised and one each for each locale to be supported in the application.&lt;/p&gt;


	&lt;p&gt;I’ve written a &lt;a href="#"&gt;tool&lt;/a&gt; to automate this step but for now we’ll take a more manual approach and code up a migration. In our example application, we’ll have support for both english and spanish.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
class AddLocalizedFieldsForProduct &amp;lt; ActiveRecord::Migration
  def self.up
    add_column :products, :name_es, :string
  end

  def self.down
    remove_column :products, :name_es
  end
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Notice, that since we’ve only a got a single attribute marked as translatable in our Product class definition and we’re only supporting a single extra locale (es-ES), we only need to add one extra column to the products table.&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
'attribute' + language suffix

e.g

'name' + 'es' =&amp;gt; 'name_es'
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;As you can see, the name of the new column is simply the original attribute name and a suffix which is simply the language part of the locale to be supported.&lt;/p&gt;


So let’s open up a console and see what we can do now that the internal storage mechanism is active.
&lt;pre&gt;
&lt;code&gt;
$ script/console
Loading development environment
&amp;gt;&amp;gt;Locale.set('en-US')                           #We set the current locale to en-US (English)
&amp;gt;&amp;gt;product = Product.create(:name =&amp;gt; 'Shoes')    #Create a new product
&amp;gt;&amp;gt;product.name                                  #Asking for the value of the 'name' attribute
=&amp;gt; "Socks"                                      #has correctly shown 'Shoes'

&amp;gt;&amp;gt;Locale.set('es-ES')                           #We change the current locale to es-ES (Spanish)

#Note: You no longer are required to reload the product instance on locale changes

&amp;gt;&amp;gt; product.name
=&amp;gt; nil                                          #By default, if there are no translations nil is returned
&amp;gt;&amp;gt; product.name = 'Zapatos'                     #In Spanish, 'Shoes' are called 'Zapatos'
&amp;gt;&amp;gt; product.save                                 #Save the product instance
&amp;gt;&amp;gt; product.name
=&amp;gt; "Zapatos"                                    #We now get the correctly translated value

&amp;gt;&amp;gt;Locale.set('en-US')                           #We change the current locale back to english
&amp;gt;&amp;gt; product.name
=&amp;gt; "Shoes"                                      #We correctly get back 'Shoes'

&amp;gt;&amp;gt;Locale.set('es-ES')                           #Once more we change the current locale
&amp;gt;&amp;gt; product.name
=&amp;gt; "Zapatos"                                    #We correctly get back 'Zapatos'
&lt;/code&gt;
&lt;/pre&gt;

&lt;h4&gt;&lt;a&gt;Exploring the “Internal Storage Mechanism”&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;So much for the simple example let’s see what else we can do:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
#Let's specify that the base locales content should be returned if untranslated
class Product &amp;lt; ActiveRecord::Base
  self.keep_translations_in_model = true
  translates :name, :base_as_default =&amp;gt; true

  belongs_to :sku
  belongs_to :price_list
  has_many :commerce_items
end

$ script/console
Loading development environment
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product = Product.find(:last)    #Simple query
&amp;gt;&amp;gt; product.name
=&amp;gt; "Shoes"
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Zapatos"

&amp;gt;&amp;gt;product2 = Product.create(:name =&amp;gt; 'Shirts')
&amp;gt;&amp;gt;product.name
=&amp;gt; "Shirts"
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Shirts"    #_base_as_default_ = true means untranslated attributes will return the base locale value
&amp;gt;&amp;gt; product.name = 'Camisas'
&amp;gt;&amp;gt; product.save
&amp;gt;&amp;gt; product.name
=&amp;gt; "Camisas"

#Now for a slightly more complex query
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; products = Product.find(:all, :select =&amp;gt; 'select distinct(name)', :include =&amp;gt; :items)
=&amp;gt; [&amp;lt;#Product :name =&amp;gt; 'Socks'&amp;gt;, &amp;lt;#Product :name =&amp;gt; 'Shoes' &amp;gt;]
&amp;gt;&amp;gt; products.first.name
=&amp;gt; "Socks"
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; products.first.name
=&amp;gt; "Calcetines"

#Now for some conditions

&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product = Product.find(:first, :conditions =&amp;gt; {:name =&amp;gt; 'Socks'})
=&amp;gt; &amp;lt;#Product :name =&amp;gt; 'Socks'&amp;gt;
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; product = Product.find(:first, :conditions =&amp;gt; {:name =&amp;gt; 'Calcetines})
=&amp;gt; nil    #Eh? What happened here?
&lt;/code&gt;
&lt;/pre&gt;

Let’s look at the query that got executed:
&lt;pre&gt;
&lt;code&gt;
select * from products where products.name = 'Calcetines';
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Remember that for the Spanish locale, the translated data has been stored in the &lt;em&gt;name_es&lt;/em&gt; column so obviously, the &lt;em&gt;find&lt;/em&gt; method wasn’t being clever enough to recognize that it should have generated:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
select * from products where products.name_es = 'Calcetines';
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;It would have been too complex to add functionality to make this seamless and would have ended up defeating the whole purpose of this method as it would most likely need to override the sql query generation mechanism and we’d be back to the same problem as the external storage mechanism has.&lt;/p&gt;


	&lt;p&gt;However, there’s a simple solution to this problem.&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; product = Product.find(:first, :conditions =&amp;gt; ["#{Product.localized_facet(:name)} = ?",'Calcetines'])
=&amp;gt; &amp;lt;#Product :name =&amp;gt; 'Calcetines'&amp;gt;
&amp;gt;&amp;gt; product.name
=&amp;gt; "Calcetines"
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;For simple condition statements like that we can go one better and use dynamic finders:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
&amp;gt;&amp;gt;Locale.set('es-ES')
&amp;gt;&amp;gt; product = Product.find_by_name('Calcetines')
=&amp;gt; &amp;lt;#Product :name =&amp;gt; 'Calcetines'&amp;gt;
&amp;gt;&amp;gt; product.name
=&amp;gt; "Calcetines"
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product = Product.find_by_name('Socks')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Activating the internal storage mechanism, overrides the AR &lt;em&gt;method_missing&lt;/em&gt; to ensure that dynamic finders correctly use the right column name for the current locale.&lt;/p&gt;


	&lt;p&gt;What other things can we do:&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product = Product.find_by_name('Socks')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Socks"
&amp;gt;&amp;gt;Locale.set('en-US')
&amp;gt;&amp;gt; product.name
=&amp;gt; "Calcetines"
&amp;gt;&amp;gt; product._name  #Using the '_' prefix to the attribute means you want the base locale content
=&amp;gt; "Socks"

&amp;gt;&amp;gt; product.name_before_type_cast
=&amp;gt; "Calcetines"
&amp;gt;&amp;gt; product._name_before_type_cast
=&amp;gt; "Socks"

&lt;/code&gt;
&lt;/pre&gt;

&lt;h4&gt;&lt;a&gt;Trouble spots?&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;Well, this all seems hunky dory, super fantastic, right? Right :)
However, there are a few things that you should keep in mind.&lt;/p&gt;


	&lt;h5&gt;Maintenance of the database schema&lt;/h5&gt;


	&lt;p&gt;In our simple example, we only had to add one extra column to our products table as we were only supporting one other language. But let’s move onto something more realistic. Imagine you’re developing a &lt;em&gt;“typical”&lt;/em&gt; web application with something like 10 models and which supports 6 different languages. And there an average of at least 5 translatable attributes per model. That works out to 300 extra fields in your tables. You can imagine the scope for errors here. Now imagine you’ve got a much larger application, schema evolution…extrapolate? :(&lt;/p&gt;


	&lt;p&gt;This is where the external mechanism’s advantage kicks in. No matter how many additional languages you want to support, or how your schema evolves, you never need to modify the schema to support these. With the internal mechanism, you have to be alert and ensure you add the right columns with the right suffix as your schema evolves.&lt;/p&gt;


	&lt;p&gt;Luckily two things go a long way to reduce the pain of this:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;ActiveRecord Migrations&lt;/li&gt;
		&lt;li&gt;the for-1.2 rake tasks that automatically generate a migration to add the required columns. (The subject of my next article)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h5&gt;Database row limitations&lt;/h5&gt;


	&lt;p&gt;The fact that you’re stuffing a table full of duplicated columns means that for some databases you may begin to reach certain limits, especially if those columns are blobs. e.g. MySQL InnoDB tables have a limit on row size that is easily reached by globalizing a table with about 4 varchar/text columns when you have to support 4 different languages. In this case, you need to switch to MyISAM tables which means you lose transactions (or you could switch your db to postgres or something like that).&lt;/p&gt;


	&lt;p&gt;So it’s important you keep these points in mind when deciding on which storage mechanism to use.&lt;/p&gt;


&lt;h4&gt;&lt;a&gt;Conclusion&lt;/a&gt;&lt;/h4&gt;

	&lt;p&gt;Phew…That’s was quite a long article. Anyway I may have a very heavy writing style and may tend to &lt;em&gt;overwrite&lt;/em&gt; but if anything I hope this has been a fairly enlightening walk through globalize’s new storage mechanism.&lt;/p&gt;


	&lt;p&gt;As I mentioned in this article, I’ve also written a couple of tools which should make migrating your existing globalized application to this system a breeze. Look out for the next article in this series where I’ll be writing a short tutorial on how to use these tools. It’ll be a lot shorter, I promise :)&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/17/globalize-internal-storage-mechanism</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-16:1329</id>
    <published>2007-03-16T11:20:00Z</published>
    <updated>2007-03-21T12:19:07Z</updated>
    <category term="articles" />
    <category term="globalize" />
    <category term="i18n" />
    <category term="rails" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/NLcaGWuJVRo/i-m-a-globalize-committer" rel="alternate" type="text/html" />
    <title>I'm a globalize committer</title>
<content type="html">
            &lt;p&gt;Thanks to the work I did on the &lt;a href="http://saimonmoore.net/2006/12/1/alternative-implementation-of-globalize-model-translations"&gt;alternative storage mechanism for globalize&lt;/a&gt; , &lt;a href="http://www.shnoo.gr"&gt;Josh Harvey&lt;/a&gt; invited me on to the &lt;a href="http://www.globalize-rails.org/globalize/"&gt;Globalize&lt;/a&gt; team.&lt;/p&gt;


	&lt;p&gt;I’m happy to announce that the Globalize team have recently released Globalize For-1.2 which provides full compatibility with Rails 1.2.x and introduces a number of new features some of which I wrote.&lt;/p&gt;


	&lt;p&gt;In the following series of articles I’m going to be writing about the work I’ve done on Globalize:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;My first task was to &lt;a href="http://saimonmoore.net/2007/3/17/globalize-internal-storage-mechanism"&gt;integrate my alternative storage plugin into globalize itself&lt;/a&gt;
(available in the &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 branch&lt;/a&gt; )&lt;/li&gt;
	&lt;/ul&gt;


	&lt;ul&gt;
	&lt;li&gt;I then kept myself busy by writing a few
&lt;a href="http://saimonmoore.net/2007/3/17/tools-for-migrating-to-internal-storage"&gt;tools to help the migration over to this new storage mechanism&lt;/a&gt;
(partly available in the &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 branch&lt;/a&gt; )&lt;/li&gt;
	&lt;/ul&gt;


	&lt;ul&gt;
	&lt;li&gt;I then introduced the concept of
&lt;a href="http://saimonmoore.net/2007/3/18/namespaced-view-translations"&gt;namespaced view translations&lt;/a&gt;
(available in the &lt;a href="http://svn.globalize-rails.org/svn/globalize/branches/for-1.2"&gt;for-1.2 branch&lt;/a&gt; )&lt;/li&gt;
	&lt;/ul&gt;


	&lt;ul&gt;
	&lt;li&gt;Recently I’ve added
&lt;a href="#"&gt;a class that encapsulates the idea of an application’s supported locales&lt;/a&gt; to globalize &lt;a href="http://svn.globalize-rails.org/svn/globalize/trunk"&gt;trunk&lt;/a&gt;.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;From this work and work done while developing &lt;a href="http://webtypes.com"&gt;our&lt;/a&gt; latest &lt;a href="http://amicsmuseuprecolombi.org"&gt;application&lt;/a&gt; I’ve also recently released a
&lt;a href="#"&gt;plugin that Globalizes Mephisto (http://mephistoblog.com)&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;P.S. (Shameless plug) If anyone out there would like help &lt;em&gt;globalizing&lt;/em&gt; their site/application then &lt;a href="http://webtypes.com"&gt;we’re available&lt;/a&gt; for &lt;a href="http://saimonmoore.net/assets/2006/10/12/cv.html"&gt;hire&lt;/a&gt; right now&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/16/i-m-a-globalize-committer</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-03-16:1328</id>
    <published>2007-03-16T09:50:00Z</published>
    <updated>2007-03-21T12:20:11Z</updated>
    <category term="articles" />
    <category term="rails" />
    <category term="ruby" />
    <category term="sysadmin" />
    <category term="webtypes" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/77xaxMWb91g/introducing-capistrano-server-extensions" rel="alternate" type="text/html" />
    <title>Introducing Capistrano Server Extensions</title>
<content type="html">
            &lt;p&gt;I’ve just released a cool tool :) lamely called &lt;a href="http://rubyforge.org/projects/capserverext"&gt;Capistrano Server Extensions&lt;/a&gt;. It’s a &lt;a href="http://manuals.rubyonrails.com/read/book/17"&gt;Capistrano&lt;/a&gt;  extension based on the &lt;a href="http://deprec.rubyforge.org/"&gt;deprec&lt;/a&gt; gem and allows you to very easily setup full rails stacks on Ubuntu servers using any combination of &lt;a href="http://sysoev.ru/en"&gt;Nginx&lt;/a&gt; / &lt;a href="http://httpd.apache.org"&gt;Apache&lt;/a&gt; / &lt;a href="http://www.mysql.com"&gt;MySQL&lt;/a&gt; or &lt;a href="http://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;e.g.&lt;/p&gt;


&lt;pre&gt;
&lt;code&gt;
  $ cap prepare_ssh
  $ cap prepare_host
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;That’s it (more or less :)&lt;/p&gt;


	&lt;p&gt;For the full article read &lt;a href="http://www.webtypes.com/2007/03/15/announcing-capistrano-server-extensions"&gt;Announcing Capistrano Server Extensions&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;I should take this opportunity to also announce that along with super fantastic &lt;a href="http://www.webtypes.com/"&gt;Thomas Maas&lt;/a&gt; I’m a &lt;strong&gt;&lt;a href="http://webtypes.com"&gt;Web Type&lt;/a&gt;&lt;/strong&gt; too. So I’ll be writing articles on both these sites.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/3/16/introducing-capistrano-server-extensions</feedburner:origLink></entry>
  <entry xml:base="http://www.saimonmoore.net/">
    <author>
      <name>saimon</name>
    </author>
    <id>tag:www.saimonmoore.net,2007-01-19:913</id>
    <published>2007-01-19T14:06:00Z</published>
    <updated>2007-03-21T12:21:01Z</updated>
    <category term="articles" />
    <link href="http://feedproxy.google.com/~r/saimonmoore/~3/XRGjp6FbrNk/dotime-doing-well" rel="alternate" type="text/html" />
    <title>Dotime doing well</title>
<content type="html">
            &lt;p&gt;Out of curiosity to see how many other people were interested in DoTime, I took a look the &lt;a href="http://gems.rubyforge.org/stats.html"&gt;gem stats&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;I was surprised to see that dotime has been downloaded a total of &lt;strong&gt;50&lt;/strong&gt; times.&lt;/p&gt;


	&lt;p&gt;That makes me happy. I’m going to fix some more bugs soon and make another alpha release. I also have to check wether basecamp has updated their api much lately and see if I can optimize it a bit more.&lt;/p&gt;
          </content>  <feedburner:origLink>http://www.saimonmoore.net/2007/1/19/dotime-doing-well</feedburner:origLink></entry>
</feed>
