Require gettext/translator

This commit is contained in:
grandeljay 2022-06-08 12:26:34 +02:00
parent cbdedfc7f4
commit dd41b890e3
20 changed files with 1079 additions and 5 deletions

View file

@ -6,7 +6,8 @@
"embed/embed": "^4.3", "embed/embed": "^4.3",
"grandel/include-directory": "^0.2.2", "grandel/include-directory": "^0.2.2",
"qferr/mjml-php": "^1.1", "qferr/mjml-php": "^1.1",
"gettext/gettext": "^5.6" "gettext/gettext": "^5.6",
"gettext/translator": "^1.1"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {

76
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "4c60c00f536a20de82f5546d4c604eb5", "content-hash": "a1fb82d56813359d39e2e661463e8dd0",
"packages": [ "packages": [
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
@ -319,6 +319,80 @@
], ],
"time": "2021-11-11T17:30:39+00:00" "time": "2021-11-11T17:30:39+00:00"
}, },
{
"name": "gettext/translator",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/php-gettext/Translator.git",
"reference": "b18ff33e8203de623854561f5e47e992fc5c50bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-gettext/Translator/zipball/b18ff33e8203de623854561f5e47e992fc5c50bb",
"reference": "b18ff33e8203de623854561f5e47e992fc5c50bb",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15",
"gettext/gettext": "^5.0.0",
"oscarotero/php-cs-fixer-config": "^1.0",
"phpunit/phpunit": "^8.0",
"squizlabs/php_codesniffer": "^3.0"
},
"suggest": {
"gettext/gettext": "Is necessary to load and generate array files used by the translator"
},
"type": "library",
"autoload": {
"psr-4": {
"Gettext\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"description": "Gettext translator functions",
"homepage": "https://github.com/php-gettext/Translator",
"keywords": [
"gettext",
"i18n",
"php",
"translator"
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/php-gettext/Translator/issues",
"source": "https://github.com/php-gettext/Translator/tree/v1.1.1"
},
"funding": [
{
"url": "https://paypal.me/oscarotero",
"type": "custom"
},
{
"url": "https://github.com/oscarotero",
"type": "github"
},
{
"url": "https://www.patreon.com/misteroom",
"type": "patreon"
}
],
"time": "2022-02-23T20:29:40+00:00"
},
{ {
"name": "grandel/include-directory", "name": "grandel/include-directory",
"version": "v0.2.2", "version": "v0.2.2",

View file

@ -18,7 +18,7 @@ return array(
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Grandel\\' => array($vendorDir . '/grandel/include-directory/src'), 'Grandel\\' => array($vendorDir . '/grandel/include-directory/src'),
'Gettext\\Languages\\' => array($vendorDir . '/gettext/languages/src'), 'Gettext\\Languages\\' => array($vendorDir . '/gettext/languages/src'),
'Gettext\\' => array($vendorDir . '/gettext/gettext/src'), 'Gettext\\' => array($vendorDir . '/gettext/gettext/src', $vendorDir . '/gettext/translator/src'),
'Embed\\' => array($vendorDir . '/embed/embed/src'), 'Embed\\' => array($vendorDir . '/embed/embed/src'),
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), 'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),

View file

@ -114,6 +114,7 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1
'Gettext\\' => 'Gettext\\' =>
array ( array (
0 => __DIR__ . '/..' . '/gettext/gettext/src', 0 => __DIR__ . '/..' . '/gettext/gettext/src',
1 => __DIR__ . '/..' . '/gettext/translator/src',
), ),
'Embed\\' => 'Embed\\' =>
array ( array (

View file

@ -403,6 +403,83 @@
], ],
"install-path": "../gettext/languages" "install-path": "../gettext/languages"
}, },
{
"name": "gettext/translator",
"version": "v1.1.1",
"version_normalized": "1.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-gettext/Translator.git",
"reference": "b18ff33e8203de623854561f5e47e992fc5c50bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-gettext/Translator/zipball/b18ff33e8203de623854561f5e47e992fc5c50bb",
"reference": "b18ff33e8203de623854561f5e47e992fc5c50bb",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15",
"gettext/gettext": "^5.0.0",
"oscarotero/php-cs-fixer-config": "^1.0",
"phpunit/phpunit": "^8.0",
"squizlabs/php_codesniffer": "^3.0"
},
"suggest": {
"gettext/gettext": "Is necessary to load and generate array files used by the translator"
},
"time": "2022-02-23T20:29:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Gettext\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"description": "Gettext translator functions",
"homepage": "https://github.com/php-gettext/Translator",
"keywords": [
"gettext",
"i18n",
"php",
"translator"
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/php-gettext/Translator/issues",
"source": "https://github.com/php-gettext/Translator/tree/v1.1.1"
},
"funding": [
{
"url": "https://paypal.me/oscarotero",
"type": "custom"
},
{
"url": "https://github.com/oscarotero",
"type": "github"
},
{
"url": "https://www.patreon.com/misteroom",
"type": "patreon"
}
],
"install-path": "../gettext/translator"
},
{ {
"name": "grandel/include-directory", "name": "grandel/include-directory",
"version": "v0.2.2", "version": "v0.2.2",

View file

@ -5,7 +5,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '1676940e47329dfd87e34a4928446ce52a8725aa', 'reference' => 'cbdedfc7f48f7851b8bf5411f75a9df9f2ceae94',
'name' => '__root__', 'name' => '__root__',
'dev' => true, 'dev' => true,
), ),
@ -16,7 +16,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '1676940e47329dfd87e34a4928446ce52a8725aa', 'reference' => 'cbdedfc7f48f7851b8bf5411f75a9df9f2ceae94',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'composer/ca-bundle' => array( 'composer/ca-bundle' => array(
@ -64,6 +64,15 @@
'reference' => 'ed56dd2c7f4024cc953ed180d25f02f2640e3ffa', 'reference' => 'ed56dd2c7f4024cc953ed180d25f02f2640e3ffa',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'gettext/translator' => array(
'pretty_version' => 'v1.1.1',
'version' => '1.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../gettext/translator',
'aliases' => array(),
'reference' => 'b18ff33e8203de623854561f5e47e992fc5c50bb',
'dev_requirement' => false,
),
'grandel/include-directory' => array( 'grandel/include-directory' => array(
'pretty_version' => 'v0.2.2', 'pretty_version' => 'v0.2.2',
'version' => '0.2.2.0', 'version' => '0.2.2.0',

31
vendor/gettext/translator/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,31 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [1.1.1] - 2022-02-23
### Security
- Updated the plural form conversion to accept a limited character set [#5]
## [1.1.0] - 2022-02-23
### Added
- Option to include empty translations in the `ArrayGenerator` [#3]
## [1.0.1] - 2020-12-01
### Fixed
- Added PHP 8 to composer.json
- Define functions only if they are undefined [#1]
## [1.0.0] - 2019-11-05
First version
[#1]: https://github.com/php-gettext/Translator/issues/1
[#3]: https://github.com/php-gettext/Translator/issues/3
[#5]: https://github.com/php-gettext/Translator/issues/5
[1.1.1]: https://github.com/php-gettext/Translator/compare/v1.1.0...v1.1.1
[1.1.0]: https://github.com/php-gettext/Translator/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/php-gettext/Translator/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/php-gettext/Translator/releases/tag/v1.0.0

View file

@ -0,0 +1,17 @@
Contributing to Gettext
=======================
Looking to contribute something to this library? Here's how you can help.
## Bugs
A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful thank you!
Please try to be as detailed as possible in your report. Include specific information about the environment version of PHP, version of gettext, etc, and steps required to reproduce the issue.
## Pull Requests
Good pull requests patches, improvements, new features are a fantastic help. New extractors or generator are welcome. Before create a pull request, please follow these instructions:
* The code must be PSR-2 compliant
* Write some tests

21
vendor/gettext/translator/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2019 Oscar Otero Marzoa
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.

92
vendor/gettext/translator/README.md vendored Normal file
View file

@ -0,0 +1,92 @@
# Translator
[![Latest Version on Packagist][ico-version]][link-packagist]
[![Software License][ico-license]](LICENSE)
[![Total Downloads][ico-downloads]][link-downloads]
Created by Oscar Otero <http://oscarotero.com> <oom@oscarotero.com> (MIT License)
Translator functions to use with [gettext/gettext](https://github.com/php-gettext/Gettext). Useful if you don't have the native gettext extension for php or want to avoid problems with it.
## Installation
```
composer require gettext/translator
```
## Translator
```php
use Gettext\Translator;
//Create a new instance of the translator
$t = new Translator();
//Load the translations from php files (generated by Gettext\Extractors\PhpArray)
$t->loadTranslations(
'locales/gl/domain1.php',
'locales/gl/domain2.php',
'locales/gl/domain3.php',
);
//Now you can use it in your templates
echo $t->gettext('apple');
```
## GettextTranslator
The class `Gettext\GettextTranslator` uses the gettext extension. It's useful because combines the performance of using real gettext functions but with the same API than `Translator` class, so you can switch to one or other translator without change code of your app.
```php
use Gettext\GettextTranslator;
//Create a new instance
$t = new GettextTranslator();
//It detects the environment variables to set the locale, but you can change it:
$t->setLanguage('gl');
//Load the domains:
$t->loadDomain('messages', 'project/Locale');
//this means you have the file "project/Locale/gl/LC_MESSAGES/messages.po"
//Now you can use it in your templates
echo $t->gettext('apple');
```
## Translator functions
To ease the use of translations in your php templates, you can use the provided functions:
```php
use Gettext\TranslatorFunctions;
//Register the translator to use the global functions
TranslatorFunctions::register($t);
echo __('apple'); // it's the same than $t->gettext('apple');
```
You can scan the php files containing these functions and extract the values with the PhpCode extractor:
```html
<!-- index.php -->
<html>
<body>
<?= __('Hello world'); ?>
</body>
</html>
```
---
Please see [CHANGELOG](CHANGELOG.md) for more information about recent changes and [CONTRIBUTING](CONTRIBUTING.md) for contributing details.
The MIT License (MIT). Please see [LICENSE](LICENSE) for more information.
[ico-version]: https://img.shields.io/packagist/v/gettext/translator.svg?style=flat-square
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
[ico-downloads]: https://img.shields.io/packagist/dt/gettext/translator.svg?style=flat-square
[link-packagist]: https://packagist.org/packages/gettext/translator
[link-downloads]: https://packagist.org/packages/gettext/translator

50
vendor/gettext/translator/composer.json vendored Normal file
View file

@ -0,0 +1,50 @@
{
"name": "gettext/translator",
"type": "library",
"description": "Gettext translator functions",
"keywords": ["php", "gettext", "i18n", "translator"],
"homepage": "https://github.com/php-gettext/Translator",
"license": "MIT",
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/php-gettext/Translator/issues"
},
"require": {
"php": "^7.2|^8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0",
"gettext/gettext": "^5.0.0",
"squizlabs/php_codesniffer": "^3.0",
"oscarotero/php-cs-fixer-config": "^1.0",
"friendsofphp/php-cs-fixer": "^2.15"
},
"suggest": {
"gettext/gettext": "Is necessary to load and generate array files used by the translator"
},
"autoload": {
"psr-4": {
"Gettext\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Gettext\\Tests\\": "tests"
}
},
"scripts": {
"test": [
"phpunit",
"phpcs"
],
"cs-fix": "php-cs-fixer fix"
}
}

View file

@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);
namespace Gettext;
class Formatter implements FormatterInterface
{
public function format(string $text, array $args): string
{
if (empty($args)) {
return $text;
}
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
}
}

View file

@ -0,0 +1,12 @@
<?php
declare(strict_types = 1);
namespace Gettext;
/**
* Interface used by formatters.
*/
interface FormatterInterface
{
public function format(string $text, array $args): string;
}

View file

@ -0,0 +1,69 @@
<?php
declare(strict_types = 1);
namespace Gettext\Generator;
use Gettext\Headers;
use Gettext\Translation;
use Gettext\Translations;
final class ArrayGenerator extends Generator
{
private $includeEmpty;
/**
* Constructs a new ArrayGenerator
* @param array|null $options
*
* bool includeEmpty Controls whether empty translations should be included (default: false)
*/
public function __construct(?array $options = null)
{
$this->includeEmpty = (bool) ($options['includeEmpty'] ?? false);
}
public function generateString(Translations $translations): string
{
$array = $this->generateArray($translations);
return sprintf('<?php return %s;', var_export($array, true));
}
public function generateArray(Translations $translations): array
{
$pluralForm = $translations->getHeaders()->getPluralForm();
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
$messages = [];
foreach ($translations as $translation) {
if ((!$this->includeEmpty && !$translation->getTranslation()) || $translation->isDisabled()) {
continue;
}
$context = $translation->getContext() ?: '';
$original = $translation->getOriginal();
if (!isset($messages[$context])) {
$messages[$context] = [];
}
if (self::hasPluralTranslations($translation)) {
$messages[$context][$original] = $translation->getPluralTranslations($pluralSize);
array_unshift($messages[$context][$original], $translation->getTranslation());
} else {
$messages[$context][$original] = $translation->getTranslation();
}
}
return [
'domain' => $translations->getDomain(),
'plural-forms' => $translations->getHeaders()->get(Headers::HEADER_PLURAL),
'messages' => $messages,
];
}
private static function hasPluralTranslations(Translation $translation): bool
{
return implode('', $translation->getPluralTranslations()) !== '';
}
}

View file

@ -0,0 +1,116 @@
<?php
declare(strict_types = 1);
namespace Gettext;
use RuntimeException;
class GettextTranslator implements TranslatorInterface
{
/**
* Detects the current language using the environment variables.
*/
public function __construct(string $language = null)
{
if (!function_exists('gettext')) {
throw new RuntimeException('This class require the gettext extension for PHP');
}
//detects the language environment respecting the priority order
//http://php.net/manual/en/function.gettext.php#114062
if (empty($language)) {
$language = getenv('LANGUAGE') ?: getenv('LC_ALL') ?: getenv('LC_MESSAGES') ?: getenv('LANG');
}
if (!empty($language)) {
$this->setLanguage($language);
}
}
/**
* Define the current locale.
*/
public function setLanguage(string $language, int $category = null): self
{
if ($category === null) {
$category = defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL;
}
setlocale($category, $language);
putenv('LANGUAGE='.$language);
return $this;
}
/**
* Loads a gettext domain.
*/
public function loadDomain(string $domain, string $path = null, bool $default = true): self
{
bindtextdomain($domain, $path);
bind_textdomain_codeset($domain, 'UTF-8');
if ($default) {
textdomain($domain);
}
return $this;
}
public function noop(string $original): string
{
return $original;
}
public function gettext(string $original): string
{
return gettext($original);
}
public function ngettext(string $original, string $plural, int $value): string
{
return ngettext($original, $plural, $value);
}
public function dngettext(string $domain, string $original, string $plural, int $value): string
{
return dngettext($domain, $original, $plural, $value);
}
public function npgettext(string $context, string $original, string $plural, int $value): string
{
$message = $context."\x04".$original;
$translation = ngettext($message, $plural, $value);
return ($translation === $message) ? $original : $translation;
}
public function pgettext(string $context, string $original): string
{
$message = $context."\x04".$original;
$translation = gettext($message);
return ($translation === $message) ? $original : $translation;
}
public function dgettext(string $domain, string $original): string
{
return dgettext($domain, $original);
}
public function dpgettext(string $domain, string $context, string $original): string
{
$message = $context."\x04".$original;
$translation = dgettext($domain, $message);
return ($translation === $message) ? $original : $translation;
}
public function dnpgettext(string $domain, string $context, string $original, string $plural, int $value): string
{
$message = $context."\x04".$original;
$translation = dngettext($domain, $message, $plural, $value);
return ($translation === $message) ? $original : $translation;
}
}

View file

@ -0,0 +1,72 @@
<?php
declare(strict_types = 1);
namespace Gettext\Loader;
use BadMethodCallException;
use Gettext\Headers;
use Gettext\Translations;
/**
* Class to load a array file
*/
final class ArrayLoader extends Loader
{
public function loadFile(string $filename, Translations $translations = null): Translations
{
$array = self::includeSafe($filename);
return $this->loadArray($array, $translations);
}
public function loadString(string $string, Translations $translations = null): Translations
{
throw new BadMethodCallException('Arrays cannot be loaded from string. Use ArrayLoader::loadFile() instead');
}
private static function includeSafe($filename): array
{
return include $filename;
}
public function loadArray(array $array, Translations $translations = null): Translations
{
if (!$translations) {
$translations = $this->createTranslations();
}
$messages = $array['messages'] ?? [];
foreach ($messages as $context => $contextTranslations) {
if ($context === '') {
$context = null;
}
foreach ($contextTranslations as $original => $value) {
if ($original === '') {
continue;
}
$translation = $this->createTranslation($context, $original);
$translations->add($translation);
if (is_array($value)) {
$translation->translate(array_shift($value));
$translation->translatePlural(...$value);
} else {
$translation->translate($value);
}
}
}
if (!empty($array['domain'])) {
$translations->setDomain($array['domain']);
}
if (!empty($array['plural-forms'])) {
$translations->getHeaders()->set(Headers::HEADER_PLURAL, $array['plural-forms']);
}
return $translations;
}
}

View file

@ -0,0 +1,231 @@
<?php
declare(strict_types = 1);
namespace Gettext;
use Gettext\Generator\ArrayGenerator;
use InvalidArgumentException;
class Translator implements TranslatorInterface
{
protected $domain;
protected $dictionary = [];
protected $plurals = [];
public static function createFromTranslations(Translations ...$allTranslations): Translator
{
$translator = new static();
$arrayGenerator = new ArrayGenerator();
foreach ($allTranslations as $translations) {
$translator->addTranslations(
$arrayGenerator->generateArray($translations)
);
}
return $translator;
}
/**
* Load new translations from php files
*/
public function loadTranslations(string ...$files): self
{
foreach ($files as $file) {
$translations = include $file;
if (!is_array($translations)) {
throw new InvalidArgumentException('Invalid translations file: it must return an array');
}
$this->addTranslations($translations);
}
return $this;
}
/**
* Add new translations to the dictionary.
*/
public function addTranslations(array $translations): self
{
$domain = $translations['domain'] ?? '';
//Set the first domain loaded as default domain
if ($this->domain === null) {
$this->domain = $domain;
}
if (isset($this->dictionary[$domain])) {
$this->dictionary[$domain] = array_replace_recursive($this->dictionary[$domain], $translations['messages']);
return $this;
}
if (!empty($translations['plural-forms'])) {
list($count, $code) = array_map('trim', explode(';', $translations['plural-forms'], 2));
// extract just the expression turn 'n' into a php variable '$n'.
// Slap on a return keyword and semicolon at the end.
$this->plurals[$domain] = [
'count' => (int) str_replace('nplurals=', '', $count),
'code' => 'return ' . static::fixTerseIfs(rtrim(str_replace('plural=', '', $code), ';')) . ';',
];
}
$this->dictionary[$domain] = $translations['messages'];
return $this;
}
/**
* Set the default domain.
*/
public function defaultDomain(string $domain): self
{
$this->domain = $domain;
return $this;
}
public function noop(string $original): string
{
return $original;
}
public function gettext(string $original): string
{
return $this->translate(null, null, $original);
}
public function ngettext(string $original, string $plural, int $value): string
{
return $this->translatePlural(null, null, $original, $plural, $value);
}
public function dngettext(string $domain, string $original, string $plural, int $value): string
{
return $this->translatePlural($domain, null, $original, $plural, $value);
}
public function npgettext(string $context, string $original, string $plural, int $value): string
{
return $this->translatePlural(null, $context, $original, $plural, $value);
}
public function pgettext(string $context, string $original): string
{
return $this->translate(null, $context, $original);
}
public function dgettext(string $domain, string $original): string
{
return $this->translate($domain, null, $original);
}
public function dpgettext(string $domain, string $context, string $original): string
{
return $this->translate($domain, $context, $original);
}
public function dnpgettext(string $domain, string $context, string $original, string $plural, int $value): string
{
return $this->translatePlural($domain, $context, $original, $plural, $value);
}
protected function translate(?string $domain, ?string $context, string $original): string
{
$translation = $this->getTranslation($domain, $context, $original);
if (isset($translation[0]) && $translation[0] !== '') {
return $translation[0];
}
return $original;
}
protected function translatePlural(
?string $domain,
?string $context,
string $original,
string $plural,
int $value
): string {
$translation = $this->getTranslation($domain, $context, $original);
$key = $this->getPluralIndex($domain, $value, $translation === null);
if (isset($translation[$key]) && $translation[$key] !== '') {
return $translation[$key];
}
return ($key === 0) ? $original : $plural;
}
/**
* Search and returns a translation.
*/
protected function getTranslation(?string $domain, ?string $context, string $original): ?array
{
if ($domain === null) {
$domain = $this->domain;
}
if ($context === null) {
$context = '';
}
$translation = $this->dictionary[$domain][$context][$original] ?? null;
return $translation === null ? $translation : (array) $translation;
}
/**
* Executes the plural decision code given the number to decide which
* plural version to take.
*/
protected function getPluralIndex(?string $domain, int $n, bool $fallback): int
{
if ($domain === null) {
$domain = $this->domain;
}
//Not loaded domain or translation, use a fallback
if (!isset($this->plurals[$domain]) || $fallback === true) {
return $n == 1 ? 0 : 1;
}
if (!isset($this->plurals[$domain]['function'])) {
$code = $this->plurals[$domain]['code'];
$this->plurals[$domain]['function'] = eval("return function (\$n) { $code };");
}
if ($this->plurals[$domain]['count'] <= 2) {
return call_user_func($this->plurals[$domain]['function'], $n) ? 1 : 0;
}
return call_user_func($this->plurals[$domain]['function'], $n);
}
/**
* This function prepares the gettext plural form expression to be evaluated by PHP
*
* Nested ternary IFs will be enclosed by parenthesis and the variable "n" will be prefixed by "$".
*
* $n==1 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2
* becomes
* $n==1 ? 0 : ($n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2)
*/
private static function fixTerseIfs(string $pluralForms): string
{
if (preg_match('/[^<>|%&!?:=n()\d\s]/', $pluralForms)) {
throw new InvalidArgumentException('Invalid plural form expression');
}
$pieces = explode(':', str_replace('n', '$n', $pluralForms));
$last = array_pop($pieces);
$pluralForms = '';
foreach ($pieces as $piece) {
$pluralForms .= "$piece:(";
}
return $pluralForms . $last . str_repeat(')', count($pieces));
}
}

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types = 1);
namespace Gettext;
abstract class TranslatorFunctions
{
private static $translator;
private static $formatter;
public static function register(TranslatorInterface $translator, FormatterInterface $formatter = null): void
{
self::$translator = $translator;
self::$formatter = $formatter ?: new Formatter();
include_once __DIR__.'/functions.php';
}
public static function getTranslator(): TranslatorInterface
{
return self::$translator;
}
public static function getFormatter(): FormatterInterface
{
return self::$formatter;
}
}

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types = 1);
namespace Gettext;
/**
* Interface used by all translators.
*/
interface TranslatorInterface
{
/**
* Noop, marks the string for translation but returns it unchanged.
*/
public function noop(string $original): string;
/**
* Gets a translation using the original string.
*/
public function gettext(string $original): string;
/**
* Gets a translation checking the plural form.
*/
public function ngettext(string $original, string $plural, int $value): string;
/**
* Gets a translation checking the domain and the plural form.
*/
public function dngettext(string $domain, string $original, string $plural, int $value): string;
/**
* Gets a translation checking the context and the plural form.
*/
public function npgettext(string $context, string $original, string $plural, int $value): string;
/**
* Gets a translation checking the context.
*/
public function pgettext(string $context, string $original): string;
/**
* Gets a translation checking the domain.
*/
public function dgettext(string $domain, string $original): string;
/**
* Gets a translation checking the domain and context.
*/
public function dpgettext(string $domain, string $context, string $original): string;
/**
* Gets a translation checking the domain, the context and the plural form.
*/
public function dnpgettext(string $domain, string $context, string $original, string $plural, int $value);
}

View file

@ -0,0 +1,102 @@
<?php
use Gettext\TranslatorFunctions as Translator;
if (!function_exists('__')) {
/**
* Returns the translation of a string.
*/
function __(string $original, ...$args): string
{
$text = Translator::getTranslator()->gettext($original);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('noop__')) {
/**
* Noop, marks the string for translation but returns it unchanged.
*/
function noop__(string $original, ...$args): string
{
$text = Translator::getTranslator()->noop($original);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('n__')) {
/**
* Returns the singular/plural translation of a string.
*/
function n__(string $original, string $plural, int $value, ...$args): string
{
$text = Translator::getTranslator()->ngettext($original, $plural, $value);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('p__')) {
/**
* Returns the translation of a string in a specific context.
*/
function p__(string $context, string $original, ...$args): string
{
$text = Translator::getTranslator()->pgettext($context, $original);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('d__')) {
/**
* Returns the translation of a string in a specific domain.
*/
function d__(string $domain, string $original, ...$args): string
{
$text = Translator::getTranslator()->dgettext($domain, $original);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('dp__')) {
/**
* Returns the translation of a string in a specific domain and context.
*/
function dp__(string $domain, string $context, string $original, ...$args): string
{
$text = Translator::getTranslator()->dpgettext($domain, $context, $original);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('dn__')) {
/**
* Returns the singular/plural translation of a string in a specific domain.
*/
function dn__(string $domain, string $original, string $plural, int $value, ...$args): string
{
$text = Translator::getTranslator()->dngettext($domain, $original, $plural, $value);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('np__')) {
/**
* Returns the singular/plural translation of a string in a specific context.
*/
function np__(string $context, string $original, string $plural, int $value, ...$args): string
{
$text = Translator::getTranslator()->npgettext($context, $original, $plural, $value);
return Translator::getFormatter()->format($text, $args);
}
}
if (!function_exists('dnp__')) {
/**
* Returns the singular/plural translation of a string in a specific domain and context.
*/
function dnp__(string $domain, string $context, string $original, string $plural, int $value, ...$args): string
{
$text = Translator::getTranslator()->dnpgettext($domain, $context, $original, $plural, $value);
return Translator::getFormatter()->format($text, $args);
}
}