I am building a multi lingual website, using ruby on rails, where part of the content is supposed to be user generated and they are supposed to be able to create different versions of it for all languages. The language support is handled by i18n gem.
Part of their content is created using Markdown through http://daringfireball.net/projects/markdown/basics .
In my database I save: object.content_markdown_en, object.content_html_en, object.content_markdown_sv, object.content_html_sv and so on for the different locales.
Now if a user changes the content, new html is supposed to be generated. But it seems unnecessary to regenerate the html for all locales if he only made changes in one of the languages.
I thought there might be some way to use something like
if object.content_markdown_[locale]_changed?
generate_new_html
end
that can be run in a loop for all possible locales. But I can't find any nice ways of doing this.
How about:
[:en, :sv].each do |locale|
if object.send("content_markdown_#{ locale }_changed?".to_sym)
send("generate_new_#{ locale }_html".to_sym)
end
end
You can use send to call methods by name:
object.send("content_markdown_#{locale}_changed?".to_sym)
Your loop would look like this:
%w(en sv).each { |locale|
if object.send("content_markdown_#{locale}_changed?".to_sym)
generate_new_html
end
}
However, using a separate translation table might be a better approach.
Related
I have added humanized-money-accessors as described here: Decimals and commas when entering a number into a Ruby on Rails form
Now I have two attributes in my model for the same type of data: the original version and the human-readable version. The problem: Since I am using activerecord-translation-yml-files, I have to put in the same translation for original attribute and the humanized_attribute, because my forms show the name of thie humanized_attribute, but on validation errors, the name of the original attribute is shown.
Is it possible to add translations dynamically? This way I could add the translation for the humanized-version of the field when the humanized_accessor-class-method is called, getting the original translation string from the yml file, instead of writing both of them (with the same value) into the yml-file, just to have more DRY.
This is dependent on the I18n gem's internal API not changing but it is possible using I18n.backend.store_translations.
This contrived example demonstrates:
I18n.with_locale(:fake_locale) { I18n.t('some_word') }
=> "translation missing: fake_locale.some_word"
I18n.backend.store_translations(:fake_locale, some_word: 'fake translation')
I18n.with_locale(:fake_locale) { I18n.t('some_word') }
=> "fake translation"
Important: This is only done in memory. Some persistence or re-generation mechanism is necessary to prevent these from disappearing when you redeploy/restart the server.
You might want to check out globalize3 gem. You have railscast tutorial http://railscasts.com/episodes/338-globalize3?view=asciicast.
I purchased a {wrap}bootstrap theme. In the process of moving the assets into my Rails 2.3.x app (don't laugh), I notice there are many places in the views and helpers where some data object is accessed. For instance:
def active(id)
if data.page.menukey == id
return 'active'
end
return ''
end
Where is this data object defined? Is it just a clumsy placeholder for people implementing the theme to replace with their own objects?
The data object you see in {wrap}bootstrap themes comes from MiddleMan "local data". MiddleMan is not required to implement a {wrap}bootstrap theme. You may wish to search and replace every instance of data with something meaningful to your application.
I've written a little library to hold translated content in model attributes. All you have to do is add the following to a model:
class Page < ActiveRecord::Base
i18n_attributes :title, :content
end
By convention, the data is written to the real attributes i18n_title and i18n_content as a hash (or hstore hash for Postgres). And a number of getters and setters give you access to "localized virtual attributes":
page = Page.new
page.title_en = 'Hello'
page.title_es = 'Hola'
page.i18n_title # => { en: "Hello", es: "Hola" }
I18n.locale = :es
page.title # => "Hola"
page.title_en # => "Hello"
You can use these virtual attributes in forms as well, but there's a downside: The form builder uses I18n to get the label and translate attribute validation errors. And it does of course look for keys such as activerecord.attributes.page.title_en if you use title_enin the form.
It would be very cumbersome to replicate the same translation for every available_locale in the locales/en.yml etc files:
activerecord:
attributes:
page:
title_en: "Title"
title_es: "Title"
...
What I'd like to do is execute some code after Rails has loaded all locales in the boot process and then clone translations for these keys. Is there a way to do this? Maybe a hook which gets called after the translations have been loaded from the YAML files? (The translations are not yet loaded when my lib loads.)
Or do you see another way to tackle this problem? (I've tried to alias I18n.translate, but I'm afraid this might cause major headache in the future.)
Thanks for your hints!
Although you dropped this approach, please let me share my thoughts:
I don't think it is incredible useful to add other locale strings into a translation file for a specific localization. Since a config/locales/$locale.yml usually starts (at least in my case) with
$locale:
...
there is no need for activerecord.attributes.page.title_es in an English localization file. I'd just put it in es.yml as activerecord.attributes.page.title.
I mean: isn't that the whole purpose of separate localization files? (Or from the developer/translator point of view: In which file should I search for .title_es, in en.yml, es.yml or both?)
I want to give my pages human-readable slugs, but Rails' built-in parameterize method isn't SEO-optimized. For example, if I have a post called "Notorious B.I.G. is the best", parameterize will give me this path:
/posts/notorious-b-i-g-is-the-best
which is suboptimal since Google construes the query "Notorious B.I.G." as "Notorious BIG" instead of "Notorious B I G" (i.e., the dots are removed rather than treated as spaces)
Likewise, "Tom's fave pizza" is converted to "tom-s-fave-pizza", when it should be "toms-fave-pizza" (since Google ignores apostrophe's as well)
To create a better parameterize, I need to know which characters Google removes from queries (so I can remove them from my URLs) and which characters Google treats as spaces (so I can convert them to dashes in my URLs).
Better still, does such a parameterize method exist?
(Besides stringex, which I think tries to be too clever. 2 representative problem cases:
[Dev]> "Notorious B.I.G. is the best".to_url
=> "notorious-b-dot-i-g-is-the-best"
[Dev]> "No, Curren$y is the best".to_url
=> "no-curren$y-is-the-best"
I would try using a gem that has been designed for generating slugs. They often make good design decisions and they have a way of updating the code for changing best practices. This document represents Google's best practices on URL design.
Here is a list of the best gems for solving this problem. They are sorted by rank which is computed based on development activity and how many people "watch" changes to the gems source code.
The top one right now is frendly_id and it looks like it will generate good slugs for your use in SEO. Here is a link to the features of the gem. You can also configure it and it looks like it is perfect for your needs.
Google appears to have good results for both the "b-i-g" and "big" in the url slugs.
For the rails side of things, yes a parameterize method exists.
"Notorious B.I.G. is the best".parameterize
=> "notorious-b-i-g-is-the-best"
I think you can create the URLs yourself... something like
class Album
before_create :set_permalink
def set_permalink
self.permalink = name.parameterize
end
def to_params
"#{id}-#{permalink}"
end
end
This will create a url structure of:
/albums/3453-notorious-b-i-g-is-the-best
You can remove the id section in to_params if you want to.
Use the title tag and description meta tag to tell google what the page is called: these carry more weight than the url. So, leave your url as /posts/notorious-b-i-g-is-the-best but put "Notorious B.I.G. is the best" in your title tag.
I know about the built-in I18n in Rails, but how can I select a database field per locale?
My model structure is something like this:
title #default (englisch)
title_de #(german)
title_it #(italian)
In my template I want to be able to write only
<%= #model.title %>
and should get the value in the right language.
Is there a plugin or a solution to use different fields per different locale settings with a structure like mine?
Although your db architecture (different locales hardcoded as table columns) seems wrong to me, I think you can achieve what you want by adding a pseudo-field to your model, something along:
# example not tested
class MyModel < ActiveRecord::Base
def localized_title(locale)
locale = locale == 'en' ? '' : '_' + locale
read_attribute("title#{locale}".to_sym")
end
end
Or, provided that you somehow make your current locale visible to your models, you can similarly overwrite the default title accessor method.
Edit: You can take a look at http://github.com/iain/translatable_columns, it seems pretty much compatible with your architecture....
Try using:
http://github.com/joshmh/globalize2
It may require renaming your columns (to a different standard).
Nowadays the best way to translate active record models fields is using https://github.com/shioyama/mobility