Interpret is a rails 3 engine to help you manage your application translations, also allowing you to have editable contents in live. In order to do so it will register the I18n activerecord backend to be used for your application. Interpret is pretty tied to it at the moment, but there are plans to make it backend-agnostic. We believe that key-value stores as I18n backends are pretty awesome and we want to support them, although the activerecord backend with Memoize and Flatten is very fast too, once you have loaded all the data.
Interpret is intented to allow live edition of your contents, making it very easy for your client, your co-workers or yourself to edit them without a deployment.
Caching techniques play a key role here in order to expire the page or the fragment in which a certain content is displayed, and Interpret helps you do this work with an observer, but you are the responsible to expire your caches. See later on caching section.
If you want you can also use Interpret only as a translation tool, like
Tolk in which this gem was initially inspired.
See later the
registered envs section on how to avoid the registration of
I18n activerecord backend. This way you can still edit your translations as
before, but in this case the modifications you make won't be directly
available in your application.
Add the gem to your Gemfile:
rails g interpret:setup
Finally you should also run this generator to create the 'translations' table required by the I18n active-record backend:
rails g interpret:migration
If you have chosen to have dynamic contents, that is editable text in your
website, this means that all this text information is now stored in some
database. They no longer belongs to the application itself, they're now seed
data, but instead of create it inside
seeds.rb you edit an
or something similar. This is the work flow Interpret expects you to follow,
develop your application using the standard
.yml locale files and put in
there anything you need. Later, after a deployment, run the
update rake task
to perform a synchronization between the production I18n database and your
.yml files, not to update the contents but to update
The tool is different but the concept is the same. The contents you see and
edit in development ARE NOT the same contents you will see in production,
because they're dynamic and someone else may have changed them. However, the
page architecture does belongs to the application, I mean, the actual HTML
code you wrote. One
<h1>, three paragraphs
<p> and a list
<ul> with five
<li>. This markup is there and it expects to have some text in it,
translated content, and it have to be there. So, that said, it's clear that
you can edit the contents, but not create or remove them.
The Update action
update action is the core of Interpret. It performs a synchronization
*.yml files and the I18n backend translations, and it is
expected to run after a deployment in order to update your production
It will create any translation that exists in
.ymlfiles but not in the database backend. When doing so, the value of the translation in yaml files are preserved and copied into the database backend. The same happens if you have created it in more than one language, it is copied for each locale.
It will remove any translation that exists in the database backend but not in the
.ymlfiles. Note that you can prevent Interpret to remove anything setting the
softoption described at the bottom of this document.
For any key that exists in both
.ymlfiles and database backend it will not do anything.
I18n.default_locale configured in your application will be the master
language Interpret will use. Keep in mind that rails lets you have a diferent
locale key hierarchy for each language in the
.yml files, and this behaviour
is prohibited in Interpret. Here, the
I18n.default_locale is the only
language that can be trusted to have all the required and correct locale keys
and it will be used to check for inconsitent translations into other
languages, knowing what you haven't translated yet.
This is also the locale used when an
update action is performed. The
synchronization will only check for inconsistent keys between
.yml files and
database backend within that master language.
As an Engine, Interpret provides you with a set of backoffice views in order
to manage your translations, and to perform some operations with them. You can
access it to the following path in your app (unless you define some
This view shows all the translations organized by their keys, in a tree structure as if they were folders and files. If you're used to the typical filesystem architecture it's pretty simple.
Here you have some tools you can use to work with the translations:
Export: Clicking the download link you will get a typical rails locale file for the current language. It's generated with ya2yaml so it may be safe to use with utf8.
Import: With the upload option you can select a locale file from your computer and it will be used to perform a massive translations update. To be precise, for each translation you have in that file it will either:
- Update that translation if it already exists, or
- Create that translation if not
Update: This action will perform an update from your
.ymllocale files. It's described in an earlier section of this readme.
Dump: Dump the contents of your
.ymllocale files into I18n backend. All contents will be overwritten, so be cautious!
All of these operations can be very expensive if you have a large number of translations, some optimization work is still required!
You can search by translation key or value, or both of them. The results will be shown in the same way as in Overview, so you can also edit them from there.
To configure Interpret create an initializer file and put in there something like this:
Interpret.configure do |config| config.parent_controller = "admin/base_controller" # Some other configuration options end
The following sections describe in detail all the configuration options available.
Interpret is intended to be used along with I18n active-record backend in
order to provide live edition capabilities for your translations. It will
automatically register the I18n.backend to the active-record one, with Memoize
and Flatten, if the current Rails environment is included in the
registered_envs list. By default i's initialized to the following:
Interpret.registered_envs = [:production, :staging]
You can override it in order to activate it also in development:
Interpret.configure do |config| config.registered_envs << :development # ... end
Or to disable it if you only want to use Interpret as a translation tool:
Interpret.configure do |config| config.registered_envs =  # ... end
Adding authorization and custom filters
If you want to add some authorization control over Interpret backoffice, or
any custom filters, you can use the
Interpret.parent_controller option. This
will make all the Interpret controllers to inherit from it, so you can check
for user authentication or whatever:
Interpret.configure do |config| config.parent_controller = "admin/base_controller" # ... end
For instance, the above code will make Interpret use
as a base class for all their controllers, and you can put in there any
before_filter you want to check for the current logged in user permissions.
It's likely you already have some controller like this to act as a base for
all your existing admin or backoffice controllers.
In order to integrate the Interpret views into your existing backoffice, you can define your own layout to be used by Interpret:
Interpret.configure do |config| config.layout = "backoffice" # ... end
Then Interpret will use the
If you want further customizations, you can edit the css file Interpret use,
public/stylesheets/interpret_style.css. Be aware that this file will
be overwritten the next time you run a
rails g interpret:setup.
For now there is no generator to copy all the views into your app, but you can do it yourself by-hand if you want to also customize them.
Remember to load the Interpret stylesheet if you use your own layout:
= stylesheet_link_tag "interpret_style"
You can make Interpret build their routes inside a scope of your choice:
Interpret.configure do |config| config.scope = "(:locale)" # ... end
The above code for instance will produce better looking urls inside interpret, as the current locale will be a prefix of the route instead of a GET parameter.
You can allow Interpret to know who is the current logged in user by setting the following:
Interpret.configure do |config| config.current_user = "current_user" # ... end
Interpret.current_user option is setted, Interpret will use it in
their controllers and views to retrieve the current user, and log their name
(or whatever string returned by calling
to_s on it) into the log messages
every time a translation is modified.
Once you have configured a
current_user function, Interpret can work with
two different roles. Use the following configuration option:
Interpret.configure do |config| config.current_user = "current_user" config.admin = "interpret_admin?" # ... end
In this example, Interpret will call
current_user.interpret_admin? to know
if the current logged in user is an interpret admin or not. Depending on the
result of this call Interpret allow more or less functionalities. If you
don't set any
admin method, all users will be admins inside Interpret. The
same happens if you don't set the
current_user option. The following roles
When the result is evaluated to false, the user is considered an editor. This role is for an user who is intended to make translations, but no to administrate the site.
- It will be able to edit translations.
- It won't be able to use any of the Tools.
- It won't be able to modify any
When the result is evaluated to true, then the user is considered an admin, so it can:
- Do everything described in the Built-in Backoffice section.
- Mark some translations as
protected, which means only editable by admins. This can be used to prevent non-technical people to mess up with interpolated translations and things like this.
Live translation edition
This feature will let you edit your translations and contents directly from your application views. This way the edition work is much more user-friendly, since you're changing what your are seeing. To do so, you will need to do two things:
Let Interpret know about who is logged in, setting the
Also set an
adminoption, to discriminate which users are interpret admins.
Use the following helper in your main layout (or all layouts your application use):
You should use it at the bottom of your
Interpret.live_editvariable to true, to enable live edition.
From there, if the current logged in user is an admin, he will be able to
translate contents in live. Note that this is NOT per user, it's a global
setting. Also note that only
admins can use it. We know about this
limitations and we will improve this functionality in the future for sure.
You also need to take care about caching, obviously this will not work with cached views.
Interpret register the I18n activerecord backend with Flatten and Memoize, so
it takes care to reload the I18n backend every time a translation is edited,
created or destroyed. Unfortunately I18n only provides a global method
.reload! to expire the cache, so we can't be more precise about what exactly
translation we want to expire, without patching I18n itself at least.
Besides that, if you're using any kind of caching technique you should use the following:
Interpret.configure do |config| config.sweeper = "my_sweeper" # ... end
Using the above code you tell Interpret to register the
MySweeper class as
an observer to
Interpret::Translation, so you will be able to run expiration
logic when a translation changes. With this, you sweeper is the entirely
responsible to expire caching, and it's responsible to run an
I18n.backend.reload! too, unless you inherit from the given
If you want some help with that, the recommended way to run custom expiration logic is to build your sweeper class like the following:
class MySweeper < Interpret::ExpirationObserver def expire_cache(key) # run your expiration logic end end
One parameter will be passed to your
expire_cache method, a string
containing the key of the affected translation. It's your business to find
out which page or fragment you have to expire from here.
Also take note that your sweeper class is in fact an observer, not a
Rails sweeper. I've initially implemented this using real sweepers, but I
simply don't like the idea to bind the expiration logic to the controllers.
And Interpret can't afford it since it needs to expire the cache from a rake
task, for example, to run an
update after a deployment.
So, you won't be able to use the default expire methods rails provides you, since they are only available from within a controller. You will need to find out a more raw way to expire your cache.
Interpret comes with two rake tasks, which are simply interfaces to run the
dump actions you can run from the Tools section of the
rake interpret:update rake interpret:dump
update task is what you may want to run after a deployment.
Using this option you choose between give a full control to Interpret over
the I18n translations or not. It defaults to
false and you can change it
Interpret.configure do |config| config.soft = true # ... end
softis set to false: Then Interpret is the owner of all I18n translations, in the sense that it hasn't to be worried about creating or deleting translations. This way, if you remove a key from the
.ymllocale file Interpret will remove that translation from the I18n backend when you run an
softis set to true: Then Interpret will be more cautious with your translations, and won't remove any of them even though if you have removed the key in the
.ymlfile. This is intented to be used when you have a situation where your I18n translations are used by some other means. Initially I've implemented this to make Interpret compatible with Armot, a tool for handle model translations directly with I18n activerecord backend.
In this case, if some translation exists in the I18n backend but not in
.ymlfiles, Interpret has no way to know if it's because you have removed them or because it's a translation handled outside Interpret. So, you will end up with unused translations in your database.
Updating, removing or creating a translation will result in a new entry in the
log/interpret.log. The user who made the modification will be also
registered in the log entry, if
current_user is available.
This can be used as a sort-of backup system, to restore the old contents of a certain translation. It won't be very difficult to write some script to do this, but by now it's not included in Interpret.
Thanks to NodeThirtyThree for their website templates released under CreativeCommons 3.0 license, one of which is used here.
This piece of software is on a very early stage of development, so use it at your own risk!