Translate-plugin: Translating vue.js data

Created on 11 Jul 2020  路  8Comments  路  Source: rainlab/translate-plugin

Hey, does anyone know if it is possible to translate data added to html from vue.js script file? So for exmaple if I have in my vue.js the following:
const app= new Vue({ el: '.main__page', data: { season: "summer", } })
and then in html: <h1 v-model="season"></h1>
how would I be able to translate the "season" variable?
I am sure, I have seen it somewhere, but can't find it anymore.
Thanks.

Question

Most helpful comment

@bennothommo @th-ink here's something I did up on a project a while ago.
In Plugin.php boot():

    // Clear the /author-plugin/translation-messages cache whenever a message updates
    Message::extend(function ($model) {
      $model->bindEvent('model.afterSave', function () {
        Cache::forget('author.plugin.translation-messages');
      });
    });

in routes.php

use RainLab\Translate\Classes\Translator;
use RainLab\Translate\Models\MessageExport;
use RainLab\Translate\Models\Locale as LocaleModel;

Route::get('/author-plugin/translation-messages', function () {
  // Store the generated data in the cache, cache will be manually emptied whenever a message updates
  // Cache::forget('author.plugin.translation-messages');
  $cacheData = Cache::rememberForever('author.plugin.translation-messages', function () {
    $translator = Translator::instance();
    $availableLocales = array_keys(LocaleModel::listEnabled());
    $activeLocale = $translator->getLocale();
    $fallbackLocale = $translator->getDefaultLocale();

    // NOTE: The actual message code is stored under "x" in the message data, the code column is
    // used in the database and turns any symbols into ".", which would turn the key account.create_header
    // into account.create.header. Thus we must request the "x" column and use it as the code
    $messageData = (new MessageExport())->exportData(MessageExport::getColumns() + ['x']);

    $messagesByLocale = [];
    foreach ($messageData as $message) {
      foreach ($availableLocales as $locale) {
        if (!empty($message[$locale])) {
          array_set($messagesByLocale, $locale . '.' . $message['x'], $message[$locale]);
        }
      }
    }

    $responseData = [
      'locale' => $activeLocale,
      'fallbackLocale' => $fallbackLocale,
      'messages' => $messagesByLocale,
    ];

    return [
      'data' => base64_encode(json_encode($responseData)),
      'etag' => md5($activeLocale . $fallbackLocale . implode('|', array_dot($messagesByLocale))),
      'last_modified' => now()->timestamp,
    ];
  });

  // Set the proper caching headers on the messages
  $response = Response::make();
  $response->header('Content-Type', 'application/json');
  $response->header('Cache-Control', 'private, max-age=604800');
  $response->setLastModified((new DateTime())->setTimestamp($cacheData['last_modified']));
  $response->setEtag($cacheData['etag']);
  $response->setPublic();
  $modified = !$response->isNotModified(App::make('request'));
  if ($modified) {
    $response->setContent(base64_decode($cacheData['data']));
  }

  return $response;
});

in Vue initialization:

import Vue from 'vue';
import VueI18n from 'vue-i18n';

window.myVueComponent.initComponent = function (vueParams) {
  $.getJSON('/author-plugin/translation-messages').done(function (data) {
    vueParams.i18n = new VueI18n(data || {});
  }).always(function () {
    new Vue(vueParams);
  });
}

All 8 comments

where does your vue.js data get generated?

in a script.js file that is load via html <script> tag.

You could provide the translation strings using a dynamically generated js file which could be used by your Vue js code

Okay, that sounds good. Is there an example somewhere with such a file?

@th-ink I would suggest looking at a library like Vue I18n which takes a messages variable, and either doing what @mjauvin suggested and dynamically generating a messages file and using that to populate the messages variable, or creating a October plugin to provide an API to get the messages dynamically. Unfortunately, I don't have any examples to show you, but my company is trying out such a method at the moment, and if it works easy enough, I'll see if I can make an example of it somewhere.

@bennothommo @th-ink here's something I did up on a project a while ago.
In Plugin.php boot():

    // Clear the /author-plugin/translation-messages cache whenever a message updates
    Message::extend(function ($model) {
      $model->bindEvent('model.afterSave', function () {
        Cache::forget('author.plugin.translation-messages');
      });
    });

in routes.php

use RainLab\Translate\Classes\Translator;
use RainLab\Translate\Models\MessageExport;
use RainLab\Translate\Models\Locale as LocaleModel;

Route::get('/author-plugin/translation-messages', function () {
  // Store the generated data in the cache, cache will be manually emptied whenever a message updates
  // Cache::forget('author.plugin.translation-messages');
  $cacheData = Cache::rememberForever('author.plugin.translation-messages', function () {
    $translator = Translator::instance();
    $availableLocales = array_keys(LocaleModel::listEnabled());
    $activeLocale = $translator->getLocale();
    $fallbackLocale = $translator->getDefaultLocale();

    // NOTE: The actual message code is stored under "x" in the message data, the code column is
    // used in the database and turns any symbols into ".", which would turn the key account.create_header
    // into account.create.header. Thus we must request the "x" column and use it as the code
    $messageData = (new MessageExport())->exportData(MessageExport::getColumns() + ['x']);

    $messagesByLocale = [];
    foreach ($messageData as $message) {
      foreach ($availableLocales as $locale) {
        if (!empty($message[$locale])) {
          array_set($messagesByLocale, $locale . '.' . $message['x'], $message[$locale]);
        }
      }
    }

    $responseData = [
      'locale' => $activeLocale,
      'fallbackLocale' => $fallbackLocale,
      'messages' => $messagesByLocale,
    ];

    return [
      'data' => base64_encode(json_encode($responseData)),
      'etag' => md5($activeLocale . $fallbackLocale . implode('|', array_dot($messagesByLocale))),
      'last_modified' => now()->timestamp,
    ];
  });

  // Set the proper caching headers on the messages
  $response = Response::make();
  $response->header('Content-Type', 'application/json');
  $response->header('Cache-Control', 'private, max-age=604800');
  $response->setLastModified((new DateTime())->setTimestamp($cacheData['last_modified']));
  $response->setEtag($cacheData['etag']);
  $response->setPublic();
  $modified = !$response->isNotModified(App::make('request'));
  if ($modified) {
    $response->setContent(base64_decode($cacheData['data']));
  }

  return $response;
});

in Vue initialization:

import Vue from 'vue';
import VueI18n from 'vue-i18n';

window.myVueComponent.initComponent = function (vueParams) {
  $.getJSON('/author-plugin/translation-messages').done(function (data) {
    vueParams.i18n = new VueI18n(data || {});
  }).always(function () {
    new Vue(vueParams);
  });
}

Technically speaking we could probably add a JSON endpoint to this plugin to get all site translations.

Thanks @bennothommo and @LukeTowers, I will try it out.

Was this page helpful?
0 / 5 - 0 ratings