Search code examples
phpsymfonytwig

How to pass translations with keys and values from Twig to a JavaScript object?


I'm trying to pass translation strings as key-value pairs through a Twig template without having to assign them again one by one.

I have my translations in a file translations/messages.fr.yaml. Example:

entity:
  foo: some translation
  bar: another translation
  third:
    title: one more

Currently I pass translations through the Twig template in a JS code block, using the 'trans' filter:

const translations = {
  foo: "{{'entity.foo'|trans}}",
  bar: "{{'entity.bar'|trans}}",
  third: {
    title: "{{'entity.third.title'|trans}}"
  }
}

Is there a way how to pass all properties of 'entity' directly? Could I access the translations as an object and pass it into the variable via JSON (as a Twig variable or, if necessary, echoing something with <?php ... ?>)?

I'm aware that I could use AJAX but I am trying to pass the data in the template.


Solution

  • I solved it with a function getTranslations that retrieves the catalogue and merges it with the fallback catalogue. Then it calls the recursive function addToArray to create a multi-dimensional array from the dot-separated key segments:

    function addToArray(array $keySegments, array|string $translations, string $translation) : array|string 
    {
        if (count($keySegments) === 0) {
            return $translation;
        }
    
        if (!is_array($translations)) {
            // syntax error in yaml file - a dot inside a key name
            return $translations;
        }
    
        $keySegment = array_shift($keySegments);
        if (!isset($translations[$keySegment])) {
            $translations[$keySegment] = [];
        }
    
        $translations[$keySegment] = addToArray($keySegments, $translations[$keySegment], $translation);
    
        return $translations;
    }
    
    
    function getTranslations(Symfony\Contracts\Translation\TranslatorInterface $translator) : array
    {
        // replace 'messages' with the required domain
        $catalogue = array_merge(
            $translator->getCatalogue()->getFallbackCatalogue()->all('messages'),
            $translator->getCatalogue()->all('messages')
        );
        $translations = [];
        foreach ($catalogue as $key => $translation) {
            $keySegments = explode('.', $key);
            $translations = array_merge($translations, addToArray($keySegments, $translations, $translation));
        }
    
        return $translations;
    }
    

    In the twig template I use the output in a script block:

    const translations = JSON.parse(`{{translations|json_encode|raw}}`);