Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 08:02:04 +01:00
parent 4413528994
commit 1d90fbf296
6865 changed files with 1091082 additions and 0 deletions

View file

@ -0,0 +1,33 @@
# Change Log
## [Unreleased] -
## [4.0.1] - 2021-06-10
* Fix TransNode constructor optional parameters
## [4.0.0] - 2021-02-25
* Add support for domain translation (#4)
* TransNode constructor signature changed, new $domain parameter `?Node $notes, ?Node $domain = null, int $lineno` (#4)
* TransNode constructor signature changed, new $context parameter `?AbstractExpression $count, ?Node $context = null, ?Node $notes` (#6)
* Add support for contexts in translations (#6)
* Add support for enabling `phpmyadmin/motranslator` or complex non php-gettext supported functions (#6)
* Add support for custom notes labels (#6)
* Make debug info disabled by default, `TransNode::$enableAddDebugInfo = true;` will add it back
* Some slight performance improvements
* Added tests for all the code
## [3.0.0] - 2020-06-14
* Add a .gitattributes file
* Support Twig 3
* Remove extra field from composer.json
* Add support field in composer.json
* Require php >= 7.1
* Setup and apply phpmyadmin/coding-standard
* Apply changes for php 8.0 compatibility (https://github.com/twigphp/Twig/issues/3327)
## [2.0.0] - 2020-01-14
* First release of this library.

View file

@ -0,0 +1,20 @@
Copyright (c) 2010-2019 Fabien Potencier
Copyright (c) 2019-2021 phpMyAdmin contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,47 @@
{
"name": "phpmyadmin/twig-i18n-extension",
"description": "Internationalization support for Twig via the gettext library",
"keywords": ["i18n","gettext"],
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "The phpMyAdmin Team",
"email": "developers@phpmyadmin.net",
"homepage": "https://www.phpmyadmin.net/team/"
}
],
"support": {
"issues": "https://github.com/phpmyadmin/twig-i18n-extension/issues",
"source": "https://github.com/phpmyadmin/twig-i18n-extension"
},
"require": {
"php": "^7.1 || ^8.0",
"twig/twig": "^1.42.3|^2.0|^3.0"
},
"require-dev": {
"phpmyadmin/coding-standard": "^3.0.0",
"phpmyadmin/motranslator": "^5.2",
"phpstan/phpstan": "^0.12.66",
"phpunit/phpunit": "^7 || ^8 || ^9"
},
"scripts": {
"phpstan": "./vendor/bin/phpstan analyse",
"phpunit": "phpunit",
"phpcs": "phpcs",
"phpcbf": "phpcbf"
},
"autoload": {
"psr-4": { "PhpMyAdmin\\Twig\\Extensions\\": "src/" }
},
"autoload-dev": {
"psr-4": { "PhpMyAdmin\\Tests\\Twig\\Extensions\\": "test/" }
},
"config":{
"sort-packages": true
}
}

View file

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/*
* This file is part of Twig I18n extension.
*
* (c) 2010-2019 Fabien Potencier
* (c) 2019-2021 phpMyAdmin contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpMyAdmin\Twig\Extensions;
use PhpMyAdmin\Twig\Extensions\TokenParser\TransTokenParser;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use function dgettext;
use function gettext;
class I18nExtension extends AbstractExtension
{
/**
* {@inheritdoc}
*/
public function getTokenParsers()
{
return [new TransTokenParser()];
}
/**
* {@inheritdoc}
*/
public function getFilters()
{
return [
new TwigFilter('trans', [$this, 'translate']), /* Note, the filter does not handle plurals */
];
}
/**
* {@inheritdoc}
*
* @return string
*/
public function getName()
{
return 'i18n';
}
/**
* Translate a GetText string via filter
*
* @param string $message The message to translate
* @param string|null $domain The GetText domain
*/
public function translate(string $message, ?string $domain = null): string
{
/* If we don't have a domain, assume we're just using the default */
if ($domain === null) {
return gettext($message);
}
/* Otherwise specify where the message comes from */
return dgettext($domain, $message);
}
}

View file

@ -0,0 +1,315 @@
<?php
declare(strict_types=1);
/*
* This file is part of Twig I18n extension.
*
* (c) 2010-2019 Fabien Potencier
* (c) 2019-2021 phpMyAdmin contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpMyAdmin\Twig\Extensions\Node;
use Twig\Compiler;
use Twig\Node\CheckToStringNode;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\TempNameExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use function array_merge;
use function count;
use function sprintf;
use function str_replace;
use function trim;
/**
* Represents a trans node.
*
* Author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TransNode extends Node
{
/**
* The label for gettext notes to be exported
*
* @var string
*/
public static $notesLabel = '// notes: ';
/**
* Enable MoTranslator functions
*
* @var bool
*/
public static $enableMoTranslator = false;
/**
* Enable calls to addDebugInfo
*
* @var bool
*/
public static $enableAddDebugInfo = false;
/**
* Enables context functions usage
*
* @var bool
*/
public static $hasContextFunctions = false;
public function __construct(
Node $body,
?Node $plural,
?AbstractExpression $count,
?Node $context = null,
?Node $notes = null,
?Node $domain = null,
int $lineno = 0,
?string $tag = null
) {
$nodes = ['body' => $body];
if ($count !== null) {
$nodes['count'] = $count;
}
if ($plural !== null) {
$nodes['plural'] = $plural;
}
if ($notes !== null) {
$nodes['notes'] = $notes;
}
if ($domain !== null) {
$nodes['domain'] = $domain;
}
if ($context !== null) {
$nodes['context'] = $context;
}
parent::__construct($nodes, [], $lineno, $tag);
}
/**
* {@inheritdoc}
*/
public function compile(Compiler $compiler)
{
if (self::$enableAddDebugInfo) {
$compiler->addDebugInfo($this);
}
[$msg, $vars] = $this->compileString($this->getNode('body'));
$hasPlural = $this->hasNode('plural');
if ($hasPlural) {
[$msg1, $vars1] = $this->compileString($this->getNode('plural'));
$vars = array_merge($vars, $vars1);
}
$hasDomain = $this->hasNode('domain');
$hasContext = $this->hasNode('context');
$function = $this->getTransFunction($hasPlural, $hasContext, $hasDomain);
if ($this->hasNode('notes')) {
$message = trim($this->getNode('notes')->getAttribute('data'));
// line breaks are not allowed cause we want a single line comment
$message = str_replace(["\n", "\r"], ' ', $message);
$compiler->raw(static::$notesLabel . $message . "\n");
}
if ($vars) {
$compiler
->raw('echo strtr(' . $function . '(');
if ($hasDomain) {
[$domain] = $this->compileString($this->getNode('domain'));
$compiler
->subcompile($domain)
->raw(', ');
}
if ($hasContext && (static::$hasContextFunctions || static::$enableMoTranslator)) {
[$context] = $this->compileString($this->getNode('context'));
$compiler
->subcompile($context)
->raw(', ');
}
$compiler
->subcompile($msg);
if ($hasPlural) {
$compiler
->raw(', ')
->subcompile($msg1)
->raw(', abs(')
->subcompile($this->getNode('count'))
->raw(')');
}
$compiler->raw('), array(');
foreach ($vars as $var) {
$attributeName = $var->getAttribute('name');
if ($attributeName === 'count') {
$compiler
->string('%count%')
->raw(' => abs(')
->subcompile($this->getNode('count'))
->raw('), ');
} else {
$compiler
->string('%' . $attributeName . '%')
->raw(' => ')
->subcompile($var)
->raw(', ');
}
}
$compiler->raw("));\n");
} else {
$compiler
->raw('echo ' . $function . '(');
if ($hasDomain) {
[$domain] = $this->compileString($this->getNode('domain'));
$compiler
->subcompile($domain)
->raw(', ');
}
if ($hasContext) {
if (static::$hasContextFunctions || static::$enableMoTranslator) {
[$context] = $this->compileString($this->getNode('context'));
$compiler
->subcompile($context)
->raw(', ');
}
}
$compiler
->subcompile($msg);
if ($hasPlural) {
$compiler
->raw(', ')
->subcompile($msg1)
->raw(', abs(')
->subcompile($this->getNode('count'))
->raw(')');
}
$compiler->raw(");\n");
}
}
/**
* Keep this method protected instead of private some implementations may use it
*/
protected function compileString(Node $body): array
{
if (
$body instanceof NameExpression
|| $body instanceof ConstantExpression
|| $body instanceof TempNameExpression
) {
return [$body, []];
}
$vars = [];
if (count($body)) {
$msg = '';
foreach ($body as $node) {
if ($node instanceof PrintNode) {
$n = $node->getNode('expr');
while ($n instanceof FilterExpression) {
$n = $n->getNode('node');
}
while ($n instanceof CheckToStringNode) {
$n = $n->getNode('expr');
}
$attributeName = $n->getAttribute('name');
$msg .= sprintf('%%%s%%', $attributeName);
$vars[] = new NameExpression($attributeName, $n->getTemplateLine());
} else {
$msg .= $node->getAttribute('data');
}
}
} else {
$msg = $body->getAttribute('data');
}
return [new Node([new ConstantExpression(trim($msg), $body->getTemplateLine())]), $vars];
}
/**
* Keep this protected to allow people to override it with their own logic
*/
protected function getTransFunction(bool $hasPlural, bool $hasContext, bool $hasDomain): string
{
$functionPrefix = '';
if (static::$enableMoTranslator) {
// The functions are prefixed with an underscore
$functionPrefix = '_';
}
// If it has not context function support or not MoTranslator
if (! static::$hasContextFunctions && ! static::$enableMoTranslator) {
// Not found on native PHP: dnpgettext, npgettext, dpgettext, pgettext
// No domain plural context support
// No domain context support
// No context support
// No plural context support
if ($hasDomain) {
// dngettext($domain, $msgid, $msgidPlural, $number);
// dgettext($domain, $msgid);
return $functionPrefix . ($hasPlural ? 'dngettext' : 'dgettext');
}
// ngettext($msgid, $msgidPlural, $number);
// gettext($msgid);
return $functionPrefix . ($hasPlural ? 'ngettext' : 'gettext');
}
if ($hasDomain) {
if ($hasPlural) {
// dnpgettext($domain, $msgctxt, $msgid, $msgidPlural, $number);
// dngettext($domain, $msgid, $msgidPlural, $number);
return $functionPrefix . ($hasContext ? 'dnpgettext' : 'dngettext');
}
// dpgettext($domain, $msgctxt, $msgid);
// dgettext($domain, $msgid);
return $functionPrefix . ($hasContext ? 'dpgettext' : 'dgettext');
}
if ($hasPlural) {
// npgettext($msgctxt, $msgid, $msgidPlural, $number);
// ngettext($msgid, $msgidPlural, $number);
return $functionPrefix . ($hasContext ? 'npgettext' : 'ngettext');
}
// pgettext($msgctxt, $msgid);
// gettext($msgid);
return $functionPrefix . ($hasContext ? 'pgettext' : 'gettext');
}
}

View file

@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
/*
* This file is part of Twig I18n extension.
*
* (c) 2010-2019 Fabien Potencier
* (c) 2019-2021 phpMyAdmin contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpMyAdmin\Twig\Extensions\TokenParser;
use PhpMyAdmin\Twig\Extensions\Node\TransNode;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\TextNode;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;
class TransTokenParser extends AbstractTokenParser
{
/**
* {@inheritdoc}
*/
public function parse(Token $token)
{
[
$body,
$plural,
$count,
$context,
$notes,
$domain,
$lineno,
$tag,
] = $this->preParse($token);
return new TransNode($body, $plural, $count, $context, $notes, $domain, $lineno, $tag);
}
protected function preParse(Token $token): array
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$domain = null;
$count = null;
$plural = null;
$notes = null;
$context = null;
/* If we aren't closing the block, do we have a domain? */
if ($stream->test(Token::NAME_TYPE)) {
$stream->expect(Token::NAME_TYPE, 'from');
$domain = $this->parser->getExpressionParser()->parseExpression();
}
if (! $stream->test(Token::BLOCK_END_TYPE)) {
$body = $this->parser->getExpressionParser()->parseExpression();
} else {
$stream->expect(Token::BLOCK_END_TYPE);
$body = $this->parser->subparse([$this, 'decideForFork']);
$next = $stream->next()->getValue();
if ($next === 'plural') {
$count = $this->parser->getExpressionParser()->parseExpression();
$stream->expect(Token::BLOCK_END_TYPE);
$plural = $this->parser->subparse([$this, 'decideForFork']);
$next = $stream->next()->getValue();
if ($next === 'notes') {
$stream->expect(Token::BLOCK_END_TYPE);
$notes = $this->parser->subparse([$this, 'decideForEnd'], true);
} elseif ($next === 'context') {
$stream->expect(Token::BLOCK_END_TYPE);
$context = $this->parser->subparse([$this, 'decideForEnd'], true);
}
} elseif ($next === 'context') {
$stream->expect(Token::BLOCK_END_TYPE);
$context = $this->parser->subparse([$this, 'decideForEnd'], true);
} elseif ($next === 'notes') {
$stream->expect(Token::BLOCK_END_TYPE);
$notes = $this->parser->subparse([$this, 'decideForEnd'], true);
}
}
$stream->expect(Token::BLOCK_END_TYPE);
$this->checkTransString($body, $lineno);
return [$body, $plural, $count, $context, $notes, $domain, $lineno, $this->getTag()];
}
/**
* @return bool
*/
public function decideForFork(Token $token)
{
return $token->test(['plural', 'context', 'notes', 'endtrans']);
}
/**
* @return bool
*/
public function decideForEnd(Token $token)
{
return $token->test('endtrans');
}
/**
* {@inheritdoc}
*/
public function getTag()
{
return 'trans';
}
/**
* @return void
*
* @throws SyntaxError
*/
protected function checkTransString(Node $body, int $lineno)
{
foreach ($body as $i => $node) {
if (
$node instanceof TextNode
||
($node instanceof PrintNode && $node->getNode('expr') instanceof NameExpression)
) {
continue;
}
throw new SyntaxError(
'The text to be translated with "trans" can only contain references to simple variables.',
$lineno
);
}
}
}