Is it possible to use symfony translations internally in vue js?

Viewed 1254

I have Symfony 4.4 application with localization on different languages translations/*.yml.

For example in translations/messages.en.yml

site:
    name: My site

So in my twig templates, I can use

{{ 'site.name'|trans }}

I started to use Vue js for my front (with WebBack and Symfony Encore). I know that Vue js has its own internationalization in JSON format. But I don't want to duplicate my translation twice...

Question
Is it possible use Symfony translations in some way in Vue JS? Something like that...

<template>
    <h2>My Vue template</h2>
    {{ 'site.name|trans }}
</template>

Question2 Is it possible to use x variable inside of vue template ?


    /**
     * @Route("", name="homepage")
     *
     * @return Response
     */
    function homepage()
    {
        return $this->render(
            'index.html.twig', [
                'x' => 'test',
            ]
        );
    }

Vue template

<template>
    <h2>My Vue template</h2>
    {{ x }}
</template>

Appreciate any advice

2 Answers

Getting Symfony translations into JS is a bit tricky. Theoretically you could get the catalog as a template variable and then assign it to a JS-variable, but with a large catalog that will be quite a lot of overhead, plus you will expose your translation labels, which might leak unwanted infos about variables, but for smaller projects I find this to be the easiest solution:

# Controller
public function index(TranslatorInterface $translator)
{
    return $this->render('index.html.twig', [
        'translation_catalog' => $translator->getCatalogue($locale),
    ]);
}

The method getCatalogue() is not part of the interface, but is provided by Symfony's translator. You might have to add a type hint for your IDE to recognize it.

# twig-template
{% block javascripts %}
    <script type="text/javascript">
        const translations = {{ translation_catalog|json_encode }}
    </script>
    {{ parent() }}
{% endblock %}

The {{ translations|json_encode }} will take your catalogue and transform it into a JSON-object. You should then be able to access translations in vue by accessing this object.

Another solution could be, to create an API endpoint which gives you the translations for a key. I find this suboptimal as you will perform lots of HTTP requests for translations which is bad for performance and you will still have the downside of exposing your translation labels with each API call. I would strongly advise against this approach.

A better solution would be to have your translations in a format that both Symfony's translation component and JS/vue will understand. Then you can simply reuse the translation files.

In modern Symfony (4.2+) this should be easier as the translator supports ICU format, which is also supported by JS libs, e.g. Format.js (there might be vue integrations for this library out there, but I am not familiar with them, so I can't recommend any). This allows you to directly import your translation files in JS, bypassing your Symfony application, but still use the same files as Symfony. Your build tool can then be used to only keep those keys you actually need in your JS code or you generate static files for each language and not rely on the original, static translation files at all during runtime.

well, we've done this. but as @dbrumann stated, it's really tricky, but it works.

  1. create a route/controller, in which you dump translations via getCatalogue into json
  2. in vue, store the json response of this controller into session storage. issue this request only when session storage variable does not exist.
  3. hook into vue i18n to fetch translations from the local storage variable
  4. profit

there are some caveats though, which you need to resolve in some way:

  1. updating translations. if you request them only when session storage variable is not present, then they don't get updated. so, apply some sort of lifetime or versioning mechanism.
  2. translations key + translation domain. you need to transform these in that dumping controller into something like domain.flattened.translation.key

but it is more effective than including them into a js var in every response.

Related