Recently airbnb posted cool article on
hammerspace
. Following line got my attention:
Fetching all translations for a page at once is not straightforward, since the strings required to render a page
are not known ahead of time
.
I have battled similar problem in past, and even though it is true that we cannot know them all upfront, we can, however register and encode them during runtime, and then load them all at once and decode individually.
I have played with it for few hours in an proof of concept rails app (
https://github.com/assembler/bulk18n
).
The application overrides rails
translate
(
t
) helper to return encoded version of translation and store its key and arguments in memory. For example
t('greeting', user: 'User 1')
would store the translation request at index 0 in registry and save optional parameters as well. It returns an object with following string representation:
\u0002
0
\u0003
. It just have two invisible utf chars and translation index in between (in this case
0
).
Given this template:
<p><%= t 'greeting', user: 'User 1' %></p>
<p><%= t 'greeting', user: 'User 2' %></p>
It will produce this:
<p>\u00020\u0003</p>
<p>\u00021\u0003</p>
Next, it processes entire
response.body
(in
around_action
filter) and just decodes these values to proper translations. At this point we can have
ALL
the keys that we need to translate, and we create the dictionary by doing
SINGLE
query to our preferred data store. Then we can simply decode them all using that dictionary.
Since we’re also storing additional options passed (:user in this example), we’re able to do the interpolation. The end result is as expected:
<p>Hello User 1</p>
<p>Hello User 2</p>
There is one final problem to tackle. What if we want to perform additional string manipulations of translated values. For example, we want to upcase translated string:
<p><%= t('greeting', user: 'User 1').upcase %></p>
To solve the problem, returned value from
t
method is really not a string. It is an object which is represented as a string, but captures all
method_missing
calls and saves them to be replayed later on. Kinda like
null object
but the one which memorizes everything and takes revenge later :)
Feel free to check out the test app at
https://github.com/assembler/bulk18n.
Its just proof of concept app used to demonstrate an idea. I think that this concept has potential in other areas as well (such as
cache personalization in rails
i wrote about earlier).