Just compare regulare rails view internationalization:
<html>
<body>
<% current_year = Time.now.year %>
<span><%= t('hello') %></span>
<h2><%= t('Dashboard') </h2>
<div><%= t('Posts count:') %><%= current_user.posts.count %></div>
<div><%= t('Click') %><a href="#"><%= t('here') %></a></div>
...
</body>
</html>
with i18n template internationalization:
<html>
<body>
<% current_year = Time.now.year %>
<span i18n="p">hello</span>
<h2>Dashboard</h2>
<div>Posts count: <%= current_user.posts.count %></div>
<div>Click<a href="#">here</a></div>
...
</body>
</html>
Nice?
It convert on the fly regular erb template to another erb template. For above example this is something like:
<html>
<body>
<% current_year = Time.now.year %>
<span>
<%- i18n_variables = {}; i18n_wrappers = [] -%>
<%= ::I18nTemplate::Translation.translate("hello", i18n_wrappers, i18n_variables) %>
</span>
<h2>
<%- i18n_variables = {}; i18n_wrappers = [] -%>
<%= ::I18nTemplate::Translation.translate("Dashboard", i18n_wrappers, i18n_variables) %>
</h2>
<div>
<%- i18n_variables = {}; i18n_wrappers = [] -%>
<%- i18n_variables['current user posts count'] = capture do -%>
<%= current_user.posts.count %>
<%- end -%>
<%= ::I18nTemplate::Translation.translate("Posts count: {current user posts count}",
i18n_wrappers, i18n_variables) %>
</div>
<div>
<%- i18n_variables = {}; i18n_wrappers = [] -%>
<%- i18n_wrappers[1] = capture do -%>
<a href="#" i18n_wrapper="1">
<%- i18n_variables = {}; i18n_wrappers = [] -%>
<%= ::I18nTemplate::Translation.translate("here", i18n_wrappers, i18n_variables) %>
</a>
<%- end -%>
<%= ::I18nTemplate::Translation.translate("Click[1]here[/1]", i18n_wrappers, i18n_variables) %>
</div>
</body>
</html>
Translation phrases (keys):
- hello
- Dashboard
- Posts count: {current user posts count}
- Click[1]here[/1]
I18nTemplate is made to extract phrases and translate html/xhtml/xml document or erb templates. Currently the it can work with (x)html documents. Translation is done by modify the original template (on the fly) to be translated on erb execution time.
The engine is leveraging the HTML document semantics. As we know HTML document element can contain : block elements and/or inline elements. The engine has the following parsing rules, based on what kind of children a parent element contains:
- block element containing only block elements - is named a parent element, and is ignored by the engine;
- block element containing only inline elements - is named phrase, while every inline element is named a word;
- inline element containing other inline elements - is also a word;
- any other variation - is considered a broken element, which should be one of there above.
Additionally for the sake of best practices and optimiztion the following rules take place:
- the following elements as considered block elements by the engine
- usual block :
blockquote p div h1 h2 h3 h4 h5 h6 li dd dt
- inline elements :
td th a legend label title caption option optgroup button
- the following elements, and their content, will be ignored by the engine:
- html elements:
select style script
- non-breaking space:
- erb scriptlets:
<% <%=
- html comments:
<!-- -->
- xhtml doctype:
<!DOCTYPE
- additional best practices are added to translate content inside such tags
In order to fix a broken elements next elements/attributes can be added to the html document to resolve engine misunderstanding:
<i18n>content</i18n>
- mark invisible for parser content for internationalization<... i18n="i" ...>content<...>
- (ignore) ignore element content internationalization<... i18n="p" ...>content<...>
- (phrase) explicitly enable content internationalization<... i18n="s" ...>content<...>
- (subphrase) mark element content as sub-phrase for parent element phrase
[1]Hello World[/1]
Example
Hello { user name }
<%= @user_name %>
as{user name}
<%= user_name %>
as{user name}
<%= @post.comments.count %>
as{post comments count}
$ gem install i18n_template
require 'i18n_template'
ActionView::Template.register_template_handler(:erb, I18nTemplate::Handler.new)
I18nTemplate.phrase_translator = lambda { |phrase| Google.translate(phrase) }
Assume we don't want to internationalize admin view templates.
class MyI18nTemplateHandler < I18nTemplate::Handler
def internationalize?(template)
if template.respond_to?(:path)
path =~ /^admin/ ? false : true
else
true
end
end
end
ActionView::Template.register_template_handler(:erb, MyI18nTemplateHandler.new)
$ rvm alias create rails23_r187 ruby-1.8.7
$ rvm alias create rails30_r193 ruby-1.9.3
$ rvm alias create rails31_r193 ruby-1.9.3
$ gem install multiversion
$ multiversion all bundle install
Against all versions:
$ multiversion all exec testrb test/*_test.rb
Against specific versions:
$ multiversion rails30_r193,rails31_r193 exec testrb test/*_test.rb
$ i18n_template --help
extract_phrases - extract phrases for translations
--format plain|gettext|yaml translation format (default gettext)
--po-root PO ROOT root directly for po files (default po)
--glob GLOB template files glob (default app/views/**/*.{erb,rhtml})
--textdomain TEXTDOMAIN gettext textdomain (default phrases)
--output-file FILE output file (default template_phrases.txt)
--locales-root DIRECTORY locales directory (default config/locales)
$ i18n_template extract_phrases --format plain --output-file /tmp/phrases.txt
$ i18n_template extract_phrases --format yaml
$ cat config/locales/phrases.yml
en:
Hello {user name}, {message}:
'[1]First name[/1] : {profile first name}':
'[1]Last name[/1] : {profile last name}':
'[1]Email[/1] : {account email}':
Copyright {current year}. All rights reserved.:
$ i18n_template extract_phrases \
--textdomain myapp \
--glob app/views/**/*.erb \
--glob lib/view/**/*.erb
$ tree --dirsfirst po
po
├── de
│ └── myapp.po
└── myapp.pot
$ cat po/phrases.pot
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2011-11-28 15:38+0200\n"
"PO-Revision-Date: 2011-11-25 21:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
# app/views/_footer.html.erb
msgid "Copyright {current year}. All rights reserved."
msgstr ""
# app/views/greeting.html.erb
msgid "Hello {user name}, {message}"
msgstr ""