JBlond / twig-trans

Twig 3 translation extension

Home Page:https://github.com/JBlond/twig-trans

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

trans filter definition

vukbgit opened this issue · comments

Hello, I have this environment: PHP 8.2, Twig 3.5.1, jblond/twig-trans 1.1.0

Filter was defined as shown into README:

$filter = new TwigFilter(
    'trans', 
    function ($context, $string) {
        return Translation::transGetText($string, $context);
    }, 
    ['needs_context' => true]
);

Templates were generated into cache (by a script that once worked without problems) but:

  • trans tags worked correctly (rendered as echo gettext("text-to-be-translated");)
  • trans filters were instead rendered into cache template as $this->env->getFilter('trans')->getCallable()("text-to-be-translated");

Consequently .po files generated by xgettext did not show text to be translated with the trans filter

After digging into Twig code I came up wit the following solution:

$filter = new TwigFilter(
  'trans', 
  'gettext',
  [ 'is_safe_callback' => 'twig_escape_filter_is_safe']
);

Maybe this helps someone else stuck in the same situation
Cheers!

commented

With that code you no longer use this library, but gettext directly.

@vukbgit Can you provide a short example?

In the example code and the template it works

ok, here is a test:

  • environment:
    • PHP 8.2.3
    • Twig 3.5.1
    • jblond/twig-trans 1.1.0
  • script: test-trans.php (see attached files): pasted from jblond/twig-trans/example/extract.php, I just added a constant needed by a package of mine
<?php

use jblond\TwigTrans\Extract;
use jblond\TwigTrans\Translation;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFilter;

require './private/share/packagist/autoload.php';

//local constant needed by and autoloaded function
define('ENVIRONMENT', 'development');

error_reporting(E_ALL);
ini_set('display_errors', 'On');

$langCode = 'de_DE';
putenv("LC_ALL=$langCode.UTF-8");
if (setlocale(LC_ALL, "$langCode.UTF-8") === false) {
    echo sprintf('Language Code %s not found', $langCode);
}

// set the path to the translation
bindtextdomain("Web_Content", "./locale");

// choose Domain
textdomain("Web_Content");

$twigConfig = [
    'cache' => './cache',
    'debug' => true,
    'auto_reload' => true
];

$twigLoader = new FilesystemLoader('./tpl/');
$twig = new Environment($twigLoader, $twigConfig);

// this is for the filter |trans
$filter = new TwigFilter(
    'trans',
    function ($context, $string) {
        return Translation::transGetText($string, $context);
    },
    ['needs_context' => true]
);
$twig->addFilter($filter);

// load the i18n extension for using the translation tag for twig
// {% trans %}my string{% endtrans %}
$twig->addExtension(new Translation());

$extract = new Extract($twig);
$extract->addTemplate('default.twig');
$extract->extract();
  • template file default.twig (see attached files): pasted from jblond/twig-trans/example/tpl/default.twig, I just changed the two strings to be translated so that they can be distinguished into template cache and .po file
<!doctype html>
<html lang="en">
<head>
    <meta name="robots" content="noindex">
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>PHP JBlond Twig Trans</title>
</head>
<body>


{% trans %}my string with tag{% endtrans %}<br><br>

{{ 'my string with filter'|trans }}<br><br>
THE END

<div style="clear:both;"></div>
</body>
</html>
  • cached template cached-template.php (see attached files): the string translated by the trans filter is not rendered as a gettext() call
....
 // line 13
        echo gettext("my string with tag");
        echo "<br><br>

";
        // line 15
        echo twig_escape_filter($this->env, $this->env->getFilter('trans')->getCallable()($context, "my string with filter"), "html", null, true);
....

  • po file messages.po (see attached files): contains only the string translated with the trans tag
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-07 09:18+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: cache/13/13ec69835dd1de6d8dabce3213b8da08.php:51
msgid "my string with tag"
msgstr ""

I tried various combinations for the trans filter definition options (is_safe, is_safe_callback, pre_escape, preserves_safety) with no luck, the only working solution I found is to bypass twig-trans library:

$filter = new TwigFilter(
  'trans', 
  'gettext',
  [
    //'needs_context' => true,
    'is_safe_callback' => 'twig_escape_filter_is_safe',
  ]
);

FILES: I had to add the .txt extension to be able to upload them

commented
msgid "my string with tag"
msgstr ""

Your msgstr is empty, but it needs to be filled and compiled to mo file. Is that a copy and paste mistake?

I wonder why you chose the code from the extract.php file instead of the example.php file?

hello:

  1. I uploaded the po file as it has been generated, if I open it with poedit it shows as Source text "my string with tag" end the Translation is empty, in my experience this is what it happens with newly generated po files; in my workflow I generate pos, edit with poedit to fill empty translations and upload back;
  2. well, I do need po files to manage sites translations and it seemed to me that example.php does not generate po files

I don't know, maybe I'm missing something about twig-trans and/or gettext logic...

Did you run a program to fill in the po file? In my setup, we use a translation api to fill in the po files.

I'm not sure to understand the question, if you mean how I add translations for the source text

  • I download the generated po files
  • edit them in poedit to add translations
  • save them
  • upload back the po and mo files and translations are immediately available.

I do not have until now set up a web interface to fill in the po files because I had the naive illusion that gettext could be somehow a standard for translators too but so far I have been disappointed, I only managed to make some of my customers install poedit and return po/mo files to me.

If you instead are wondering how po files are generated into my application these are the steps:

  • twig templates contain trans tags and filters
  • i have a script that calls an instance of a class I wrote that:
    • sets twig cache (Twig\Environment::setCache())
    • loops al the twig templates and loads each template (Twig\Environment::loadTemplate())
    • loops configured languages and calls xgettext to generate po files:
$commands = <<<EOT
find {$this->buildPathToTranslationsCache()} -type f \( -name '*.php' \) -print | xargs xgettext -c --default-domain={$domain} -p {$paths[$context]->poFolder} --from-code=UTF-8 --no-location -L PHP -d {$domain} --package-name={$package} -j - && msgattrib --set-obsolete -o {$paths[$context]->poFile} {$paths[$context]->poFile}
EOT;
//if local context cat the share po file and regenerate the mo file to incorporate any new share translation
if($context == 'local' && is_file($pathToSharePoFile)) {
    $commands .= <<<EOT
    && msgcat --use-first -o {$pathToLocalPoFile} {$pathToSharePoFile} {$pathToLocalPoFile} && msgfmt -o {$pathToLocalMoFile} {$pathToLocalPoFile}
EOT;
}
//exec commands
passthru($commands, $output);

It worked like a charm in the past but with this last environment (see above) I have problems to make the trans filter work. I suspect some change in Twig (maybe into Twig\Extension\EscaperExtension.php twig_escape_filter function?) but I was not able to find an explanation

oh sorry was misundesrtanding that there were 2 msgstr's you were expecting to see.

Something else... are you always using the filter like:

{{ 'text with filter'|trans }}

Does it make a difference if you do something like:

{% set linkName = 'text with filter'|trans %}

<a href="">{{ linkName }}</a>

Just curious, cause in my app we only ever use the filter in a manner like that, rather than as part of an output string, since with the directly output string {{ something }} you can always use the tags {% trans %}something{% endtrans %}

Only other thing I can think of is something about php8.2. Haven't updated to that yet (though I actually need to start doing that next week, so we'll see I guess).

nope! Part of my translations are actually stored into a twig variable this way:

{% set labels = setLabels('navigation', {
    'home': 'home'|trans,
    'prodotti': 'i nostri prodotti'|trans,
}) %} 

But just to meticulous I made a test with the pages I'm working on, reverting the trans filter definition to the one written in the docs:

<div>
    {% trans %}
    new text by tag
    {% endtrans %}
</div>
<div>
    {% set test = 'another text by filter'|trans %}
    {{ test }}
</div>

new text by tag appears into po file, another text by filter does not (and both are correctly displayed into page)

commented

I added the last example to https://github.com/JBlond/twig-trans/tree/twig-trans-8
It works as expected.

Also note: You have to restart the web server or fpm process in order to load changes in the mo files. PHP gettext is aggressively caching the content.

I added the last example to https://github.com/JBlond/twig-trans/tree/twig-trans-8 It works as expected.

which version of PHP with?

Also note: You have to restart the web server or fpm process in order to load changes in the mo files. PHP gettext is aggressively caching the content.

Yes, I experienced this in the past and in fact I was - pleasantly - surprised when in the current environment (after uploading the po/mo files with the translated text) I had no need to restart the PHP FPM process to see the new transltion be used into the pages.
Anyway I'm curious: I saw that the mo file is included in the last commit, how do you generate it? because I include the call by PHP to gettext shell command to get the job done but in my last tests with jblond/twig-trans using Extract::extract() only the po file has been generated. I have this feeling that I still miss some piece of the puzzle...

commented

The PHP version is 8.2.0 tested on Windows. On Debian, it is PHP 8.2.4

I use msgfmt

msgfmt Web_Content.po -o Web_Content.mo
commented

@vukbgit If you use https://github.com/JBlond/twig-trans/tree/twig-trans-8 does the example work on your setup?

Hey JBlond, sorry I couldn't find the time to test the above version yet, but I stumbled over issue #4 and I think it applies to my situation too: generating twig templates cache and translations inserted by filter not showing up into po file. Am I wrong?