Simplify translations

While working at REDACTED, I reported the following issue.

We develop in Spain an application in English for Saudi Arabia, how is it that we don’t already have easy to use translations?

Our translations are cumbersome to manage. To add one you have to:

  1. create a hierarchical key like some-module.some-part.some-section.some-title
  2. add its translation in code like this.title = this.translateService.instant('some-module.some-part.some-section.some-title');
  3. open a terminal window at the app main directory, then issue $ npm run translation.export
  4. open a browser, then navigate to a third party website and authenticate
  5. search some-module.some-part.some-section.some-title
  6. select your key in the results (beware that approximate results are shown too)
  7. edit its translation in a dialog box
  8. save the transaltion
  9. open a terminal window at the app main directory, then issue: $ npm run translation.import
  10. reload the page to show the translated title

Why can’t I just add the translated key to a translations file? I guess because the programmers that started developing the app were required to use that third party website for allowing professional translators to do their job. That is a reasonable requirement but there is no need for programmers to continually export and import translations, simply to make them appear on the page they are working on. Translations could automatically be exported and imported at any later time, like when merging changes into the development branch.

It wouldn’t be difficult to write an extraction script to allow coding like this:

// .../src/some-module/some-part/some-section.translations.ts
export const translations = {
    "some-title": "..."
};

// .../src/some-module/some-part/some-section.ts
this.title = x('some-title');

There is a problem with extraction scripts, though. They are static text analyzers that read code and extract some.key from expressions like instant('some.key'). Thus they can’t extract interpolated keys like instant(`${some}.key`), where some is a string variable whose value will be set later. For example, if some = 'another', then ${some}.key would be another.key.

Interpolated keys are very useful for compressing many code lines into one cleaner expression. For example, this code

let translation;
switch (some) {
    case 'some':
        translation = x('some.key');
        break;
    case 'another':
        translation = x('another.key');
        break;
    case 'yet.another':
        translation = x('yet.another.key');
        break;
    default: // programmer error
        throw new Error(`Unexpected value for 'some' variable (got '${some}')`);
}

can be compressed to this one liner:

const translation = x(`${some}.key`);

While it’s true that we can’t have both the independence from run time (static analyzer) and the flexibility of interpolations (dynamic analyzer) in the same tool, nonetheless we can easily have both benefits with a static analyzer and a bit of overhead. In fact, all we need to do is to declare all those interpolated keys in a file that only needs to exist. (no need to use it anywhere)

// interpolated-translation-keys.ts
x('some.key');
x('another.key');
x('yet.another.key');

With a file like that, the static analyzer would find it, eat it, and spit out translatable keys, which could eventually be exported. At the same time, the code would work perfectly with interpolated translation keys.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.