Interpret
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.
Installation
Add the gem to your Gemfile:
gem 'interpret'
Then you must run the interpret setup generator in order to copy some asset files required for the backoffice section, javascripts, css's and a couple of images:
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
Development considerations
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 en.yml
file
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
modifications inside .yml
files, not to update the contents but to update
the keys.
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
elements <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
The update
action is the core of Interpret. It performs a synchronization
between your *.yml
files and the I18n backend translations, and it is
expected to run after a deployment in order to update your production
translations.
It will create any translation that exists in
.yml
files 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
.yml
files. Note that you can prevent Interpret to remove anything setting thesoft
option described at the bottom of this document.For any key that exists in both
.yml
files and database backend it will not do anything.
Main language
The 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.
Built-in Backoffice
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 scope
,
see later):
http://localhost:3000/interpret
Overview
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 can edit your translations using best_in_place, such amazing in-place edition tool by bernat.
Tools
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
.yml
locale files. It's described in an earlier section of this readme.Dump: Dump the contents of your
.yml
locale 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!
Search
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.
Configuration
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.
Registered environments
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 Admin::BaseController
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.
Custom layouts
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 layouts/backoffice.html.<wathever>
layout.
If you want further customizations, you can edit the css file Interpret use,
it's in 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"
Routes scope
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.
Authentication
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
If the 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.
Roles
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
are available:
Editor
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
protected
translation.
Admin
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
current_user
option.Also set an
admin
option, to discriminate which users are interpret admins.Use the following helper in your main layout (or all layouts your application use):
= interpret_live_edition
You should use it at the bottom of your body
block.
- Set the
Interpret.live_edit
variable 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.
Caching
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
Interpret::ExpirationObserver
class.
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.
Rake tasks
Interpret comes with two rake tasks, which are simply interfaces to run the
same update
and dump
actions you can run from the Tools section of the
backoffice.
rake interpret:update
rake interpret:dump
The update
task is what you may want to run after a deployment.
Soft behavior
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
with:
Interpret.configure do |config|
config.soft = true
# ...
end
When
soft
is 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.yml
locale file Interpret will remove that translation from the I18n backend when you run anupdate
.When
soft
is 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.yml
file. 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 .yml
files, 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.
Logger
Updating, removing or creating a translation will result in a new entry in the
log file 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.
Final notes
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!