What's the best practice for internationalizing, say, a Terms of Service document in Rails 3? I can think of two options:
Creating a partial for each locale and choosing which one to load according to the current user's locale.
<li><%= I18n.t :tos_paragraph_1 %></li><li><%= I18n.t :tos_paragraph_2 %></li>
None of these seems like a good solution. Any ideas?
There are a few solutions, but if I were doing this for a production project, I would probably do something like the following:
Create files for your translated Terms of Services in config/locales/terms/, naming them terms.en.html, replacing en with the locale for each translation and html with the format of the file (e.g. you could use Haml, Markdown, etc.).
Create the following helper methods (put them in app/helpers/application_helper.rb to use them everywhere, but you can put them in whatever helper file you need/want):
def localized_document_for(document, locale)
raise ArgumentError.new('nil is not a valid document') if document.nil?
raise I18n::InvalidLocale.new('nil is not a valid locale') if locale.nil?
localized_document = path_for_localized_document(document, locale)
raise MissingTranslationData unless File.exists?(localized_document)
# If you're using Markdown, etc. replace with code to parse/format your document
File.open(localized_document).readlines.join
end
def path_for_localized_document(document, locale)
"#{Rails.root}/config/locales/#{document}/#{document}.#{locale.to_s}.html"
end
Now, in your views, you can use localized_document_for('terms', I18n.locale) any time you need to get the contents of the Terms of Service in the user's language. Now the code you're using to fetch the document is DRY (you can easily fetch other documents by creating another directory in config/locales and changing the value of the document argument), and your translated documents are stored in their own directory and can easily be edited (and don't depend on YAML, which brings no value to storing a single document in a file).
Note that if you wanted to do it the "Rails 3 Way," you could use the I18n::Backend::Chain (see https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/chain.rb), and pass in I18n::Backend::Simple.new along with a custom backend that reads the files as necessary, but for a one-off deal I believe the helpers work sufficiently.
Creating a partial for each language is definitly undry.
The Rails way is the second option you suggest with a .yml for each language.
The doc provides great examples to let you organize the yml according to each page. Thus you have shortcuts for all your variables.
See http://guides.rubyonrails.org/i18n.html
Related
I have to translate the majority of an application I am working on to Dutch. I created a du.yml file and am formatting it in the same way my en.yml file is formatted (to be sure):
en:
Hello: "Hello World"
When outputting the en.yml the application populates the text properly. However when I load the du.yml it gives me the translation not found span wrap around the desired translated material.
The steps I have taken to set this up are as follows -
added to application_controller.rb:
before_filter :set_locale
def set_locale
I18n.locale = params[:lang] if params[:lang].present?
end
This allows me to pass a param in the query-string to determine what language to use.
I then did as shown up top and added values to be translated on both the en.yml and du.yml files
I then call the values from the respective files with (for example) <%= t :hello %>. When I set english as the param (?lang=en), everything works great. However, when I put ?lang=du, I get what I mentioned before - the dreaded span tag of missing translation.
Any ideas on what I may have done wrong? Thanks!
Oh yeah..and both files are in the same directory (config/locales)
Learned that, although rails development allows you to make changes on the fly, file additions require a server re-load. Thanks to house9 for the clarification.
My goal is to generate a directory of static html, javascript, and image files within my Rails (3) app, driven by ERB templates. For example, as a developer I might want to generate/update these files:
#{Rails.root}/public/products/baseball.html
#{Rails.root}/public/products/football.js
..from the following template files:
#{Rails.root}/product_templates/baseball.html.erb
#{Rails.root}/product_templates/football.js.erb
Ideally the templates would have access to my app's Rails environment (including URL helpers, view helpers, partials, etc.).
What's the latest and greatest way to accomplish this?
I experimented with a custom Rails generator, but found that I needed to write custom logic for skipping non-ERB files, substituting file names, etc. There must be a better way.
I'm not sure what you are trying to do exactly, that may help provide better answers, but here is some useful information:
You can call into erb directly, some information on that is here, which have probably already been doing:
http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
For the list of template files an easy Dir.glob should be able to help find the specific files easily and loop through them:
http://ruby-doc.org/core/classes/Dir.html#M000629
The tricky part I wouldn't know how to advise you on is getting access to the helpers and other things Rails provides. The helpers that you write are just modules, so you could mix those in, something similar might be possible with the built-in rails helpers.
This is interesting and related but doesn't directly answer your question, since its uses the Liquid templating engine instead of ERB, but otherwise, it does some of the static site generation you are talking about:
https://github.com/mojombo/jekyll
This is how I accomplished something similar. It accepts source and destination directories, wipes out the destination, then processes the source directory, either ERB-processing files and placing them in the destination or simply copying them (in the case of on-ERB files). It would need to be modified to handle recursively processing a directory.
I invoke it from a rake task like so:
DirectoryGenerator.new.generate(Rails.root.join('src'), Rails.root.join('public', 'dest'))
class DirectoryGenerator
include Rails.application.routes.url_helpers
include ActionView::Helpers::TagHelper
default_url_options[:host] = 'www.example.com'
def generate(source, destination)
FileUtils.rmtree(destination)
FileUtils.mkdir_p(destination)
Dir.glob(File.join(source, '*')).each do |path|
pathname = Pathname.new(path)
if pathname.extname == '.erb'
File.open(destination.join(pathname.basename.sub(/\.erb$/, '')), 'w') do |file|
file.puts(ERB.new(File.read(path)).result(binding))
end
else
FileUtils.cp(pathname, File.join(destination, pathname.basename))
end
end
end
end
Have you looked into Rails templates?
http://m.onkey.org/rails-templates for instance..
Not sure what you are getting at exactly.. are you trying to generate client sites by providing a few parameters.. that the end goal?
I'm find such a gem a whole day, but not find a good one. I want to write one, but I'm not able to do it.
The data in my database may be English text, which will be dump to a yml file with plain text. And some are non-English text, which will be binary type.
And both of them may have such code:
<% xxx %>
When I use rake db:fixtures:load to load them into database, error may occur: method xxx not found.
I wan't to find a good gem can handle this problem. Thank for any help
UPDATE
I have gave up finding such a gem. At first, I think it's a easy task, but now, after some researchings, I have to say, it's much harder than I expected.
The reason you are having problems is because the Fixture loader will pass your fixture through erb before loading the data. This means that if you have <% xxx %> in your yaml file then Rails will see that as erb and try to run a method called xxx.
There does not seem to be an easy way to turn off erb processing of fixtures. I have tried replacing the fixtures with CSV fixtures and this still does ERB pre-processing.
Without a simple answer I have to ask the question Why do you have these strings in your file?
Do you want them to be expanded by erb?
Err...I'm not sure if you actually need a gem for this? Rails natively can turn any model into YAML.
Let's say you have a model called "Objects". You could hit a route that looks like:
/objects.yaml
and you would get a giant text file of all your Objects in YAML form.
Of course, you would want to have something like:
respond_to do |format|
format.yaml {render :yaml => #objects}
end
in your restful controller.
If you'd rather not hit a route to do this, you can always do
#yaml = []
#objects.each do |object|
#yaml.push object.to_yaml
end
anywhere in ruby, which will give you an array of yaml objects, that you can then write to a file at your leisure.
I imagine that if rails itself is generating the yaml, then it would be able to then later load it as a fixture?
Hoping some learned Rails developers here can recommend an existing Ruby on Rails plugin or gem that allows you to continue using the Simple I18n backend whilst allowing you to optionally specify translations in the database.
Here's why:
I have one Rails app used for many websites. For the example I'll just use 2 websites:
Website 1: Leprechauns R Us
Website 2: Unicorns R Us
Most translations are the same for both websites, but occassionally I want to override a translation. For example, in my en-US.yml file I have the following translation:
view_all: View all
And for most websites this translation is fine, including for website 1 (Leprechauns) where I'm happy to use "View all".
However, for website 2, I'd like to use "View all Unicorns" as the view_all translation and I'd like to specify this in the database. For maintenance reasons I don't want to specify this override in a YAML file.
Many thanks,
Eliot
In the end I opted to take advantage of Rails' I18n::Backend::Simple's ability to process both .yml files and .rb files as locale dictionaries.
Artifacts created:
DB migration to create a translations table with columns: locale, key, text
Translation model to map to the translations table
Class method to_locale_hash on Translation model that returns a locale keyed hash as required by I18n::Backend::Simple.load_rb
A single-line file located at config/translations.rb with the line 'Translation.to_locale_hash'
For the source code see the Spree extension (sorry not in a Rails plugin structure, it will be easy to move over to a plugin should you require it) here:
http://github.com/eliotsykes/spree-i18n-db/tree/master
I have never worked with web services and rails, and obviously this is something I need to learn.
I have chosen to use hpricot because it looks great.
Anyway, _why's been nice enough to provide the following example on the hpricot website:
#!ruby
require 'hpricot'
require 'open-uri'
# load the RedHanded home page
doc = Hpricot(open("http://redhanded.hobix.com/index.html"))
# change the CSS class on links
(doc/"span.entryPermalink").set("class", "newLinks")
# remove the sidebar
(doc/"#sidebar").remove
# print the altered HTML
puts doc
Which looks simple, elegant, and easy peasey.
Works great in Ruby, but my question is: How do I break this up in rails?
I experimented with adding this all to a single controller, but couldn't think of the best way to call it in a view.
So if you were parsing an XML file from a web API and printing it in nice clean HTML with Hpricot, how would you break up the activity over the models, views, and controllers, and what would you put where?
Model, model, model, model, model. Skinny controllers, simple views.
The RedHandedHomePage model does the parsing on initialization, then call 'def render' in the controller, set output to an instance variable, and print that in a view.
I'd probably go for a REST approach and have resources that represent the different entities within the XML file being consumed. Do you have a specific example of the XML that you can give?