Caching Translations in Node.js

In our previous post we built a simple Node.js application that uses ExpressJS engine with EJS templates and Bootstrap CSS for the user interface. We added Tml-JS-Express library and translated the app to a few languages.

In this post we will see how we can cache and share translations across all our Node servers, how we can group translations into sources and how we can version and release translations using the Translation Exchange service.

Caching

The way we setup our application in the previous post required direct access to the Translation Exchange API to get the most recent translations. Although, this solution works in development and translation modes, is it not the right approach for staging and production. In production environment, translations must be cached to achieve the best performance of your applications. Our SDK offers many ways of how that can be done.

TML SDK for Node.js offers two main cache categories: Dynamic Cache and Static Cache.

Dynamic Cache

Dynamic cache can be redeployed/upgraded without restarting your application. For instance, you can deploy a new language or release updated translations without ever restarting your servers. The two dynamic cache adapters that come with our library are Memcached and Redis, but it is very easy to add adapters to any other external cache storage. In our future post we will show you how to create a new dynamic cache adapter for Couchbase database.

It is also important to note about memory utilization with dynamic cache. Only translations that are needed to render a specific page are retrieved from the Cache and used in the application. If your app is fairly large and you use multiple servers, this type of caching will significantly reduce the memory size needed for each process running your application.

Static Cache

Static cache requires exporting resource files from Translation Exchange, adding them to your application and restarting your servers. The static resource files are loaded into memory once application starts and can not be changed unless you repeat the process. You will get a slightly better performance by loading all translations into memory, but that would come at a cost of memory consumption and inability to deploy translations and languages without redeploying your production servers with new resources.

If you ever decide to stop using Translation Exchange service, you can simply export all your translations to your local static cache and serve them completely independently from the service.

Let’s see how dynamic cache can be configured with our sample application. We will use Memcache as our cache server.

First we need to install memcached module and add it to our package file.

[shell title=”Shell”]
$ npm i –save memcached
[/shell]

Let’s open app.js and indicate that we want to use memcache as a cache adapter:

[js title=”app.js”]
app.use(tml.init(“ff08ec9c583a98d002f23e0bad46ca24f103fb54b87517d28c56791233618a4c”, {
cache: {
adapter: ‘memcache’,
hosts: [“localhost:11211”],
namespace: “my_app_translations”
}
}));
[/js]

If you have more than one memcache server, you can list them all under the hosts attribute. Namespace is a useful way to group translations cache together.

Let’s restart our application and see what happens.

node_sample_translated

You are now serving your own translations from cache. On the first page load our SDK looks at your memcache and checks if translations are available for the sources your were loading. Since it doesn’t find any translations, it loads the translations from our API and stores them back in your memcache server.

We will show you how you can invalidate, publish and rollback translations in the last section of this post. Also, keep in mind, when you enable in-context translations – the SDK will always retrieve translations from the live API and not use cache. This is done to ensure that translators can access the most recent translations and work on unpublished languages.

Sources

Sources are logical groups of translation keys that serve a couple of purposes. One one hand they provide a mechanism to group translations into logical units that can be managed and monitored independently. On the other hand, they are used for caching data in your local cache.

By default, every page is registered a separate source. We typically take the url, strip it down from all parameters and host and use the rest to create a source. All translation keys that are loaded during the page rendering will automatically be added to the page source. As you go through your application, notice that we create a source for every page you load. You can specify how you want to name sources, and you can create any number of sub-sources. Sources can be nested. But we currently only support 2 levels of nesting.

Let’s group some of our translation keys together into sources. For instance, most likely our entire navigation bar will be reused across multiple pages, so instead of making every page contain all navigation elements, we will group all navigation items under the “navigation” source. This will ensure that if you ever add or change any items under the navigation source, only the cache for navigation source will be invalidated and re-pulled from our service. All pages containing the navigation source will not need to be invalidated. We can group keys together using the following syntax:

[html title=”views/index.ejs” mark=”2,25″]

[/html]

Let’s enable translation mode by pressing Ctrl+Shift+I – we should see all strings highlighted based on their status.

node_sample_untranslated

View your dashboard and see that a new source called “navigation” has been registered as well.

dashboard_sources

Disable translation mode by pressing Ctrl+Shift+I. Now navigation strings will be cached in their own source called “navigation”.

The sources are purely logical, and you can create any number of sources. When you export translation cache to file system, sources will be stored in their own files. Source names should be all lower case and preferably not contain any spaces. You can use “/” to create a directory structure. For example, “common/navigation” will create a file “navigation.json” under “common” folder.

Releases

Now that we have enabled caching for our application, and translated it to a number of languages, we want to publish and release the latest translations. We first publish the latest translations into a release and then we let our application know that the latest translations are available. The second steps notifies your local cache that it needs to reload translation from our service.

To publish the latest translations, visit the “Releases” section in your dashboard and click on “Publish” button.

publish_translations

The release process takes a snapshot of all your translations and pushes them to our global Content Delivery Network (CDN). Our CDN is available in 40 data centers around the world.

Dynamic Cache Release

We now need to let our application know about new release. There are a number of ways to do so. One way is that our SDK offers an optional hook under: “YOUR_APP/tml/upgrade?access_token=YOUR_TOKEN”. This URL can only be called with your application’s access token. If token is not provided, the URL will resolve to 404 error. If you choose to use our hook, we can automatically call it when release on our side is completed. Alternatively, you can invoke it yourself. Or you can call, “Tml.cache.reset_version”, from anywhere within your application. On your next page load, your local dynamic cache will be rebuilt from our CDN.

Static Cache Release

If you use static cache, you can download the entire release from our service, expand it in your application’s folder, and point your file cache to the folder. For instance, you can create a folder called “cache/translations”, then you point the “File” cache adapter to that folder:

[js title=”app.js”]
app.use(tml.init(“ff08ec9c583a98d002f23e0bad46ca24f103fb54b87517d28c56791233618a4c”, {
cache: {
adapter: ‘file’,
path: “cache/translations/{CACHE_VERSION}”
}
}));
[/js]

This requires you adding the latest translations to your SCM, releasing them and deploying your application. Once deployed the translations from static cache will be used.

Get Started Today!

Create an account to get started now! No credit card required.

Get Started