Posterous theme by Cory Watilo

Multilingual site on Rails

Задавался давно уже вопросом о том, как сделать многоязычный сайт на рельсах. Сначала нехватало знаний, потом времени. Недавно понадобилось таки реализовать такой сайт. Сразу скажу, получилось. Первым делом задумался над переводом в шаблонах. Благо, тут вариантов было много и я выбрал simple-localization. С рельсами 2.1 работает версия 3.0. Но она ставится экспортом из svn: [cc lang="bash"] cd vendor/plugins svn co http://svn.arkanis.de/projects/rails_plugins/simple_localization/branches/3.0 simple_localization [/cc] Теперь можно вставлять конструкции l(:domain, :key), которые из файла перевода из раздела app: будут брать домен и ключ, указанные в методе. Кстати, я вынес файлы перевода в директорию translations в корень проекта. Также в перевод я добавил массив с датами в родительном падеже (:declinable_monthnames). Подключается плагин в config/initializers/simple_localization.rb строками: [cc lang="ruby"] SimpleLocalization.init :languages => [:ru, :en, :uk], :lang_file_dir => "#{RAILS_ROOT}/translations" class Date silence_warnings do DECLINABLE_MONTHNAMES = ArkanisDevelopment::SimpleLocalization::ProxyObject.new :dates, :declinable_monthnames do |localized_data| [nil] + localized_data end end end [/cc] Язык я категорически отказался хранить в сессии - это несерьезно. Так невозможно дать ссылку товарищу и быть уверенным, что он увидит о же что и вы. В роутах реализовал так: [cc lang="ruby"] ActionController::Routing::Routes.draw do |map| map.with_options :path_prefix => '/:lang' do |lang| lang.connect 'news', :controller=>"news", :action=>"index" lang.connect 'news/:id', :controller=>"news", :action=>"show" lang.connect '', :controller=>"pages", :action=>"index" lang.connect 'feedback', :controller=>"pages", :action=>"feedback" lang.connect '*link', :controller=>"pages", :action=>"show" end map.root :controller => "pages", :action=>"index" end [/cc] В app/controllers/application.rb добавил парочку методов и фильтр: [cc lang="ruby"] class ApplicationController show_path }.update(options.symbolize_keys) escape = options.key?(:escape) ? options.delete(:escape) : true url = @controller.send(:url_for, options) when String escape = true url = options when NilClass url = @controller.send(:url_for, nil) else escape = false url = polymorphic_path(options) end url = escape ? escape_once(url) : url if url =~ /\/.+/ and url !~ /^\/(#{Localization.loaded_languages.join('|')})(\/.+)?$/ url = "/#{Localization.used.to_s}#{url}" end url end def link_to(name, options = {}, html_options = nil) url = case options when String self.url_for options when :back @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else self.url_for(options) end if html_options html_options = html_options.stringify_keys href = html_options['href'] convert_options_to_javascript!(html_options, url) tag_options = tag_options(html_options) else tag_options = nil end href_attr = "href=\"#{url}\"" unless href "#{name || url}" end end end end [/cc] Я знаю что это жутко некрасиво, но я не знаю как изменить в методе всего одну строку. Можно было создать свой хелпер, но хотелось переопределить существующие, чтобы все было по возможности прозрачно. Теперь дело дошло и до хранения локализованого контента в БД. Вот как у меня устроена таблица Pages: [cc lang="ruby"] class CreatePages true do |t| t.string :lang, :title, :link, :null=>false t.text :body t.boolean :textilize, :default=>1, :null=>false t.timestamps end add_index :pages, [:link, :lang], :unique=>true add_index :pages, [:link] end def self.down drop_table :pages end end [/cc] а в app/models/page.rb имеем следующее: [cc lang="ruby"] class Page {:lang=>Localization.used.to_s} end [/cc] Так можно оперировать локализоваными страницами как Page.localized. Вроде бы из интересного все, остальное рутина. Если у кого есть советы как что дополнить/измениь - с радостью выслушаю.