GithubHelp home page GithubHelp logo

twig-translation's Introduction

Twig Translation Extension

A Twig Translation Extension.

Latest Version on Packagist Software License Build Status Code Coverage Scrutinizer Code Quality Total Downloads

Please read this first!

The symfony/twig-bridge also provides a Twig 3 TranslationExtension to translate messages with the trans filter. For this reason the odan/twig-translation component is just redundant and will be deprecated in the near future. I strongly recommend you to use the symfony/twig-bridge TranslationExtension instead.

Here you can find an installation guide:

Requirements

  • PHP 7.3+ or 8.0+

Installation

composer require odan/twig-translation

Registering the extension

This example uses the symfony/translation component:

composer require symfony/translation 

Register the Twig Extension:

$loader = new \Twig\Loader\FilesystemLoader('/path/to/templates');
$twig = new \Twig\Environment($loader, array(
    'cache' => '/path/to/twig-cache',
));

$translator = new \Symfony\Component\Translation\Translator(
    'en_US',
    new MessageFormatter(new IdentityTranslator()),
    null
);

$translator->addLoader('mo', new MoFileLoader());

$twig->addExtension(new \Odan\Twig\TwigTranslationExtension($translator));

Slim 4 integration

To install the symfony/translation component, run:

composer require symfony/translation 

Add settings:

// Locale settings
$settings['locale'] = [
    'path' => '/path/to/resources/locale',
    'cache' => '/path/to/locale-cache',
    'locale' => 'en_US',
    'domain' => 'messages',
    // Should be set to false in production
    'debug' => false,
];

Add a new container definition:

<?php

use Odan\Twig\TwigTranslationExtension;
use Psr\Container\ContainerInterface;
use Symfony\Component\Translation\Formatter\MessageFormatter;
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Translation\Loader\MoFileLoader;
use Symfony\Component\Translation\Translator;

// ...

return [
    // ...

    Translator::class => function (ContainerInterface $container) {
        $settings = $container->get('settings')['locale'];

        $translator = new Translator(
            $settings['locale'],
            new MessageFormatter(new IdentityTranslator()),
            $settings['cache'],
            $settings['debug']
        );

        $translator->addLoader('mo', new MoFileLoader());

        // Optional: Inject the translator instance into the __() function
        // __($translator);

        return $translator;
    },

    Twig::class => function (ContainerInterface $container) {
        $twig = Twig::create('/path/to/templates', []);
    
        // Add extension
        $translator = $container->get(Translator::class);
        $twig->addExtension(new TwigTranslationExtension($translator));
    
        // Add more extension ...
    
        return $twig;
    },

];

Create a global translation function

This step is optional, but recommend if you want to translate messages directly in PHP.

Create the file src/Utility/translate.php and copy / paste this content:

<?php

use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * Translate text.
 *
 * @param string|TranslatorInterface $message The message being translated or the translator
 * @param string|int|float|bool ...$context The context arguments
 *
 * @return string The translated message
 */
function __($message, ...$context): string
{
    /** @var TranslatorInterface $translator */
    static $translator = null;
    if ($message instanceof TranslatorInterface) {
        $translator = $message;

        return '';
    }

    $translated = $translator->trans($message);
    if (!empty($context)) {
        $translated = vsprintf($translated, $context);
    }

    return $translated;
}

Register the composer autoloader in composer.json:

"autoload": {
    "files": [
        "src/Utility/translate.php"
    ]
},

Run: composer update

Usage

Translate a text:

{{ __('Yes') }}

Translate a text with a placeholder:

{{ __('Hello: %s', username) }}

Output (depends on the language):

Hello admin

Translate a text with multiple placeholders:

{{ __('First name: %s, Last name: %s', firstName, lastName) }}

Output (depends on the language):

First name: John, Last name: Doe

Create a plural translation:

Example 1:

{% if count > 1 %}
    {{ count }} {{ __('Users') }}
{% else %}
    {{ count }} {{ __('User') }}
{% endif %}

Example 2:

{% if users|length > 1 %}
    {{ users|length }} {{ __('Users') }}
{% else %}
    {{ users|length }} {{ __('User') }}
{% endif %}

Create a complex plural translation:

{% if not count %}
    {{ __('No users') }}
{% elseif count = 1 %}
    {{ count }} {{ __('User') }}
{% else %}
    {{ count }} {{ __('Users') }}
{% endif %}

Parsing with Poedit

The workflow

  1. Parse all twig files (php bin/parse-twig.php)
  2. Start Poedit and open the .po file
  3. Click the Update button to parse all PHP and Twig cache files
  4. Translate the text and save the file.

Poedit Setup

  • Start Poedit and open the .po file
  • Open the menu: Catalogue > Properties...
  • Open the tab: Source paths
    • Add a new path and point it to the twig cache
    • The path must be relative to the base path e.g. ..\temp\twig-cache
  • Open the tab: Source keyword
    • Add a new keyword with the name __ (2 underscores)
  • Click the OK button and Update the calalogue.

Parsing Twig files

You need to iterate and compile all your Twig templates. The compilation step generates the PHP cache files that can be parsed from Poedit. This script is only an example and must be adapted to your individual environment.

Twig settings:

// Twig settings
$settings['twig'] = [
    'path' => '/path/to/twig/templates',
    // Should be set to true in production
    'cache_enabled' => true,
    'cache_path' => '/path/to/twig-cache', // <---
];

File: bin/parse-twig.php

use Odan\Twig\TwigCompiler;
use Slim\App;
use Slim\Views\Twig;

// Bootstrap Slim application

/** @var ContainerInterface $container */
$container = (require __DIR__ . '/../config/bootstrap.php')->getContainer();

/** @var App $app */
$app = $container->get(App::class);

// Read twig settings
$settings = $container->get('settings')['twig'];
$cachePath = (string)$settings['cache_path'];

$twig = $container->get(Twig::class)->getEnvironment();

// Compile twig templates (*.twig) to PHP code
$compiler = new TwigCompiler($twig, $cachePath, true);
$compiler->compile();

echo "Done\n";

return 0;

To run this script just enter: php bin/parse-twig.php

Similar libraries

The symfony/twig-bridge provides TranslationExtension for Twig 3+.

Read more

License

  • MIT

twig-translation's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar odan avatar twebt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

twig-translation's Issues

Update for Twig 3

Now that twig-extensions is deprecated and not available for Twig 3, how can this be updated to work with Twig 3? I can't even find 'trans' or i18n extension as part of the 'Core Extensions'.

Or, is this something that will have to be created from scratch, without using Twig's Core Extensions?

I'm not asking you to rewrite this, only asking your opinion. I am considering using your class for compiling the templates along with PHP-I18N in a project of mine.

Thanks for all you do. I am learning alot, at least I hope I am, from reading and using your projects and tutorials.

Im stuck ... ConfigCacheFactory' not found

Hi Odan,

any idea on this ?

`
Type: Error
Code: 0
Message: Class 'Symfony\Component\Config\ConfigCacheFactory' not found
File: ...\vendor\symfony\translation\Translator.php
Line: 461
Trace

#0 ..\vendor\symfony\translation\Translator.php(287): Symfony\Component\Translation\Translator->getConfigCacheFactory()
#1 ..\vendor\symfony\translation\Translator.php(261): Symfony\Component\Translation\Translator->initializeCacheCatalogue('fr_FR')
#2 ..\vendor\symfony\translation\Translator.php(240): Symfony\Component\Translation\Translator->loadCatalogue('fr_FR')
#3 ..\vendor\symfony\translation\Translator.php(206): Symfony\Component\Translation\Translator->getCatalogue('fr_FR')
#4 ..\vendor\odan\twig-translation\src\TwigTranslationExtension.php(66): Symfony\Component\Translation\Translator->trans('Welcome', Array)
#5 ..\tmp\twig\fd\fd9eef701efe2a41a295486cca34e1a74fd5a02c9924a43be694b595671751e5.php(51): Odan\Twig\TwigTranslationExtension->__('Welcome')`

TwigCompiler

When I import and use your TwigCompiler, this line $this->cachePath = str_replace('\\', '/', trim($cachePath, '\/'));
removes leading '/', I'm assuming to make the ensure a relative path. But my path is an absolute path and this breaks the ability to create the dir, if needed.

When I used this in my project, before you updated this, I had to remove the str_replace and simply use $this->cachePath = trim($cachePath);

When I look at your Slim4-Skeleton it appears that you are also using an absolute path, so I am not sure how it works there, either.

I am probably missing something that you have configured in your project that I do not have in mine.

Any help would be appreciated.

Thanks again for the quick update.

I suppose that a right trim would do the trick also .

Translation file not read?

I made a project based on the slim4 skeleton, leaving out a number of things for I don't need them now. I did leave in the twig-translation. For some reason, it doesn't seem to get my translation text.

In my en_US_messages.po file I have:
msgid "strCurrentTime" msgstr "Current time"

In my twig template I have:
{{ __('strCurrentTime') }}

My settings locale is set to
en-US

Now when I load the page, I see 'strCurrentTime', and not the message string. Even if I change the settings locale (not the browser) to 'fr_FR' it will not get the value from the 'po' file. If I empty the locale-cache directory, new files are made, but I don't see something recognizable in these files related to the string. If I place echo commands in the 'translate.php' file, I get response the result on my page, but never a translated value.

Any tips?

Problems with bin/parse-twig.php

I modified the bin/parse-twig.php suggested in https://github.com/odan/twig-translation#parsing-twig-files slightly to fit into my app (https://github.com/vaizard/glued-skeleton/blob/master/glued/Core/Bin/parse-twig.php). Upon running the script, I'm getting

root@gdev:/var/www/html/glued-skeleton# php glued/Core/Bin/parse-twig.php 
PHP Notice:  Undefined index: SERVER_NAME in /var/www/html/glued-skeleton/glued/settings.php on line 109
PHP Notice:  Undefined index: SERVER_NAME in /var/www/html/glued-skeleton/glued/settings.php on line 110
PHP Notice:  Undefined index: SERVER_NAME in /var/www/html/glued-skeleton/glued/settings.php on line 111
Parsing: Calendar/Views/browse.twig
Parsing: Calendar/Views/manage.twig
Parsing: Core/Views/accounts.read.twig
PHP Fatal error:  Uncaught Twig\Error\SyntaxError: Unknown "url_for" function. in /var/www/html/glued-skeleton/glued/Core/Views/accounts.read.twig:17
Stack trace:
#0 /var/www/html/glued-skeleton/vendor/twig/twig/src/ExpressionParser.php(451): Twig\ExpressionParser->getFunctionNodeClass()
#1 /var/www/html/glued-skeleton/vendor/twig/twig/src/ExpressionParser.php(235): Twig\ExpressionParser->getFunctionNode()
#2 /var/www/html/glued-skeleton/vendor/twig/twig/src/ExpressionParser.php(175): Twig\ExpressionParser->parsePrimaryExpression()
#3 /var/www/html/glued-skeleton/vendor/twig/twig/src/ExpressionParser.php(70): Twig\ExpressionParser->getPrimary()
#4 /var/www/html/glued-skeleton/vendor/twig/twig/src/Parser.php(142): Twig\ExpressionParser->parseExpression()
#5 /var/www/html/glued-skeleton/vendor/twig/twig/src/TokenParser/BlockTokenParser.php(45): Twig\Parser->subparse()
#6 /var/www/html/glued-skeleton/vendor/twig/twig/src/Parser.php(185): Twig\TokenParser\BlockTokenParser->parse()
#7 /var/www/html/glued-skeleton/vendor/twig/twig/s in /var/www/html/glued-skeleton/glued/Core/Views/accounts.read.twig on line 17

I'm not sure why I get the Unknown "url_for" function error. It seems that the slimphp/Twig-View somehow doesn't get loaded, even though I'm getting $twig from the container, so I should be 1:1 with the rest of the app. I'm really at loss how to fix this. Got any ideas you could kindly suggest?

Thanks, P.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.