基础代码
This commit is contained in:
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3::getLoader();
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../gettext/languages/bin" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/export-plural-rules" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../gettext/languages/bin/export-plural-rules
|
||||
php "%BIN_TARGET%" %*
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../php-parallel-lint/php-parallel-lint" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/parallel-lint" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../php-parallel-lint/php-parallel-lint/parallel-lint
|
||||
php "%BIN_TARGET%" %*
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../squizlabs/php_codesniffer/bin" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/phpcbf" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcbf
|
||||
php "%BIN_TARGET%" %*
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../squizlabs/php_codesniffer/bin" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/phpcs" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcs
|
||||
php "%BIN_TARGET%" %*
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../wp-cli/wp-cli/bin" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/wp" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../wp-cli/wp-cli/bin/wp
|
||||
sh "%BIN_TARGET%" %*
|
||||
Vendored
+477
@@ -0,0 +1,477 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
//no-op
|
||||
} elseif ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
+460
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class InstalledVersions
|
||||
{
|
||||
private static $installed = array (
|
||||
'root' =>
|
||||
array (
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => NULL,
|
||||
'name' => 'automattic/underscores',
|
||||
),
|
||||
'versions' =>
|
||||
array (
|
||||
'automattic/underscores' =>
|
||||
array (
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => NULL,
|
||||
),
|
||||
'dealerdirect/phpcodesniffer-composer-installer' =>
|
||||
array (
|
||||
'pretty_version' => 'v0.7.1',
|
||||
'version' => '0.7.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'fe390591e0241955f22eb9ba327d137e501c771c',
|
||||
),
|
||||
'gettext/gettext' =>
|
||||
array (
|
||||
'pretty_version' => 'v4.8.3',
|
||||
'version' => '4.8.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '57ff4fb16647e78e80a5909fe3c190f1c3110321',
|
||||
),
|
||||
'gettext/languages' =>
|
||||
array (
|
||||
'pretty_version' => '2.6.0',
|
||||
'version' => '2.6.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '38ea0482f649e0802e475f0ed19fa993bcb7a618',
|
||||
),
|
||||
'grogy/php-parallel-lint' =>
|
||||
array (
|
||||
'replaced' =>
|
||||
array (
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'jakub-onderka/php-parallel-lint' =>
|
||||
array (
|
||||
'replaced' =>
|
||||
array (
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'mck89/peast' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.12.0',
|
||||
'version' => '1.12.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '833be7a294627a8c5b1c482cbf489f73bf9b8086',
|
||||
),
|
||||
'mustache/mustache' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.13.0',
|
||||
'version' => '2.13.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'e95c5a008c23d3151d59ea72484d4f72049ab7f4',
|
||||
),
|
||||
'php-parallel-lint/php-parallel-lint' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.2.0',
|
||||
'version' => '1.2.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '474f18bc6cc6aca61ca40bfab55139de614e51ca',
|
||||
),
|
||||
'phpcompatibility/php-compatibility' =>
|
||||
array (
|
||||
'pretty_version' => '9.3.5',
|
||||
'version' => '9.3.5.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '9fb324479acf6f39452e0655d2429cc0d3914243',
|
||||
),
|
||||
'phpcompatibility/phpcompatibility-paragonie' =>
|
||||
array (
|
||||
'pretty_version' => '1.3.1',
|
||||
'version' => '1.3.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'ddabec839cc003651f2ce695c938686d1086cf43',
|
||||
),
|
||||
'phpcompatibility/phpcompatibility-wp' =>
|
||||
array (
|
||||
'pretty_version' => '2.1.1',
|
||||
'version' => '2.1.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e',
|
||||
),
|
||||
'rmccue/requests' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.7.0',
|
||||
'version' => '1.7.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '87932f52ffad70504d93f04f15690cf16a089546',
|
||||
),
|
||||
'squizlabs/php_codesniffer' =>
|
||||
array (
|
||||
'pretty_version' => '3.5.8',
|
||||
'version' => '3.5.8.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '9d583721a7157ee997f235f327de038e7ea6dac4',
|
||||
),
|
||||
'symfony/finder' =>
|
||||
array (
|
||||
'pretty_version' => 'v5.2.3',
|
||||
'version' => '5.2.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '4adc8d172d602008c204c2e16956f99257248e03',
|
||||
),
|
||||
'wp-cli/i18n-command' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.2.6',
|
||||
'version' => '2.2.6.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'a66da3f09f6a728832381012848c3074bf1635c8',
|
||||
),
|
||||
'wp-cli/mustangostang-spyc' =>
|
||||
array (
|
||||
'pretty_version' => '0.6.3',
|
||||
'version' => '0.6.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7',
|
||||
),
|
||||
'wp-cli/php-cli-tools' =>
|
||||
array (
|
||||
'pretty_version' => 'v0.11.11',
|
||||
'version' => '0.11.11.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f',
|
||||
),
|
||||
'wp-cli/wp-cli' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.4.1',
|
||||
'version' => '2.4.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'ceb18598e79befa9b2a37a51efbb34910628988b',
|
||||
),
|
||||
'wp-coding-standards/wpcs' =>
|
||||
array (
|
||||
'pretty_version' => '2.3.0',
|
||||
'version' => '2.3.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '7da1894633f168fe244afc6de00d141f27517b62',
|
||||
),
|
||||
'wptrt/wpthemereview' =>
|
||||
array (
|
||||
'pretty_version' => '0.2.1',
|
||||
'version' => '0.2.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '462e59020dad9399ed2fe8e61f2a21b5e206e420',
|
||||
),
|
||||
),
|
||||
);
|
||||
private static $canGetVendors;
|
||||
private static $installedByVendor = array();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function isInstalled($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getRawData()
|
||||
{
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$installed[] = self::$installed;
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
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.
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Application' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Application.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ArrayIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Blame' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Error' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Exception' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\FileWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\IWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\JsonOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Manager' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
|
||||
'JakubOnderka\\PhpParallelLint\\MultipleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\NullWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Output' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ParallelLint' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\Process' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Result' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Result.php',
|
||||
'JakubOnderka\\PhpParallelLint\\RunTimeException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Settings' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
|
||||
'JakubOnderka\\PhpParallelLint\\SyntaxError' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\TextOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\TextOutputColored' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
);
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'be01b9b16925dcb22165c40b46681ac6' => $vendorDir . '/wp-cli/php-cli-tools/lib/cli/cli.php',
|
||||
'3937806105cc8e221b8fa8db5b70d2f2' => $vendorDir . '/wp-cli/mustangostang-spyc/includes/functions.php',
|
||||
'ffb465a494c3101218c4417180c2c9a2' => $vendorDir . '/wp-cli/i18n-command/i18n-command.php',
|
||||
);
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'cli' => array($vendorDir . '/wp-cli/php-cli-tools/lib'),
|
||||
'WP_CLI' => array($vendorDir . '/wp-cli/wp-cli/php'),
|
||||
'Requests' => array($vendorDir . '/rmccue/requests/library'),
|
||||
'Mustache' => array($vendorDir . '/mustache/mustache/src'),
|
||||
);
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'WP_CLI\\I18n\\' => array($vendorDir . '/wp-cli/i18n-command/src'),
|
||||
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
|
||||
'Peast\\test\\' => array($vendorDir . '/mck89/peast/test/Peast'),
|
||||
'Peast\\' => array($vendorDir . '/mck89/peast/lib/Peast'),
|
||||
'Mustangostang\\' => array($vendorDir . '/wp-cli/mustangostang-spyc/src'),
|
||||
'Gettext\\Languages\\' => array($vendorDir . '/gettext/languages/src'),
|
||||
'Gettext\\' => array($vendorDir . '/gettext/gettext/src'),
|
||||
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'),
|
||||
);
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire4def2de59a6f8d56a69341afa83adef3($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire4def2de59a6f8d56a69341afa83adef3($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit4def2de59a6f8d56a69341afa83adef3
|
||||
{
|
||||
public static $files = array (
|
||||
'be01b9b16925dcb22165c40b46681ac6' => __DIR__ . '/..' . '/wp-cli/php-cli-tools/lib/cli/cli.php',
|
||||
'3937806105cc8e221b8fa8db5b70d2f2' => __DIR__ . '/..' . '/wp-cli/mustangostang-spyc/includes/functions.php',
|
||||
'ffb465a494c3101218c4417180c2c9a2' => __DIR__ . '/..' . '/wp-cli/i18n-command/i18n-command.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'W' =>
|
||||
array (
|
||||
'WP_CLI\\I18n\\' => 12,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Component\\Finder\\' => 25,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Peast\\test\\' => 11,
|
||||
'Peast\\' => 6,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
'Mustangostang\\' => 14,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'Gettext\\Languages\\' => 18,
|
||||
'Gettext\\' => 8,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 55,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'WP_CLI\\I18n\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wp-cli/i18n-command/src',
|
||||
),
|
||||
'Symfony\\Component\\Finder\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
||||
),
|
||||
'Peast\\test\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/mck89/peast/test/Peast',
|
||||
),
|
||||
'Peast\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/mck89/peast/lib/Peast',
|
||||
),
|
||||
'Mustangostang\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wp-cli/mustangostang-spyc/src',
|
||||
),
|
||||
'Gettext\\Languages\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/gettext/languages/src',
|
||||
),
|
||||
'Gettext\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/gettext/gettext/src',
|
||||
),
|
||||
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'c' =>
|
||||
array (
|
||||
'cli' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wp-cli/php-cli-tools/lib',
|
||||
),
|
||||
),
|
||||
'W' =>
|
||||
array (
|
||||
'WP_CLI' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wp-cli/wp-cli/php',
|
||||
),
|
||||
),
|
||||
'R' =>
|
||||
array (
|
||||
'Requests' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/rmccue/requests/library',
|
||||
),
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
'Mustache' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/mustache/mustache/src',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Application' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Application.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ArrayIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Blame' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Error' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Exception' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\FileWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\IWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\JsonOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Manager' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
|
||||
'JakubOnderka\\PhpParallelLint\\MultipleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\NullWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Output' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\ParallelLint' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\Process' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
|
||||
'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Result' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Result.php',
|
||||
'JakubOnderka\\PhpParallelLint\\RunTimeException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
|
||||
'JakubOnderka\\PhpParallelLint\\Settings' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
|
||||
'JakubOnderka\\PhpParallelLint\\SyntaxError' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
|
||||
'JakubOnderka\\PhpParallelLint\\TextOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
'JakubOnderka\\PhpParallelLint\\TextOutputColored' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+1137
File diff suppressed because it is too large
Load Diff
Vendored
+200
@@ -0,0 +1,200 @@
|
||||
<?php return array (
|
||||
'root' =>
|
||||
array (
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => NULL,
|
||||
'name' => 'automattic/underscores',
|
||||
),
|
||||
'versions' =>
|
||||
array (
|
||||
'automattic/underscores' =>
|
||||
array (
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => NULL,
|
||||
),
|
||||
'dealerdirect/phpcodesniffer-composer-installer' =>
|
||||
array (
|
||||
'pretty_version' => 'v0.7.1',
|
||||
'version' => '0.7.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'fe390591e0241955f22eb9ba327d137e501c771c',
|
||||
),
|
||||
'gettext/gettext' =>
|
||||
array (
|
||||
'pretty_version' => 'v4.8.3',
|
||||
'version' => '4.8.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '57ff4fb16647e78e80a5909fe3c190f1c3110321',
|
||||
),
|
||||
'gettext/languages' =>
|
||||
array (
|
||||
'pretty_version' => '2.6.0',
|
||||
'version' => '2.6.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '38ea0482f649e0802e475f0ed19fa993bcb7a618',
|
||||
),
|
||||
'grogy/php-parallel-lint' =>
|
||||
array (
|
||||
'replaced' =>
|
||||
array (
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'jakub-onderka/php-parallel-lint' =>
|
||||
array (
|
||||
'replaced' =>
|
||||
array (
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'mck89/peast' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.12.0',
|
||||
'version' => '1.12.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '833be7a294627a8c5b1c482cbf489f73bf9b8086',
|
||||
),
|
||||
'mustache/mustache' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.13.0',
|
||||
'version' => '2.13.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'e95c5a008c23d3151d59ea72484d4f72049ab7f4',
|
||||
),
|
||||
'php-parallel-lint/php-parallel-lint' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.2.0',
|
||||
'version' => '1.2.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '474f18bc6cc6aca61ca40bfab55139de614e51ca',
|
||||
),
|
||||
'phpcompatibility/php-compatibility' =>
|
||||
array (
|
||||
'pretty_version' => '9.3.5',
|
||||
'version' => '9.3.5.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '9fb324479acf6f39452e0655d2429cc0d3914243',
|
||||
),
|
||||
'phpcompatibility/phpcompatibility-paragonie' =>
|
||||
array (
|
||||
'pretty_version' => '1.3.1',
|
||||
'version' => '1.3.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'ddabec839cc003651f2ce695c938686d1086cf43',
|
||||
),
|
||||
'phpcompatibility/phpcompatibility-wp' =>
|
||||
array (
|
||||
'pretty_version' => '2.1.1',
|
||||
'version' => '2.1.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e',
|
||||
),
|
||||
'rmccue/requests' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.7.0',
|
||||
'version' => '1.7.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '87932f52ffad70504d93f04f15690cf16a089546',
|
||||
),
|
||||
'squizlabs/php_codesniffer' =>
|
||||
array (
|
||||
'pretty_version' => '3.5.8',
|
||||
'version' => '3.5.8.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '9d583721a7157ee997f235f327de038e7ea6dac4',
|
||||
),
|
||||
'symfony/finder' =>
|
||||
array (
|
||||
'pretty_version' => 'v5.2.3',
|
||||
'version' => '5.2.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '4adc8d172d602008c204c2e16956f99257248e03',
|
||||
),
|
||||
'wp-cli/i18n-command' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.2.6',
|
||||
'version' => '2.2.6.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'a66da3f09f6a728832381012848c3074bf1635c8',
|
||||
),
|
||||
'wp-cli/mustangostang-spyc' =>
|
||||
array (
|
||||
'pretty_version' => '0.6.3',
|
||||
'version' => '0.6.3.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7',
|
||||
),
|
||||
'wp-cli/php-cli-tools' =>
|
||||
array (
|
||||
'pretty_version' => 'v0.11.11',
|
||||
'version' => '0.11.11.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f',
|
||||
),
|
||||
'wp-cli/wp-cli' =>
|
||||
array (
|
||||
'pretty_version' => 'v2.4.1',
|
||||
'version' => '2.4.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => 'ceb18598e79befa9b2a37a51efbb34910628988b',
|
||||
),
|
||||
'wp-coding-standards/wpcs' =>
|
||||
array (
|
||||
'pretty_version' => '2.3.0',
|
||||
'version' => '2.3.0.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '7da1894633f168fe244afc6de00d141f27517b62',
|
||||
),
|
||||
'wptrt/wpthemereview' =>
|
||||
array (
|
||||
'pretty_version' => '0.2.1',
|
||||
'version' => '0.2.1.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '462e59020dad9399ed2fe8e61f2a21b5e206e420',
|
||||
),
|
||||
),
|
||||
);
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 50600)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"remark-preset-lint-recommended",
|
||||
["remark-lint-list-item-indent", "space"]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
extends: default
|
||||
rules:
|
||||
line-length:
|
||||
level: warning
|
||||
max: 120
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
<potherca@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2020 Dealerdirect B.V.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,251 @@
|
||||
# PHP_CodeSniffer Standards Composer Installer Plugin
|
||||
|
||||
![Project Stage][project-stage-shield]
|
||||
![Last Commit][last-updated-shield]
|
||||
![Awesome][awesome-shield]
|
||||
[![License][license-shield]](LICENSE.md)
|
||||
|
||||
[![Travis][travis-shield]][travis]
|
||||
[![Scrutinizer][scrutinizer-shield]][scrutinizer]
|
||||
[![Latest Version on Packagist][packagist-version-shield]][packagist-version]
|
||||
[![Packagist][packagist-shield]][packagist]
|
||||
|
||||
[![Contributor Covenant][code-of-conduct-shield]][code-of-conduct]
|
||||
|
||||
This composer installer plugin allows for easy installation of [PHP_CodeSniffer][codesniffer] coding standards (rulesets).
|
||||
|
||||
No more symbolic linking of directories, checking out repositories on specific locations or changing
|
||||
the `phpcs` configuration.
|
||||
|
||||
_Note: This plugin is compatible with both version 2.x and 3.x of_ [PHP_CodeSniffer][codesniffer]
|
||||
|
||||
## Usage
|
||||
|
||||
Installation can be done with [Composer][composer], by requiring this package as a development dependency:
|
||||
|
||||
```bash
|
||||
composer require --dev dealerdirect/phpcodesniffer-composer-installer
|
||||
```
|
||||
|
||||
That's it.
|
||||
|
||||
### How it works
|
||||
|
||||
Basically, this plugin executes the following steps:
|
||||
|
||||
- This plugin searches for `phpcodesniffer-standard` packages in all of your currently installed Composer packages.
|
||||
- Matching packages and the project itself are scanned for PHP_CodeSniffer rulesets.
|
||||
- The plugin will call PHP_CodeSniffer and configure the `installed_paths` option.
|
||||
|
||||
### Example project
|
||||
|
||||
The following is an example Composer project and has included
|
||||
multiple `phpcodesniffer-standard` packages.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dealerdirect/example-project",
|
||||
"description": "Just an example project",
|
||||
"type": "project",
|
||||
"require": {},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "*",
|
||||
"object-calisthenics/phpcs-calisthenics-rules": "*",
|
||||
"phpcompatibility/php-compatibility": "*",
|
||||
"wp-coding-standards/wpcs": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After running `composer install` PHP_CodeSniffer just works:
|
||||
|
||||
```bash
|
||||
$ ./vendor/bin/phpcs -i
|
||||
The installed coding standards are MySource, PEAR, PSR1, PSR2, Squiz, Zend, PHPCompatibility, WordPress,
|
||||
WordPress-Core, WordPress-Docs, WordPress-Extra and WordPress-VIP
|
||||
```
|
||||
|
||||
### Calling the plugin directly
|
||||
|
||||
In some circumstances, it is desirable to call this plugin's functionality
|
||||
directly. For instance, during development or in [CI][definition-ci] environments.
|
||||
|
||||
As the plugin requires Composer to work, direct calls need to be wired through a
|
||||
project's `composer.json`.
|
||||
|
||||
This is done by adding a call to the `Plugin::run` function in the `script`
|
||||
section of the `composer.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"install-codestandards": [
|
||||
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The command can then be called using `composer run-script install-codestandards` or
|
||||
referenced from other script configurations, as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"install-codestandards": [
|
||||
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"@install-codestandards"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
For more details about Composer scripts, please refer to [the section on scripts
|
||||
in the Composer manual][composer-manual-scripts].
|
||||
|
||||
### Changing the Coding Standards search depth
|
||||
|
||||
By default, this plugin searches up for Coding Standards up to three directories
|
||||
deep. In most cases, this should be sufficient. However, this plugin allows
|
||||
you to customize the search depth setting if needed.
|
||||
|
||||
```json
|
||||
{
|
||||
"extra": {
|
||||
"phpcodesniffer-search-depth": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Caveats
|
||||
|
||||
When this plugin is installed globally, composer will load the _global_ plugin rather
|
||||
than the one from the local repository. Despite [this behavior being documented
|
||||
in the composer manual][using-composer-plugins], it could potentially confuse
|
||||
as another version of the plugin could be run and not the one specified by the project.
|
||||
|
||||
## Developing Coding Standards
|
||||
|
||||
Coding standard can be developed normally, as documented by [PHP_CodeSniffer][codesniffer], in the [Coding Standard Tutorial][tutorial].
|
||||
|
||||
Create a composer package of your coding standard by adding a `composer.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"name" : "acme/phpcodesniffer-our-standards",
|
||||
"description" : "Package contains all coding standards of the Acme company",
|
||||
"require" : {
|
||||
"php" : ">=5.4.0,<8.0.0-dev",
|
||||
"squizlabs/php_codesniffer" : "^3.0"
|
||||
},
|
||||
"type" : "phpcodesniffer-standard"
|
||||
}
|
||||
```
|
||||
|
||||
Requirements:
|
||||
* The repository may contain one or more standards.
|
||||
* Each standard can have a separate directory no deeper than 3 levels from the repository root.
|
||||
* The package `type` must be `phpcodesniffer-standard`. Without this, the plugin will not trigger.
|
||||
|
||||
### Requiring the plugin from within your coding standard
|
||||
|
||||
If your coding standard itself depends on additional external PHPCS standards, this plugin can
|
||||
make life easier on your end-users by taking care of the installation of all standards - yours
|
||||
and your dependencies - for them.
|
||||
|
||||
This can help reduce the number of support questions about setting the `installed_paths`, as well
|
||||
as simplify your standard's installation instructions.
|
||||
|
||||
For this to work, make sure your external standard adds this plugin to the `composer.json` config
|
||||
via `require`, **not** `require-dev`.
|
||||
|
||||
> :warning: Your end-user may already `require-dev` this plugin and/or other external standards used
|
||||
> by your end-users may require this plugin as well.
|
||||
>
|
||||
> To prevent your end-users getting into "_dependency hell_", make sure to make the version requirement
|
||||
> for this plugin flexible.
|
||||
>
|
||||
> As, for now, this plugin is still regarded as "unstable" (version < 1.0), remember that Composer
|
||||
> treats unstable minors as majors and will not be able to resolve one config requiring this plugin
|
||||
> at version `^0.5`, while another requires it at version `^0.6`.
|
||||
> Either allow multiple minors or use `*` as the version requirement.
|
||||
>
|
||||
> Some examples of flexible requirements which can be used:
|
||||
> ```bash
|
||||
> composer require dealerdirect/phpcodesniffer-composer-installer:"*"
|
||||
> composer require dealerdirect/phpcodesniffer-composer-installer:"0.*"
|
||||
> composer require dealerdirect/phpcodesniffer-composer-installer:"^0.4 || ^0.5 || ^0.6"
|
||||
> ```
|
||||
|
||||
## Changelog
|
||||
|
||||
This repository does not contain a `CHANGELOG.md` file, however, we do publish a changelog on each release
|
||||
using the [GitHub releases][changelog] functionality.
|
||||
|
||||
## Contributing
|
||||
|
||||
This is an active open-source project. We are always open to people who want to
|
||||
use the code or contribute to it.
|
||||
|
||||
We've set up a separate document for our [contribution guidelines][contributing-guidelines].
|
||||
|
||||
Thank you for being involved! :heart_eyes:
|
||||
|
||||
## Authors & contributors
|
||||
|
||||
The original idea and setup of this repository is by [Franck Nijhof][frenck], employee @ Dealerdirect.
|
||||
|
||||
For a full list of all author and/or contributors, check [the contributors page][contributors].
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Dealerdirect B.V.
|
||||
|
||||
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.
|
||||
|
||||
[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg
|
||||
[changelog]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/releases
|
||||
[code-of-conduct-shield]: https://img.shields.io/badge/Contributor%20Covenant-v2.0-ff69b4.svg
|
||||
[code-of-conduct]: CODE_OF_CONDUCT.md
|
||||
[codesniffer]: https://github.com/squizlabs/PHP_CodeSniffer
|
||||
[composer-manual-scripts]: https://getcomposer.org/doc/articles/scripts.md
|
||||
[composer]: https://getcomposer.org/
|
||||
[contributing-guidelines]: CONTRIBUTING.md
|
||||
[contributors]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors
|
||||
[definition-ci]: https://en.wikipedia.org/wiki/Continuous_integration
|
||||
[frenck]: https://github.com/frenck
|
||||
[last-updated-shield]: https://img.shields.io/github/last-commit/Dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[license-shield]: https://img.shields.io/github/license/dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[packagist-shield]: https://img.shields.io/packagist/dt/dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[packagist-version-shield]: https://img.shields.io/packagist/v/dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[packagist-version]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer
|
||||
[packagist]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer
|
||||
[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg
|
||||
[scrutinizer-shield]: https://img.shields.io/scrutinizer/g/dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[scrutinizer]: https://scrutinizer-ci.com/g/dealerdirect/phpcodesniffer-composer-installer/
|
||||
[travis-shield]: https://img.shields.io/travis/Dealerdirect/phpcodesniffer-composer-installer.svg
|
||||
[travis]: https://travis-ci.org/Dealerdirect/phpcodesniffer-composer-installer
|
||||
[tutorial]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Coding-Standard-Tutorial
|
||||
[using-composer-plugins]: https://getcomposer.org/doc/articles/plugins.md#using-plugins
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "dealerdirect/phpcodesniffer-composer-installer",
|
||||
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
|
||||
"type": "composer-plugin",
|
||||
"keywords": [
|
||||
"composer", "installer", "plugin",
|
||||
"phpcs", "codesniffer", "phpcodesniffer", "php_codesniffer",
|
||||
"standard", "standards", "style guide", "stylecheck",
|
||||
"qa", "quality", "code quality", "tests"
|
||||
],
|
||||
"homepage": "http://www.dealerdirect.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Franck Nijhof",
|
||||
"email": "franck.nijhof@dealerdirect.com",
|
||||
"homepage": "http://www.frenck.nl",
|
||||
"role": "Developer / IT Manager"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
|
||||
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"composer-plugin-api": "^1.0 || ^2.0",
|
||||
"squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "*",
|
||||
"sensiolabs/security-checker": "^4.1.0",
|
||||
"phpcompatibility/php-compatibility": "^9.0"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"install-codestandards": [
|
||||
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Dealerdirect PHP_CodeSniffer Standards
|
||||
* Composer Installer Plugin package.
|
||||
*
|
||||
* @copyright 2016-2020 Dealerdirect B.V.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace Dealerdirect\Composer\Plugin\Installers\PHPCodeSniffer;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
use Composer\Script\Event;
|
||||
use Composer\Script\ScriptEvents;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Process\Exception\LogicException;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
/**
|
||||
* PHP_CodeSniffer standard installation manager.
|
||||
*
|
||||
* @author Franck Nijhof <franck.nijhof@dealerdirect.com>
|
||||
*/
|
||||
class Plugin implements PluginInterface, EventSubscriberInterface
|
||||
{
|
||||
|
||||
const KEY_MAX_DEPTH = 'phpcodesniffer-search-depth';
|
||||
|
||||
const MESSAGE_ERROR_WRONG_MAX_DEPTH =
|
||||
'The value of "%s" (in the composer.json "extra".section) must be an integer larger then %d, %s given.';
|
||||
const MESSAGE_NOT_INSTALLED = 'PHPCodeSniffer is not installed';
|
||||
const MESSAGE_NOTHING_TO_INSTALL = 'Nothing to install or update';
|
||||
const MESSAGE_PLUGIN_UNINSTALLED = 'PHPCodeSniffer Composer Installer is uninstalled';
|
||||
const MESSAGE_RUNNING_INSTALLER = 'Running PHPCodeSniffer Composer Installer';
|
||||
|
||||
const PACKAGE_NAME = 'squizlabs/php_codesniffer';
|
||||
const PACKAGE_TYPE = 'phpcodesniffer-standard';
|
||||
|
||||
const PHPCS_CONFIG_REGEX = '`%s:[^\r\n]+`';
|
||||
const PHPCS_CONFIG_KEY = 'installed_paths';
|
||||
|
||||
const PLUGIN_NAME = 'dealerdirect/phpcodesniffer-composer-installer';
|
||||
|
||||
/**
|
||||
* @var Composer
|
||||
*/
|
||||
private $composer;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cwd;
|
||||
|
||||
/**
|
||||
* @var Filesystem
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $installedPaths;
|
||||
|
||||
/**
|
||||
* @var IOInterface
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* @var ProcessExecutor
|
||||
*/
|
||||
private $processExecutor;
|
||||
|
||||
/**
|
||||
* Triggers the plugin's main functionality.
|
||||
*
|
||||
* Makes it possible to run the plugin as a custom command.
|
||||
*
|
||||
* @param Event $event
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function run(Event $event)
|
||||
{
|
||||
$io = $event->getIO();
|
||||
$composer = $event->getComposer();
|
||||
|
||||
$instance = new static();
|
||||
|
||||
$instance->io = $io;
|
||||
$instance->composer = $composer;
|
||||
$instance->init();
|
||||
$instance->onDependenciesChangedEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the plugin so it's main functionality can be run.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
$this->cwd = getcwd();
|
||||
$this->installedPaths = array();
|
||||
|
||||
$this->processExecutor = new ProcessExecutor($this->io);
|
||||
$this->filesystem = new Filesystem($this->processExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
ScriptEvents::POST_INSTALL_CMD => array(
|
||||
array('onDependenciesChangedEvent', 0),
|
||||
),
|
||||
ScriptEvents::POST_UPDATE_CMD => array(
|
||||
array('onDependenciesChangedEvent', 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for post install and post update events.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function onDependenciesChangedEvent()
|
||||
{
|
||||
$io = $this->io;
|
||||
$isVerbose = $io->isVerbose();
|
||||
$exitCode = 0;
|
||||
|
||||
if ($isVerbose) {
|
||||
$io->write(sprintf('<info>%s</info>', self::MESSAGE_RUNNING_INSTALLER));
|
||||
}
|
||||
|
||||
if ($this->isPHPCodeSnifferInstalled() === true) {
|
||||
$this->loadInstalledPaths();
|
||||
$installPathCleaned = $this->cleanInstalledPaths();
|
||||
$installPathUpdated = $this->updateInstalledPaths();
|
||||
|
||||
if ($installPathCleaned === true || $installPathUpdated === true) {
|
||||
$exitCode = $this->saveInstalledPaths();
|
||||
} elseif ($isVerbose) {
|
||||
$io->write(sprintf('<info>%s</info>', self::MESSAGE_NOTHING_TO_INSTALL));
|
||||
}
|
||||
} else {
|
||||
$pluginPackage = $this
|
||||
->composer
|
||||
->getRepositoryManager()
|
||||
->getLocalRepository()
|
||||
->findPackages(self::PLUGIN_NAME)
|
||||
;
|
||||
|
||||
$isPluginUninstalled = count($pluginPackage) === 0;
|
||||
|
||||
if ($isPluginUninstalled) {
|
||||
if ($isVerbose) {
|
||||
$io->write(sprintf('<info>%s</info>', self::MESSAGE_PLUGIN_UNINSTALLED));
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
if ($isVerbose) {
|
||||
$io->write(sprintf('<error>%s</error>', self::MESSAGE_NOT_INSTALLED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all paths from PHP_CodeSniffer into an array.
|
||||
*
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function loadInstalledPaths()
|
||||
{
|
||||
if ($this->isPHPCodeSnifferInstalled() === true) {
|
||||
$this->processExecutor->execute(
|
||||
sprintf(
|
||||
'phpcs --config-show %s',
|
||||
self::PHPCS_CONFIG_KEY
|
||||
),
|
||||
$output,
|
||||
$this->composer->getConfig()->get('bin-dir')
|
||||
);
|
||||
|
||||
$regex = sprintf(self::PHPCS_CONFIG_REGEX, self::PHPCS_CONFIG_KEY);
|
||||
if (preg_match($regex, $output, $match) === 1) {
|
||||
$phpcsInstalledPaths = str_replace(self::PHPCS_CONFIG_KEY . ': ', '', $match[0]);
|
||||
$phpcsInstalledPaths = trim($phpcsInstalledPaths);
|
||||
|
||||
if ($phpcsInstalledPaths !== '') {
|
||||
$this->installedPaths = explode(',', $phpcsInstalledPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all coding standard paths back into PHP_CodeSniffer
|
||||
*
|
||||
* @throws LogicException
|
||||
* @throws ProcessFailedException
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @return int Exit code. 0 for success, 1 or higher for failure.
|
||||
*/
|
||||
private function saveInstalledPaths()
|
||||
{
|
||||
// Check if we found installed paths to set.
|
||||
if (count($this->installedPaths) !== 0) {
|
||||
sort($this->installedPaths);
|
||||
$paths = implode(',', $this->installedPaths);
|
||||
$arguments = array('--config-set', self::PHPCS_CONFIG_KEY, $paths);
|
||||
$configMessage = sprintf(
|
||||
'PHP CodeSniffer Config <info>%s</info> <comment>set to</comment> <info>%s</info>',
|
||||
self::PHPCS_CONFIG_KEY,
|
||||
$paths
|
||||
);
|
||||
} else {
|
||||
// Delete the installed paths if none were found.
|
||||
$arguments = array('--config-delete', self::PHPCS_CONFIG_KEY);
|
||||
$configMessage = sprintf(
|
||||
'PHP CodeSniffer Config <info>%s</info> <comment>delete</comment>',
|
||||
self::PHPCS_CONFIG_KEY
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare message in case of failure
|
||||
$failMessage = sprintf(
|
||||
'Failed to set PHP CodeSniffer <info>%s</info> Config',
|
||||
self::PHPCS_CONFIG_KEY
|
||||
);
|
||||
|
||||
// Determine the path to the main PHPCS file.
|
||||
$phpcsPath = $this->getPHPCodeSnifferInstallPath();
|
||||
if (file_exists($phpcsPath . '/bin/phpcs') === true) {
|
||||
// PHPCS 3.x.
|
||||
$phpcsExecutable = './bin/phpcs';
|
||||
} else {
|
||||
// PHPCS 2.x.
|
||||
$phpcsExecutable = './scripts/phpcs';
|
||||
}
|
||||
|
||||
// Okay, lets rock!
|
||||
$command = vsprintf(
|
||||
'%s %s %s',
|
||||
array(
|
||||
'php executable' => $this->getPhpExecCommand(),
|
||||
'phpcs executable' => $phpcsExecutable,
|
||||
'arguments' => implode(' ', $arguments)
|
||||
)
|
||||
);
|
||||
|
||||
$exitCode = $this->processExecutor->execute($command, $configResult, $phpcsPath);
|
||||
if ($exitCode === 0) {
|
||||
$exitCode = $this->verifySaveSuccess();
|
||||
}
|
||||
|
||||
if ($exitCode === 0) {
|
||||
$this->io->write($configMessage);
|
||||
} else {
|
||||
$this->io->write($failMessage);
|
||||
}
|
||||
|
||||
if ($this->io->isVerbose() && !empty($configResult)) {
|
||||
$this->io->write(sprintf('<info>%s</info>', $configResult));
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the paths which were expected to be saved, have been.
|
||||
*
|
||||
* @return int Exit code. 0 for success, 1 for failure.
|
||||
*/
|
||||
private function verifySaveSuccess()
|
||||
{
|
||||
$exitCode = 1;
|
||||
$expectedPaths = $this->installedPaths;
|
||||
|
||||
// Request the currently set installed paths after the save.
|
||||
$this->loadInstalledPaths();
|
||||
|
||||
$registeredPaths = array_intersect($this->installedPaths, $expectedPaths);
|
||||
$registeredCount = count($registeredPaths);
|
||||
$expectedCount = count($expectedPaths);
|
||||
|
||||
if ($expectedCount === $registeredCount) {
|
||||
$exitCode = 0;
|
||||
}
|
||||
|
||||
if ($exitCode === 1 && $this->io->isVerbose()) {
|
||||
$verificationMessage = sprintf(
|
||||
"Paths to external standards found by the plugin: <info>%s</info>\n"
|
||||
. 'Actual paths registered with PHPCS: <info>%s</info>',
|
||||
implode(', ', $expectedPaths),
|
||||
implode(', ', $this->installedPaths)
|
||||
);
|
||||
$this->io->write($verificationMessage);
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the current PHP version being used.
|
||||
*
|
||||
* Duplicate of the same in the EventDispatcher class in Composer itself.
|
||||
*/
|
||||
protected function getPhpExecCommand()
|
||||
{
|
||||
$finder = new PhpExecutableFinder();
|
||||
|
||||
$phpPath = $finder->find(false);
|
||||
|
||||
if ($phpPath === false) {
|
||||
throw new \RuntimeException('Failed to locate PHP binary to execute ' . $phpPath);
|
||||
}
|
||||
|
||||
$phpArgs = $finder->findArguments();
|
||||
$phpArgs = $phpArgs
|
||||
? ' ' . implode(' ', $phpArgs)
|
||||
: ''
|
||||
;
|
||||
|
||||
$command = ProcessExecutor::escape($phpPath) .
|
||||
$phpArgs .
|
||||
' -d allow_url_fopen=' . ProcessExecutor::escape(ini_get('allow_url_fopen')) .
|
||||
' -d disable_functions=' . ProcessExecutor::escape(ini_get('disable_functions')) .
|
||||
' -d memory_limit=' . ProcessExecutor::escape(ini_get('memory_limit'))
|
||||
;
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate trough all known paths and check if they are still valid.
|
||||
*
|
||||
* If path does not exists, is not an directory or isn't readable, the path
|
||||
* is removed from the list.
|
||||
*
|
||||
* @return bool True if changes where made, false otherwise
|
||||
*/
|
||||
private function cleanInstalledPaths()
|
||||
{
|
||||
$changes = false;
|
||||
foreach ($this->installedPaths as $key => $path) {
|
||||
// This might be a relative path as well
|
||||
$alternativePath = realpath($this->getPHPCodeSnifferInstallPath() . DIRECTORY_SEPARATOR . $path);
|
||||
|
||||
if (
|
||||
(is_dir($path) === false || is_readable($path) === false) &&
|
||||
(is_dir($alternativePath) === false || is_readable($alternativePath) === false)
|
||||
) {
|
||||
unset($this->installedPaths[$key]);
|
||||
$changes = true;
|
||||
}
|
||||
}
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all installed packages (including the root package) against
|
||||
* the installed paths from PHP_CodeSniffer and add the missing ones.
|
||||
*
|
||||
* @return bool True if changes where made, false otherwise
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function updateInstalledPaths()
|
||||
{
|
||||
$changes = false;
|
||||
|
||||
$searchPaths = array($this->cwd);
|
||||
$codingStandardPackages = $this->getPHPCodingStandardPackages();
|
||||
foreach ($codingStandardPackages as $package) {
|
||||
$installPath = $this->composer->getInstallationManager()->getInstallPath($package);
|
||||
if ($this->filesystem->isAbsolutePath($installPath) === false) {
|
||||
$installPath = $this->filesystem->normalizePath(
|
||||
$this->cwd . DIRECTORY_SEPARATOR . $installPath
|
||||
);
|
||||
}
|
||||
$searchPaths[] = $installPath;
|
||||
}
|
||||
|
||||
$finder = new Finder();
|
||||
$finder->files()
|
||||
->depth('<= ' . $this->getMaxDepth())
|
||||
->depth('>= ' . $this->getMinDepth())
|
||||
->ignoreUnreadableDirs()
|
||||
->ignoreVCS(true)
|
||||
->in($searchPaths)
|
||||
->name('ruleset.xml');
|
||||
|
||||
// Process each found possible ruleset.
|
||||
foreach ($finder as $ruleset) {
|
||||
$standardsPath = $ruleset->getPath();
|
||||
|
||||
// Pick the directory above the directory containing the standard, unless this is the project root.
|
||||
if ($standardsPath !== $this->cwd) {
|
||||
$standardsPath = dirname($standardsPath);
|
||||
}
|
||||
|
||||
// Use relative paths for local project repositories.
|
||||
if ($this->isRunningGlobally() === false) {
|
||||
$standardsPath = $this->filesystem->findShortestPath(
|
||||
$this->getPHPCodeSnifferInstallPath(),
|
||||
$standardsPath,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// De-duplicate and add when directory is not configured.
|
||||
if (in_array($standardsPath, $this->installedPaths, true) === false) {
|
||||
$this->installedPaths[] = $standardsPath;
|
||||
$changes = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through Composers' local repository looking for valid Coding
|
||||
* Standard packages.
|
||||
*
|
||||
* If the package is the RootPackage (the one the plugin is installed into),
|
||||
* the package is ignored for now since it needs a different install path logic.
|
||||
*
|
||||
* @return array Composer packages containing coding standard(s)
|
||||
*/
|
||||
private function getPHPCodingStandardPackages()
|
||||
{
|
||||
$codingStandardPackages = array_filter(
|
||||
$this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
|
||||
function (PackageInterface $package) {
|
||||
if ($package instanceof AliasPackage) {
|
||||
return false;
|
||||
}
|
||||
return $package->getType() === Plugin::PACKAGE_TYPE;
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
! $this->composer->getPackage() instanceof RootPackageInterface
|
||||
&& $this->composer->getPackage()->getType() === self::PACKAGE_TYPE
|
||||
) {
|
||||
$codingStandardPackages[] = $this->composer->getPackage();
|
||||
}
|
||||
|
||||
return $codingStandardPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the installed PHP_CodeSniffer Composer package
|
||||
*
|
||||
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
|
||||
*
|
||||
* @return PackageInterface|null
|
||||
*/
|
||||
private function getPHPCodeSnifferPackage($versionConstraint = null)
|
||||
{
|
||||
$packages = $this
|
||||
->composer
|
||||
->getRepositoryManager()
|
||||
->getLocalRepository()
|
||||
->findPackages(self::PACKAGE_NAME, $versionConstraint);
|
||||
|
||||
return array_shift($packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the PHP_CodeSniffer package installation location
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getPHPCodeSnifferInstallPath()
|
||||
{
|
||||
return $this->composer->getInstallationManager()->getInstallPath($this->getPHPCodeSnifferPackage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple check if PHP_CodeSniffer is installed.
|
||||
*
|
||||
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
|
||||
*
|
||||
* @return bool Whether PHP_CodeSniffer is installed
|
||||
*/
|
||||
private function isPHPCodeSnifferInstalled($versionConstraint = null)
|
||||
{
|
||||
return ($this->getPHPCodeSnifferPackage($versionConstraint) !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if composer is running "global"
|
||||
* This check kinda dirty, but it is the "Composer Way"
|
||||
*
|
||||
* @return bool Whether Composer is running "globally"
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function isRunningGlobally()
|
||||
{
|
||||
return ($this->composer->getConfig()->get('home') === $this->cwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the maximum search depth when searching for Coding Standards.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function getMaxDepth()
|
||||
{
|
||||
$maxDepth = 3;
|
||||
|
||||
$extra = $this->composer->getPackage()->getExtra();
|
||||
|
||||
if (array_key_exists(self::KEY_MAX_DEPTH, $extra)) {
|
||||
$maxDepth = $extra[self::KEY_MAX_DEPTH];
|
||||
$minDepth = $this->getMinDepth();
|
||||
|
||||
if (
|
||||
(string) (int) $maxDepth !== (string) $maxDepth /* Must be an integer or cleanly castable to one */
|
||||
|| $maxDepth <= $minDepth /* Larger than the minimum */
|
||||
|| is_float($maxDepth) === true /* Within the boundaries of integer */
|
||||
) {
|
||||
$message = vsprintf(
|
||||
self::MESSAGE_ERROR_WRONG_MAX_DEPTH,
|
||||
array(
|
||||
'key' => self::KEY_MAX_DEPTH,
|
||||
'min' => $minDepth,
|
||||
'given' => var_export($maxDepth, true),
|
||||
)
|
||||
);
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $maxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimal search depth for Coding Standard packages.
|
||||
*
|
||||
* Usually this is 0, unless PHP_CodeSniffer >= 3 is used.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function getMinDepth()
|
||||
{
|
||||
if ($this->isPHPCodeSnifferInstalled('>= 3.0.0') !== true) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
# 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/).
|
||||
|
||||
Previous releases are documented in [github releases](https://github.com/oscarotero/Gettext/releases)
|
||||
|
||||
## [4.8.3] - 2020-11-18
|
||||
### Fixed
|
||||
- Blade extractor for Laravel8/Jetstream [#261]
|
||||
|
||||
## [4.8.2] - 2019-12-02
|
||||
### Fixed
|
||||
- UTF-8 handling for VueJs extractor [#242]
|
||||
|
||||
## [4.8.1] - 2019-11-15
|
||||
### Fixed
|
||||
- Php error when scanning for a single domain but other string found [#238]
|
||||
|
||||
## [4.8.0] - 2019-11-04
|
||||
### Changed
|
||||
- Many `private` properties and methods were changed to `protected` in order to improve the extensibility [#231]
|
||||
|
||||
### Fixed
|
||||
- PHP 7.4 support [#230]
|
||||
|
||||
## [4.7.0] - 2019-10-07
|
||||
### Added
|
||||
- Support for UnitID in Xliff [#221] [#224] [#225]
|
||||
- Support for scan multiple domains at the same time [#223]
|
||||
|
||||
### Fixed
|
||||
- New lines in windows [#218] [#226]
|
||||
|
||||
## [4.6.3] - 2019-07-15
|
||||
### Added
|
||||
- Some VueJs extraction improvements and additions [#205], [#213]
|
||||
|
||||
### Fixed
|
||||
- Multiline extractions in jsCode [#200]
|
||||
- Support for js template literals [#214]
|
||||
- Fixed tabs in PHP comments [#215]
|
||||
|
||||
## [4.6.2] - 2019-01-12
|
||||
### Added
|
||||
- New option `facade` in blade extractor to use a facade instead create a blade compiler [#197], [#198]
|
||||
|
||||
### Fixed
|
||||
- Added php-7.3 to travis
|
||||
- Added VueJS extractor method docblocks for IDEs [#191]
|
||||
|
||||
## [4.6.1] - 2018-08-27
|
||||
### Fixed
|
||||
- VueJS DOM parsing [#188]
|
||||
- Javascript parser was unable to extract some functions [#187]
|
||||
|
||||
## [4.6.0] - 2018-06-26
|
||||
### Added
|
||||
- New extractor for VueJs [#178]
|
||||
|
||||
### Fixed
|
||||
- Do not include empty translations containing the headers in the translator [#182]
|
||||
- Test enhancement [#177]
|
||||
|
||||
## [4.5.0] - 2018-04-23
|
||||
### Added
|
||||
- Support for disabled translations
|
||||
|
||||
### Fixed
|
||||
- Added php-7.2 to travis
|
||||
- Fixed po tests on bigendian [#159]
|
||||
- Improved comment estraction [#166]
|
||||
- Fixed incorrect docs to dn__ function [#170]
|
||||
- Ignored phpcs.xml file on export [#168]
|
||||
- Improved `@method` docs in `Translations` [#175]
|
||||
|
||||
## [4.4.4] - 2018-02-21
|
||||
### Fixed
|
||||
- Changed the comment extraction to be compatible with gettext behaviour: the comment must be placed in the line preceding the function [#161]
|
||||
|
||||
### Security
|
||||
- Validate eval input from plural forms [#156]
|
||||
|
||||
## [4.4.3] - 2017-08-09
|
||||
### Fixed
|
||||
- Handle `NULL` arguments on extract entries in php. For example `dn__(null, 'singular', 'plural')`.
|
||||
- Fixed the `PhpCode` and `JsCode` extractors that didn't extract `dn__` and `dngettext` entries [#155].
|
||||
- Fixed the `PhpCode` and `JsCode` extractors that didn't extract `dnpgettext` correctly.
|
||||
|
||||
## [4.4.2] - 2017-07-27
|
||||
### Fixed
|
||||
- Clone the translations in `Translations::mergeWith` to prevent that the translation is referenced in both places. [#152]
|
||||
- Fixed escaped quotes in the javascript extractor [#154]
|
||||
|
||||
## [4.4.1] - 2017-05-20
|
||||
### Fixed
|
||||
- Fixed a bug where the options was not passed correctly to the merging Translations object [#147]
|
||||
- Unified the plural behaviours between PHP gettext and Translator when the plural translation is unknown [#148]
|
||||
- Removed the deprecated function `create_function()` and use `eval()` instead
|
||||
|
||||
## [4.4.0] - 2017-05-10
|
||||
### Added
|
||||
- New option `noLocation` to po generator, to omit the references [#143]
|
||||
- New options `delimiter`, `enclosure` and `escape_char` to Csv and CsvDictionary extractors and generators [#145]
|
||||
- Added the missing `dn__()` function [#146]
|
||||
|
||||
### Fixed
|
||||
- Improved the code style including php_codesniffer in development
|
||||
|
||||
## 4.3.0 - 2017-03-04
|
||||
### Added
|
||||
- Added support for named placeholders (using `strtr`). For example:
|
||||
```php
|
||||
__('Hello :name', [':name' => 'World']);
|
||||
```
|
||||
- Added support for Twig v2
|
||||
- New function `BaseTranslator::includeFunctions()` to include the functions file without register any translator
|
||||
|
||||
### Fixed
|
||||
- Fixed a bug related with the javascript source extraction with single quotes
|
||||
|
||||
[#143]: https://github.com/oscarotero/Gettext/issues/143
|
||||
[#145]: https://github.com/oscarotero/Gettext/issues/145
|
||||
[#146]: https://github.com/oscarotero/Gettext/issues/146
|
||||
[#147]: https://github.com/oscarotero/Gettext/issues/147
|
||||
[#148]: https://github.com/oscarotero/Gettext/issues/148
|
||||
[#152]: https://github.com/oscarotero/Gettext/issues/152
|
||||
[#154]: https://github.com/oscarotero/Gettext/issues/154
|
||||
[#155]: https://github.com/oscarotero/Gettext/issues/155
|
||||
[#156]: https://github.com/oscarotero/Gettext/issues/156
|
||||
[#159]: https://github.com/oscarotero/Gettext/issues/159
|
||||
[#161]: https://github.com/oscarotero/Gettext/issues/161
|
||||
[#166]: https://github.com/oscarotero/Gettext/issues/166
|
||||
[#168]: https://github.com/oscarotero/Gettext/issues/168
|
||||
[#170]: https://github.com/oscarotero/Gettext/issues/170
|
||||
[#175]: https://github.com/oscarotero/Gettext/issues/175
|
||||
[#177]: https://github.com/oscarotero/Gettext/issues/177
|
||||
[#178]: https://github.com/oscarotero/Gettext/issues/178
|
||||
[#182]: https://github.com/oscarotero/Gettext/issues/182
|
||||
[#187]: https://github.com/oscarotero/Gettext/issues/187
|
||||
[#188]: https://github.com/oscarotero/Gettext/issues/188
|
||||
[#191]: https://github.com/oscarotero/Gettext/issues/191
|
||||
[#197]: https://github.com/oscarotero/Gettext/issues/197
|
||||
[#198]: https://github.com/oscarotero/Gettext/issues/198
|
||||
[#200]: https://github.com/oscarotero/Gettext/issues/200
|
||||
[#205]: https://github.com/oscarotero/Gettext/issues/205
|
||||
[#213]: https://github.com/oscarotero/Gettext/issues/213
|
||||
[#214]: https://github.com/oscarotero/Gettext/issues/214
|
||||
[#215]: https://github.com/oscarotero/Gettext/issues/215
|
||||
[#218]: https://github.com/oscarotero/Gettext/issues/218
|
||||
[#221]: https://github.com/oscarotero/Gettext/issues/221
|
||||
[#223]: https://github.com/oscarotero/Gettext/issues/223
|
||||
[#224]: https://github.com/oscarotero/Gettext/issues/224
|
||||
[#225]: https://github.com/oscarotero/Gettext/issues/225
|
||||
[#226]: https://github.com/oscarotero/Gettext/issues/226
|
||||
[#230]: https://github.com/oscarotero/Gettext/issues/230
|
||||
[#231]: https://github.com/oscarotero/Gettext/issues/231
|
||||
[#238]: https://github.com/oscarotero/Gettext/issues/238
|
||||
[#242]: https://github.com/oscarotero/Gettext/issues/242
|
||||
[#261]: https://github.com/oscarotero/Gettext/issues/261
|
||||
|
||||
[4.8.3]: https://github.com/oscarotero/Gettext/compare/v4.8.2...v4.8.3
|
||||
[4.8.2]: https://github.com/oscarotero/Gettext/compare/v4.8.1...v4.8.2
|
||||
[4.8.1]: https://github.com/oscarotero/Gettext/compare/v4.8.0...v4.8.1
|
||||
[4.8.0]: https://github.com/oscarotero/Gettext/compare/v4.7.0...v4.8.0
|
||||
[4.7.0]: https://github.com/oscarotero/Gettext/compare/v4.6.3...v4.7.0
|
||||
[4.6.3]: https://github.com/oscarotero/Gettext/compare/v4.6.2...v4.6.3
|
||||
[4.6.2]: https://github.com/oscarotero/Gettext/compare/v4.6.1...v4.6.2
|
||||
[4.6.1]: https://github.com/oscarotero/Gettext/compare/v4.6.0...v4.6.1
|
||||
[4.6.0]: https://github.com/oscarotero/Gettext/compare/v4.5.0...v4.6.0
|
||||
[4.5.0]: https://github.com/oscarotero/Gettext/compare/v4.4.4...v4.5.0
|
||||
[4.4.4]: https://github.com/oscarotero/Gettext/compare/v4.4.3...v4.4.4
|
||||
[4.4.3]: https://github.com/oscarotero/Gettext/compare/v4.4.2...v4.4.3
|
||||
[4.4.2]: https://github.com/oscarotero/Gettext/compare/v4.4.1...v4.4.2
|
||||
[4.4.1]: https://github.com/oscarotero/Gettext/compare/v4.4.0...v4.4.1
|
||||
[4.4.0]: https://github.com/oscarotero/Gettext/compare/v4.3.0...v4.4.0
|
||||
+17
@@ -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
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 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.
|
||||
Vendored
+425
@@ -0,0 +1,425 @@
|
||||
Gettext
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/oscarotero/Gettext)
|
||||
[](https://scrutinizer-ci.com/g/oscarotero/Gettext/?branch=master)
|
||||
[](https://packagist.org/packages/gettext/gettext)
|
||||
[](https://packagist.org/packages/gettext/gettext)
|
||||
[](https://packagist.org/packages/gettext/gettext)
|
||||
[](https://packagist.org/packages/gettext/gettext)
|
||||
|
||||
[](https://insight.sensiolabs.com/projects/496dc2a6-43be-4046-a283-f8370239dd47)
|
||||
|
||||
Created by Oscar Otero <http://oscarotero.com> <oom@oscarotero.com> (MIT License)
|
||||
|
||||
Gettext is a PHP (>=5.4) library to import/export/edit gettext from PO, MO, PHP, JS files, etc.
|
||||
|
||||
## Installation
|
||||
|
||||
With composer (recomended):
|
||||
|
||||
```
|
||||
composer require gettext/gettext
|
||||
```
|
||||
|
||||
If you don't use composer in your project, you have to download and place this package in a directory of your project. You need to install also [gettext/languages](https://github.com/mlocati/cldr-to-gettext-plural-rules). Then, include the autoloaders of both projects in any place of your php code:
|
||||
|
||||
```php
|
||||
include_once "libs/gettext/src/autoloader.php";
|
||||
include_once "libs/cldr-to-gettext-plural-rules/src/autoloader.php";
|
||||
```
|
||||
|
||||
## Classes and functions
|
||||
|
||||
This package contains the following classes:
|
||||
|
||||
* `Gettext\Translation` - A translation definition
|
||||
* `Gettext\Translations` - A collection of translations
|
||||
* `Gettext\Extractors\*` - Import translations from various sources (po, mo, php, js, etc)
|
||||
* `Gettext\Generators\*` - Export translations to various formats (po, mo, php, json, etc)
|
||||
* `Gettext\Translator` - To use the translations in your php templates instead the [gettext extension](http://php.net/gettext)
|
||||
* `Gettext\GettextTranslator` - To use the [gettext extension](http://php.net/gettext)
|
||||
|
||||
## Usage example
|
||||
|
||||
```php
|
||||
use Gettext\Translations;
|
||||
|
||||
//import from a .po file:
|
||||
$translations = Translations::fromPoFile('locales/gl.po');
|
||||
|
||||
//edit some translations:
|
||||
$translation = $translations->find(null, 'apple');
|
||||
|
||||
if ($translation) {
|
||||
$translation->setTranslation('Mazá');
|
||||
}
|
||||
|
||||
//export to a php array:
|
||||
$translations->toPhpArrayFile('locales/gl.php');
|
||||
|
||||
//and to a .mo file
|
||||
$translations->toMoFile('Locale/gl/LC_MESSAGES/messages.mo');
|
||||
```
|
||||
|
||||
If you want use this translations in your php templates without using the gettext extension:
|
||||
|
||||
```php
|
||||
use Gettext\Translator;
|
||||
|
||||
//Create the translator instance
|
||||
$t = new Translator();
|
||||
|
||||
//Load your translations (exported as PhpArray):
|
||||
$t->loadTranslations('locales/gl.php');
|
||||
|
||||
//Use it:
|
||||
echo $t->gettext('apple'); // "Mazá"
|
||||
|
||||
//If you want use global functions:
|
||||
$t->register();
|
||||
|
||||
echo __('apple'); // "Mazá"
|
||||
```
|
||||
|
||||
To use this translations with the gettext extension:
|
||||
|
||||
```php
|
||||
use Gettext\GettextTranslator;
|
||||
|
||||
//Create the translator instance
|
||||
$t = new GettextTranslator();
|
||||
|
||||
//Set the language and load the domain
|
||||
$t->setLanguage('gl');
|
||||
$t->loadDomain('messages', 'Locale');
|
||||
|
||||
//Use it:
|
||||
echo $t->gettext('apple'); // "Mazá"
|
||||
|
||||
//Or use the gettext functions
|
||||
echo gettext('apple'); // "Mazá"
|
||||
|
||||
//If you want use the global functions
|
||||
$t->register();
|
||||
|
||||
echo __('apple'); // "Mazá"
|
||||
|
||||
//And use sprintf/strtr placeholders
|
||||
echo __('Hello %s', 'world'); //Hello world
|
||||
echo __('Hello {name}', ['{name}' => 'world']); //Hello world
|
||||
```
|
||||
|
||||
The benefits of using the functions provided by this library (`__()` instead `_()` or `gettext()`) are:
|
||||
|
||||
* You are using the same functions, no matter whether the translations are provided by gettext extension or any other method.
|
||||
* You can use variables easier because `sprintf` functionality is included. For example: `__('Hello %s', 'world')` instead `sprintf(_('Hello %s'), 'world')`.
|
||||
* You can also use named placeholders if the second argument is an array. For example: `__('Hello %name%', ['%name%' => 'world'])` instead of `strtr(_('Hello %name%'), ['%name%' => 'world'])`.
|
||||
|
||||
## Translation
|
||||
|
||||
The `Gettext\Translation` class stores all information about a translation: the original text, the translated text, source references, comments, etc.
|
||||
|
||||
```php
|
||||
// __construct($context, $original, $plural)
|
||||
$translation = new Gettext\Translation('comments', 'One comment', '%s comments');
|
||||
|
||||
$translation->setTranslation('Un comentario');
|
||||
$translation->setPluralTranslation('%s comentarios');
|
||||
|
||||
$translation->addReference('templates/comments/comment.php', 34);
|
||||
$translation->addComment('To display the amount of comments in a post');
|
||||
|
||||
echo $translation->getContext(); // comments
|
||||
echo $translation->getOriginal(); // One comment
|
||||
echo $translation->getTranslation(); // Un comentario
|
||||
|
||||
// etc...
|
||||
```
|
||||
|
||||
## Translations
|
||||
|
||||
The `Gettext\Translations` class stores a collection of translations:
|
||||
|
||||
```php
|
||||
$translations = new Gettext\Translations();
|
||||
|
||||
//You can add new translations using the array syntax
|
||||
$translations[] = new Gettext\Translation('comments', 'One comment', '%s comments');
|
||||
|
||||
//Or using the "insert" method
|
||||
$insertedTranslation = $translations->insert('comments', 'One comment', '%s comments');
|
||||
|
||||
//Find a specific translation
|
||||
$translation = $translations->find('comments', 'One comment');
|
||||
|
||||
//Edit headers, domain, etc
|
||||
$translations->setHeader('Last-Translator', 'Oscar Otero');
|
||||
$translations->setDomain('my-blog');
|
||||
```
|
||||
|
||||
## Extractors
|
||||
|
||||
The extrators allows to fetch gettext values from any source. For example, to scan a .po file:
|
||||
|
||||
```php
|
||||
$translations = new Gettext\Translations();
|
||||
|
||||
//From a file
|
||||
Gettext\Extractors\Po::fromFile('locales/en.po', $translations);
|
||||
|
||||
//From a string
|
||||
$string = file_get_contents('locales2/en.po');
|
||||
Gettext\Extractors\Po::fromString($string, $translations);
|
||||
```
|
||||
|
||||
The better way to use extractors is using the magic methods of `Gettext\Translations`:
|
||||
|
||||
```php
|
||||
//Create a Translations instance using a po file
|
||||
$translations = Gettext\Translations::fromPoFile('locales/en.po');
|
||||
|
||||
//Add more messages from other files
|
||||
$translations->addFromPoFile('locales2/en.po');
|
||||
```
|
||||
|
||||
The available extractors are the following:
|
||||
|
||||
Name | Description | Example
|
||||
---- | ----------- | --------
|
||||
**Blade** | Scans a Blade template (For laravel users). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/blade/input.php)
|
||||
**Csv** | Gets the messages from csv. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Csv.csv)
|
||||
**CsvDictionary** | Gets the messages from csv (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/CsvDictionary.csv)
|
||||
**Jed** | Gets the messages from a json compatible with [Jed](http://slexaxton.github.com/Jed/). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Jed.json)
|
||||
**JsCode** | Scans javascript code looking for all gettext functions (the same than PhpCode but for javascript). You can use [the javascript gettext-translator library](https://github.com/oscarotero/gettext-translator) | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/jscode/input.js)
|
||||
**Json** | Gets the messages from json compatible with [gettext-translator](https://github.com/oscarotero/gettext-translator). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Json.json)
|
||||
**JsonDictionary** | Gets the messages from a json (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/JsonDictionary.json)
|
||||
**Mo** | Gets the messages from MO. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Mo.mo)
|
||||
**PhpArray** | Gets the messages from a php file that returns an array. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/PhpArray.php)
|
||||
**PhpCode** | Scans php code looking for all gettext functions (see `translator_functions.php`). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/phpcode/input.php)
|
||||
**Po** | Gets the messages from PO. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Po.po)
|
||||
**Twig** | To scan a Twig template. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/twig/input.php)
|
||||
**Xliff** | Gets the messages from [xliff (2.0)](http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Xliff.xlf)
|
||||
**Yaml** | Gets the messages from yaml. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Yaml.yml)
|
||||
**YamlDictionary** | Gets the messages from a yaml (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/YamlDictionary.yml)
|
||||
**VueJs** | Gets the messages from a VueJs template. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/vuejs/input.vue)
|
||||
|
||||
## Generators
|
||||
|
||||
The generators export a `Gettext\Translations` instance to any format (po, mo, array, etc).
|
||||
|
||||
```php
|
||||
//Save to a file
|
||||
Gettext\Generators\Po::toFile($translations, 'locales/en.po');
|
||||
|
||||
//Return as a string
|
||||
$content = Gettext\Generators\Po::toString($translations);
|
||||
file_put_contents('locales/en.po', $content);
|
||||
```
|
||||
|
||||
Like extractors, the better way to use generators is using the magic methods of `Gettext\Translations`:
|
||||
|
||||
```php
|
||||
//Extract messages from a php code file
|
||||
$translations = Gettext\Translations::fromPhpCodeFile('templates/index.php');
|
||||
|
||||
//Export to a po file
|
||||
$translations->toPoFile('locales/en.po');
|
||||
|
||||
//Export to a po string
|
||||
$content = $translations->toPoString();
|
||||
file_put_contents('locales/en.po', $content);
|
||||
```
|
||||
|
||||
The available generators are the following:
|
||||
|
||||
Name | Description | Example
|
||||
---- | ----------- | --------
|
||||
**Csv** | Exports to csv. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Csv.csv)
|
||||
**CsvDictionary** | Exports to csv (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/CsvDictionary.csv)
|
||||
**Json** | Exports to json, compatible with [gettext-translator](https://github.com/oscarotero/gettext-translator). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Json.json)
|
||||
**JsonDictionary** | Exports to json (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/JsonDictionary.json)
|
||||
**Mo** | Exports to Mo. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Mo.mo)
|
||||
**PhpArray** | Exports to php code that returns an array. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/PhpArray.php)
|
||||
**Po** | Exports to Po. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Po.po)
|
||||
**Jed** | Exports to json format compatible with [Jed](http://slexaxton.github.com/Jed/). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Jed.json)
|
||||
**Xliff** | Exports to [xliff (2.0)](http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Xliff.xlf)
|
||||
**Yaml** | Exports to yaml. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Yaml.yml)
|
||||
**YamlDictionary** | Exports to yaml (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/YamlDictionary.yml)
|
||||
|
||||
## Translator
|
||||
|
||||
The class `Gettext\Translator` implements the gettext functions in php. Useful if you don't have the native gettext extension for php or want to avoid problems with it. You can load the translations from a php array file or using a `Gettext\Translations` instance:
|
||||
|
||||
```php
|
||||
use Gettext\Translator;
|
||||
|
||||
//Create a new instance of the translator
|
||||
$t = new Translator();
|
||||
|
||||
//Load the translations using any of the following ways:
|
||||
|
||||
// 1. from php files (generated by Gettext\Extractors\PhpArray)
|
||||
$t->loadTranslations('locales/gl.php');
|
||||
|
||||
// 2. using the array directly
|
||||
$array = include 'locales/gl.php';
|
||||
$t->loadTranslations($array);
|
||||
|
||||
// 3. using a Gettext\Translations instance (slower)
|
||||
$translations = Gettext\Translations::fromPoFile('locales/gl.po');
|
||||
$t->loadTranslations($translations);
|
||||
|
||||
//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 deppending of the environment 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');
|
||||
```
|
||||
|
||||
## Global functions
|
||||
|
||||
To ease the use of translations in your php templates, you can use the provided functions:
|
||||
|
||||
```php
|
||||
//Register the translator to use the global functions
|
||||
$t->register();
|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
|
||||
## Merge translations
|
||||
|
||||
To work with different translations you may want merge them in an unique file. There are two ways to do this:
|
||||
|
||||
The simplest way is adding new translations:
|
||||
|
||||
```php
|
||||
use Gettext\Translations;
|
||||
|
||||
$translations = Translations::fromPoFile('my-file1.po');
|
||||
$translations->addFromPoFile('my-file2.po');
|
||||
```
|
||||
|
||||
A more advanced way is merge two `Translations` instances:
|
||||
|
||||
```php
|
||||
use Gettext\Translations;
|
||||
|
||||
//Create a new Translations instances with our translations.
|
||||
|
||||
$translations1 = Translations::fromPoFile('my-file1.po');
|
||||
$translations2 = Translations::fromPoFile('my-file2.po');
|
||||
|
||||
//Merge one inside other:
|
||||
$translations1->mergeWith($translations2);
|
||||
|
||||
//Now translations1 has all values
|
||||
```
|
||||
|
||||
The second argument of `mergeWith` defines how the merge will be done. Use the `Gettext\Merge` constants to configure the merging:
|
||||
|
||||
Constant | Description
|
||||
--------- | -----------
|
||||
`Merge::ADD` | Adds the translations from `$translations2` that are missing
|
||||
`Merge::REMOVE` | Removes the translations missing in `$translations2`
|
||||
`Merge::HEADERS_ADD` | Adds the headers from `$translations2` that are missing
|
||||
`Merge::HEADERS_REMOVE` | Removes the headers missing in `$translations2`
|
||||
`Merge::HEADERS_OVERRIDE` | Overrides the headers with the values of `$translations2`
|
||||
`Merge::LANGUAGE_OVERRIDE` | Set the language defined in `$translations2`
|
||||
`Merge::DOMAIN_OVERRIDE` | Set the domain defined in `$translations2`
|
||||
`Merge::TRANSLATION_OVERRIDE` | Override the translation and plural translations with the value of `$translation2`
|
||||
`Merge::COMMENTS_OURS` | Use only the comments of `$translation1`
|
||||
`Merge::COMMENTS_THEIRS` | Use only the comments of `$translation2`
|
||||
`Merge::EXTRACTED_COMMENTS_OURS` | Use only the extracted comments of `$translation1`
|
||||
`Merge::EXTRACTED_COMMENTS_THEIRS` | Use only the extracted comments of `$translation2`
|
||||
`Merge::FLAGS_OURS` | Use only the flags of `$translation1`
|
||||
`Merge::FLAGS_THEIRS` | Use only the flags of `$translation2`
|
||||
`Merge::REFERENCES_OURS` | Use only the references of `$translation1`
|
||||
`Merge::REFERENCES_THEIRS` | Use only the references of `$translation2`
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
use Gettext\Translations;
|
||||
use Gettext\Merge;
|
||||
|
||||
//Scan the php code to find the latest gettext translations
|
||||
$phpTranslations = Translations::fromPhpCodeFile('my-templates.php');
|
||||
|
||||
//Get the translations of the code that are stored in a po file
|
||||
$poTranslations = Translations::fromPoFile('locale.po');
|
||||
|
||||
//Merge the translations from the po file using the references from `$phpTranslations`:
|
||||
$translations->mergeWith($poTranslations, Merge::REFERENCES_OURS);
|
||||
|
||||
//Now save a po file with the result
|
||||
$translations->toPoFile('locale.po');
|
||||
```
|
||||
|
||||
Note, if the second argument is not defined, the default value is `Merge::DEFAULTS` that's equivalent to `Merge::ADD | Merge::HEADERS_ADD`.
|
||||
|
||||
## Use from CLI
|
||||
|
||||
There's a Robo task to use this library from the command line interface: https://github.com/oscarotero/GettextRobo
|
||||
|
||||
## Use in the browser
|
||||
|
||||
If you want to use your translations in the browser, there's a javascript translator: https://github.com/oscarotero/gettext-translator
|
||||
|
||||
## Third party packages
|
||||
|
||||
Twig integration:
|
||||
|
||||
* [jaimeperez/twig-configurable-i18n](https://packagist.org/packages/jaimeperez/twig-configurable-i18n)
|
||||
* [cemerson/translator-twig-extension](https://packagist.org/packages/cemerson/translator-twig-extension)
|
||||
|
||||
Framework integration:
|
||||
|
||||
* [Laravel 5](https://packagist.org/packages/eusonlito/laravel-gettext)
|
||||
* [CakePHP 3](https://packagist.org/packages/k1low/po)
|
||||
* [Symfony 2](https://packagist.org/packages/mablae/gettext-bundle)
|
||||
|
||||
[add your package](https://github.com/oscarotero/Gettext/issues/new)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks to all [contributors](https://github.com/oscarotero/Gettext/graphs/contributors) specially to [@mlocati](https://github.com/mlocati).
|
||||
|
||||
## Donations
|
||||
|
||||
If this library is useful for you, consider to donate to the author.
|
||||
|
||||
[Buy me a beer :beer:](https://www.paypal.me/oscarotero)
|
||||
|
||||
Thanks in advance!
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "gettext/gettext",
|
||||
"type": "library",
|
||||
"description": "PHP gettext manager",
|
||||
"keywords": ["js", "gettext", "i18n", "translation", "po", "mo"],
|
||||
"homepage": "https://github.com/oscarotero/Gettext",
|
||||
"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/oscarotero/Gettext/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"gettext/languages": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/view": "*",
|
||||
"twig/twig": "^1.31|^2.0",
|
||||
"twig/extensions": "*",
|
||||
"symfony/yaml": "~2",
|
||||
"phpunit/phpunit": "^4.8|^5.7|^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"illuminate/view": "Is necessary if you want to use the Blade extractor",
|
||||
"twig/twig": "Is necessary if you want to use the Twig extractor",
|
||||
"twig/extensions": "Is necessary if you want to use the Twig extractor",
|
||||
"symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Gettext\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Gettext\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"phpunit",
|
||||
"phpcs"
|
||||
]
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
abstract class BaseTranslator implements TranslatorInterface
|
||||
{
|
||||
/** @var TranslatorInterface */
|
||||
public static $current;
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*/
|
||||
public function noop($original)
|
||||
{
|
||||
return $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$previous = static::$current;
|
||||
|
||||
static::$current = $this;
|
||||
|
||||
static::includeFunctions();
|
||||
|
||||
return $previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the gettext functions
|
||||
*/
|
||||
public static function includeFunctions()
|
||||
{
|
||||
include_once __DIR__.'/translator_functions.php';
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\View\Compilers\BladeCompiler;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from blade.php files returning arrays.
|
||||
*/
|
||||
class Blade extends Extractor implements ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
if (empty($options['facade'])) {
|
||||
$cachePath = empty($options['cachePath']) ? sys_get_temp_dir() : $options['cachePath'];
|
||||
$bladeCompiler = new BladeCompiler(new Filesystem(), $cachePath);
|
||||
|
||||
if (method_exists($bladeCompiler, 'withoutComponentTags')) {
|
||||
$bladeCompiler->withoutComponentTags();
|
||||
}
|
||||
|
||||
$string = $bladeCompiler->compileString($string);
|
||||
} else {
|
||||
$string = $options['facade']::compileString($string);
|
||||
}
|
||||
|
||||
PhpCode::fromString($string, $translations, $options);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\HeadersExtractorTrait;
|
||||
use Gettext\Utils\CsvTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from csv.
|
||||
*/
|
||||
class Csv extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use HeadersExtractorTrait;
|
||||
use CsvTrait;
|
||||
|
||||
public static $options = [
|
||||
'delimiter' => ",",
|
||||
'enclosure' => '"',
|
||||
'escape_char' => "\\"
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$handle = fopen('php://memory', 'w');
|
||||
|
||||
fputs($handle, $string);
|
||||
rewind($handle);
|
||||
|
||||
while ($row = static::fgetcsv($handle, $options)) {
|
||||
$context = array_shift($row);
|
||||
$original = array_shift($row);
|
||||
|
||||
if ($context === '' && $original === '') {
|
||||
static::extractHeaders(array_shift($row), $translations);
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation = $translations->insert($context, $original);
|
||||
|
||||
if (!empty($row)) {
|
||||
$translation->setTranslation(array_shift($row));
|
||||
$translation->setPluralTranslations($row);
|
||||
}
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\HeadersExtractorTrait;
|
||||
use Gettext\Utils\CsvTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from csv.
|
||||
*/
|
||||
class CsvDictionary extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use HeadersExtractorTrait;
|
||||
use CsvTrait;
|
||||
|
||||
public static $options = [
|
||||
'delimiter' => ",",
|
||||
'enclosure' => '"',
|
||||
'escape_char' => "\\"
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$handle = fopen('php://memory', 'w');
|
||||
|
||||
fputs($handle, $string);
|
||||
rewind($handle);
|
||||
|
||||
while ($row = static::fgetcsv($handle, $options)) {
|
||||
list($original, $translation) = $row + ['', ''];
|
||||
|
||||
if ($original === '') {
|
||||
static::extractHeaders($translation, $translations);
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations->insert(null, $original)->setTranslation($translation);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Gettext\Translations;
|
||||
|
||||
abstract class Extractor implements ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromFile($file, Translations $translations, array $options = [])
|
||||
{
|
||||
foreach (static::getFiles($file) as $file) {
|
||||
$options['file'] = $file;
|
||||
static::fromString(static::readFile($file), $translations, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and returns all files.
|
||||
*
|
||||
* @param string|array $file The file/s
|
||||
*
|
||||
* @return array The file paths
|
||||
*/
|
||||
protected static function getFiles($file)
|
||||
{
|
||||
if (empty($file)) {
|
||||
throw new InvalidArgumentException('There is not any file defined');
|
||||
}
|
||||
|
||||
if (is_string($file)) {
|
||||
if (!is_file($file)) {
|
||||
throw new InvalidArgumentException("'$file' is not a valid file");
|
||||
}
|
||||
|
||||
if (!is_readable($file)) {
|
||||
throw new InvalidArgumentException("'$file' is not a readable file");
|
||||
}
|
||||
|
||||
return [$file];
|
||||
}
|
||||
|
||||
if (is_array($file)) {
|
||||
$files = [];
|
||||
|
||||
foreach ($file as $f) {
|
||||
$files = array_merge($files, static::getFiles($f));
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('The first argument must be string or array');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns the content of a file.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function readFile($file)
|
||||
{
|
||||
$length = filesize($file);
|
||||
|
||||
if (!($fd = fopen($file, 'rb'))) {
|
||||
throw new Exception("Cannot read the file '$file', probably permissions");
|
||||
}
|
||||
|
||||
$content = $length ? fread($fd, $length) : '';
|
||||
fclose($fd);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
interface ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Extract the translations from a file.
|
||||
*
|
||||
* @param array|string $file A path of a file or files
|
||||
* @param Translations $translations The translations instance to append the new translations.
|
||||
* @param array $options
|
||||
*/
|
||||
public static function fromFile($file, Translations $translations, array $options = []);
|
||||
|
||||
/**
|
||||
* Parses a string and append the translations found in the Translations instance.
|
||||
*
|
||||
* @param string $string
|
||||
* @param Translations $translations
|
||||
* @param array $options
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = []);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
interface ExtractorMultiInterface
|
||||
{
|
||||
/**
|
||||
* Parses a string and append the translations found in the Translations instance.
|
||||
* Allows scanning for multiple domains at a time (each Translation has to have a different domain)
|
||||
*
|
||||
* @param string $string
|
||||
* @param Translations[] $translations
|
||||
* @param array $options
|
||||
*/
|
||||
public static function fromStringMultiple($string, array $translations, array $options = []);
|
||||
|
||||
/**
|
||||
* Parses a string and append the translations found in the Translations instance.
|
||||
* Allows scanning for multiple domains at a time (each Translation has to have a different domain)
|
||||
*
|
||||
* @param $file
|
||||
* @param Translations[] $translations
|
||||
* @param array $options
|
||||
*/
|
||||
public static function fromFileMultiple($file, array $translations, array $options = []);
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from json files.
|
||||
*/
|
||||
class Jed extends Extractor implements ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
static::extract(json_decode($string, true), $translations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an array of translations and append to the Translations instance.
|
||||
*
|
||||
* @param array $content
|
||||
* @param Translations $translations
|
||||
*/
|
||||
public static function extract(array $content, Translations $translations)
|
||||
{
|
||||
$messages = current($content);
|
||||
$headers = isset($messages['']) ? $messages[''] : null;
|
||||
unset($messages['']);
|
||||
|
||||
if (!empty($headers['domain'])) {
|
||||
$translations->setDomain($headers['domain']);
|
||||
}
|
||||
|
||||
if (!empty($headers['lang'])) {
|
||||
$translations->setLanguage($headers['lang']);
|
||||
}
|
||||
|
||||
if (!empty($headers['plural-forms'])) {
|
||||
$translations->setHeader(Translations::HEADER_PLURAL, $headers['plural-forms']);
|
||||
}
|
||||
|
||||
$context_glue = '\u0004';
|
||||
|
||||
foreach ($messages as $key => $translation) {
|
||||
$key = explode($context_glue, $key);
|
||||
$context = isset($key[1]) ? array_shift($key) : '';
|
||||
|
||||
$translations->insert($context, array_shift($key))
|
||||
->setTranslation(array_shift($translation))
|
||||
->setPluralTranslations($translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Exception;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\FunctionsScanner;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from javascript files.
|
||||
*/
|
||||
class JsCode extends Extractor implements ExtractorInterface, ExtractorMultiInterface
|
||||
{
|
||||
public static $options = [
|
||||
'constants' => [],
|
||||
|
||||
'functions' => [
|
||||
'gettext' => 'gettext',
|
||||
'__' => 'gettext',
|
||||
'ngettext' => 'ngettext',
|
||||
'n__' => 'ngettext',
|
||||
'pgettext' => 'pgettext',
|
||||
'p__' => 'pgettext',
|
||||
'dgettext' => 'dgettext',
|
||||
'd__' => 'dgettext',
|
||||
'dngettext' => 'dngettext',
|
||||
'dn__' => 'dngettext',
|
||||
'dpgettext' => 'dpgettext',
|
||||
'dp__' => 'dpgettext',
|
||||
'npgettext' => 'npgettext',
|
||||
'np__' => 'npgettext',
|
||||
'dnpgettext' => 'dnpgettext',
|
||||
'dnp__' => 'dnpgettext',
|
||||
'noop' => 'noop',
|
||||
'noop__' => 'noop',
|
||||
],
|
||||
];
|
||||
|
||||
protected static $functionsScannerClass = 'Gettext\Utils\JsFunctionsScanner';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
static::fromStringMultiple($string, [$translations], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromStringMultiple($string, array $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
/** @var FunctionsScanner $functions */
|
||||
$functions = new static::$functionsScannerClass($string);
|
||||
$functions->saveGettextFunctions($translations, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromFileMultiple($file, array $translations, array $options = [])
|
||||
{
|
||||
foreach (static::getFiles($file) as $file) {
|
||||
$options['file'] = $file;
|
||||
static::fromStringMultiple(static::readFile($file), $translations, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from json.
|
||||
*/
|
||||
class Json extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$messages = json_decode($string, true);
|
||||
|
||||
if (is_array($messages)) {
|
||||
static::fromArray($messages, $translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\DictionaryTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from plain json.
|
||||
*/
|
||||
class JsonDictionary extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use DictionaryTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$messages = json_decode($string, true);
|
||||
|
||||
if (is_array($messages)) {
|
||||
static::fromArray($messages, $translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Exception;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\StringReader;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from .mo files.
|
||||
*/
|
||||
class Mo extends Extractor implements ExtractorInterface
|
||||
{
|
||||
const MAGIC1 = -1794895138;
|
||||
const MAGIC2 = -569244523;
|
||||
const MAGIC3 = 2500072158;
|
||||
|
||||
protected static $stringReaderClass = 'Gettext\Utils\StringReader';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
/** @var StringReader $stream */
|
||||
$stream = new static::$stringReaderClass($string);
|
||||
$magic = static::readInt($stream, 'V');
|
||||
|
||||
if (($magic === static::MAGIC1) || ($magic === static::MAGIC3)) { //to make sure it works for 64-bit platforms
|
||||
$byteOrder = 'V'; //low endian
|
||||
} elseif ($magic === (static::MAGIC2 & 0xFFFFFFFF)) {
|
||||
$byteOrder = 'N'; //big endian
|
||||
} else {
|
||||
throw new Exception('Not MO file');
|
||||
}
|
||||
|
||||
static::readInt($stream, $byteOrder);
|
||||
|
||||
$total = static::readInt($stream, $byteOrder); //total string count
|
||||
$originals = static::readInt($stream, $byteOrder); //offset of original table
|
||||
$tran = static::readInt($stream, $byteOrder); //offset of translation table
|
||||
|
||||
$stream->seekto($originals);
|
||||
$table_originals = static::readIntArray($stream, $byteOrder, $total * 2);
|
||||
|
||||
$stream->seekto($tran);
|
||||
$table_translations = static::readIntArray($stream, $byteOrder, $total * 2);
|
||||
|
||||
for ($i = 0; $i < $total; ++$i) {
|
||||
$next = $i * 2;
|
||||
|
||||
$stream->seekto($table_originals[$next + 2]);
|
||||
$original = $stream->read($table_originals[$next + 1]);
|
||||
|
||||
$stream->seekto($table_translations[$next + 2]);
|
||||
$translated = $stream->read($table_translations[$next + 1]);
|
||||
|
||||
if ($original === '') {
|
||||
// Headers
|
||||
foreach (explode("\n", $translated) as $headerLine) {
|
||||
if ($headerLine === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$headerChunks = preg_split('/:\s*/', $headerLine, 2);
|
||||
$translations->setHeader($headerChunks[0], isset($headerChunks[1]) ? $headerChunks[1] : '');
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$chunks = explode("\x04", $original, 2);
|
||||
|
||||
if (isset($chunks[1])) {
|
||||
$context = $chunks[0];
|
||||
$original = $chunks[1];
|
||||
} else {
|
||||
$context = '';
|
||||
}
|
||||
|
||||
$chunks = explode("\x00", $original, 2);
|
||||
|
||||
if (isset($chunks[1])) {
|
||||
$original = $chunks[0];
|
||||
$plural = $chunks[1];
|
||||
} else {
|
||||
$plural = '';
|
||||
}
|
||||
|
||||
$translation = $translations->insert($context, $original, $plural);
|
||||
|
||||
if ($translated === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($plural === '') {
|
||||
$translation->setTranslation($translated);
|
||||
continue;
|
||||
}
|
||||
|
||||
$v = explode("\x00", $translated);
|
||||
$translation->setTranslation(array_shift($v));
|
||||
$translation->setPluralTranslations($v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StringReader $stream
|
||||
* @param string $byteOrder
|
||||
*/
|
||||
protected static function readInt(StringReader $stream, $byteOrder)
|
||||
{
|
||||
if (($read = $stream->read(4)) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$read = unpack($byteOrder, $read);
|
||||
|
||||
return array_shift($read);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StringReader $stream
|
||||
* @param string $byteOrder
|
||||
* @param int $count
|
||||
*/
|
||||
protected static function readIntArray(StringReader $stream, $byteOrder, $count)
|
||||
{
|
||||
return unpack($byteOrder.$count, $stream->read(4 * $count));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from php files returning arrays.
|
||||
*/
|
||||
class PhpArray extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromFile($file, Translations $translations, array $options = [])
|
||||
{
|
||||
foreach (static::getFiles($file) as $file) {
|
||||
static::fromArray(include($file), $translations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
throw new BadMethodCallException('PhpArray::fromString() cannot be called. Use PhpArray::fromFile()');
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Exception;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\FunctionsScanner;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from php files returning arrays.
|
||||
*/
|
||||
class PhpCode extends Extractor implements ExtractorInterface, ExtractorMultiInterface
|
||||
{
|
||||
public static $options = [
|
||||
// - false: to not extract comments
|
||||
// - empty string: to extract all comments
|
||||
// - non-empty string: to extract comments that start with that string
|
||||
// - array with strings to extract comments format.
|
||||
'extractComments' => false,
|
||||
|
||||
'constants' => [],
|
||||
|
||||
'functions' => [
|
||||
'gettext' => 'gettext',
|
||||
'__' => 'gettext',
|
||||
'ngettext' => 'ngettext',
|
||||
'n__' => 'ngettext',
|
||||
'pgettext' => 'pgettext',
|
||||
'p__' => 'pgettext',
|
||||
'dgettext' => 'dgettext',
|
||||
'd__' => 'dgettext',
|
||||
'dngettext' => 'dngettext',
|
||||
'dn__' => 'dngettext',
|
||||
'dpgettext' => 'dpgettext',
|
||||
'dp__' => 'dpgettext',
|
||||
'npgettext' => 'npgettext',
|
||||
'np__' => 'npgettext',
|
||||
'dnpgettext' => 'dnpgettext',
|
||||
'dnp__' => 'dnpgettext',
|
||||
'noop' => 'noop',
|
||||
'noop__' => 'noop',
|
||||
],
|
||||
];
|
||||
|
||||
protected static $functionsScannerClass = 'Gettext\Utils\PhpFunctionsScanner';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
static::fromStringMultiple($string, [$translations], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromStringMultiple($string, array $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
/** @var FunctionsScanner $functions */
|
||||
$functions = new static::$functionsScannerClass($string);
|
||||
|
||||
if ($options['extractComments'] !== false) {
|
||||
$functions->enableCommentsExtraction($options['extractComments']);
|
||||
}
|
||||
|
||||
$functions->saveGettextFunctions($translations, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function fromFileMultiple($file, array $translations, array $options = [])
|
||||
{
|
||||
foreach (static::getFiles($file) as $file) {
|
||||
$options['file'] = $file;
|
||||
static::fromStringMultiple(static::readFile($file), $translations, $options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a T_CONSTANT_ENCAPSED_STRING string.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertString($value)
|
||||
{
|
||||
if (strpos($value, '\\') === false) {
|
||||
return substr($value, 1, -1);
|
||||
}
|
||||
|
||||
if ($value[0] === "'") {
|
||||
return strtr(substr($value, 1, -1), ['\\\\' => '\\', '\\\'' => '\'']);
|
||||
}
|
||||
|
||||
$value = substr($value, 1, -1);
|
||||
|
||||
return preg_replace_callback(
|
||||
'/\\\(n|r|t|v|e|f|\$|"|\\\|x[0-9A-Fa-f]{1,2}|u{[0-9a-f]{1,6}}|[0-7]{1,3})/',
|
||||
function ($match) {
|
||||
switch ($match[1][0]) {
|
||||
case 'n':
|
||||
return "\n";
|
||||
case 'r':
|
||||
return "\r";
|
||||
case 't':
|
||||
return "\t";
|
||||
case 'v':
|
||||
return "\v";
|
||||
case 'e':
|
||||
return "\e";
|
||||
case 'f':
|
||||
return "\f";
|
||||
case '$':
|
||||
return '$';
|
||||
case '"':
|
||||
return '"';
|
||||
case '\\':
|
||||
return '\\';
|
||||
case 'x':
|
||||
return chr(hexdec(substr($match[1], 1)));
|
||||
case 'u':
|
||||
return static::unicodeChar(hexdec(substr($match[1], 1)));
|
||||
default:
|
||||
return chr(octdec($match[1]));
|
||||
}
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $dec
|
||||
* @return string|null
|
||||
* @see http://php.net/manual/en/function.chr.php#118804
|
||||
*/
|
||||
protected static function unicodeChar($dec)
|
||||
{
|
||||
if ($dec < 0x80) {
|
||||
return chr($dec);
|
||||
}
|
||||
|
||||
if ($dec < 0x0800) {
|
||||
return chr(0xC0 + ($dec >> 6))
|
||||
. chr(0x80 + ($dec & 0x3f));
|
||||
}
|
||||
|
||||
if ($dec < 0x010000) {
|
||||
return chr(0xE0 + ($dec >> 12))
|
||||
. chr(0x80 + (($dec >> 6) & 0x3f))
|
||||
. chr(0x80 + ($dec & 0x3f));
|
||||
}
|
||||
|
||||
if ($dec < 0x200000) {
|
||||
return chr(0xF0 + ($dec >> 18))
|
||||
. chr(0x80 + (($dec >> 12) & 0x3f))
|
||||
. chr(0x80 + (($dec >> 6) & 0x3f))
|
||||
. chr(0x80 + ($dec & 0x3f));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Translation;
|
||||
use Gettext\Utils\HeadersExtractorTrait;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from php files returning arrays.
|
||||
*/
|
||||
class Po extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use HeadersExtractorTrait;
|
||||
|
||||
/**
|
||||
* Parses a .po file and append the translations found in the Translations instance.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$lines = explode("\n", $string);
|
||||
$i = 0;
|
||||
|
||||
$translation = $translations->createNewTranslation('', '');
|
||||
|
||||
for ($n = count($lines); $i < $n; ++$i) {
|
||||
$line = trim($lines[$i]);
|
||||
$line = static::fixMultiLines($line, $lines, $i);
|
||||
|
||||
if ($line === '') {
|
||||
if ($translation->is('', '')) {
|
||||
static::extractHeaders($translation->getTranslation(), $translations);
|
||||
} elseif ($translation->hasOriginal()) {
|
||||
$translations[] = $translation;
|
||||
}
|
||||
|
||||
$translation = $translations->createNewTranslation('', '');
|
||||
continue;
|
||||
}
|
||||
|
||||
$splitLine = preg_split('/\s+/', $line, 2);
|
||||
$key = $splitLine[0];
|
||||
$data = isset($splitLine[1]) ? $splitLine[1] : '';
|
||||
|
||||
if ($key === '#~') {
|
||||
$translation->setDisabled(true);
|
||||
|
||||
$splitLine = preg_split('/\s+/', $data, 2);
|
||||
$key = $splitLine[0];
|
||||
$data = isset($splitLine[1]) ? $splitLine[1] : '';
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case '#':
|
||||
$translation->addComment($data);
|
||||
$append = null;
|
||||
break;
|
||||
|
||||
case '#.':
|
||||
$translation->addExtractedComment($data);
|
||||
$append = null;
|
||||
break;
|
||||
|
||||
case '#,':
|
||||
foreach (array_map('trim', explode(',', trim($data))) as $value) {
|
||||
$translation->addFlag($value);
|
||||
}
|
||||
$append = null;
|
||||
break;
|
||||
|
||||
case '#:':
|
||||
foreach (preg_split('/\s+/', trim($data)) as $value) {
|
||||
if (preg_match('/^(.+)(:(\d*))?$/U', $value, $matches)) {
|
||||
$translation->addReference($matches[1], isset($matches[3]) ? $matches[3] : null);
|
||||
}
|
||||
}
|
||||
$append = null;
|
||||
break;
|
||||
|
||||
case 'msgctxt':
|
||||
$translation = $translation->getClone(static::convertString($data));
|
||||
$append = 'Context';
|
||||
break;
|
||||
|
||||
case 'msgid':
|
||||
$translation = $translation->getClone(null, static::convertString($data));
|
||||
$append = 'Original';
|
||||
break;
|
||||
|
||||
case 'msgid_plural':
|
||||
$translation->setPlural(static::convertString($data));
|
||||
$append = 'Plural';
|
||||
break;
|
||||
|
||||
case 'msgstr':
|
||||
case 'msgstr[0]':
|
||||
$translation->setTranslation(static::convertString($data));
|
||||
$append = 'Translation';
|
||||
break;
|
||||
|
||||
case 'msgstr[1]':
|
||||
$translation->setPluralTranslations([static::convertString($data)]);
|
||||
$append = 'PluralTranslation';
|
||||
break;
|
||||
|
||||
default:
|
||||
if (strpos($key, 'msgstr[') === 0) {
|
||||
$p = $translation->getPluralTranslations();
|
||||
$p[] = static::convertString($data);
|
||||
|
||||
$translation->setPluralTranslations($p);
|
||||
$append = 'PluralTranslation';
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($append)) {
|
||||
if ($append === 'Context') {
|
||||
$translation = $translation->getClone($translation->getContext()
|
||||
."\n"
|
||||
.static::convertString($data));
|
||||
break;
|
||||
}
|
||||
|
||||
if ($append === 'Original') {
|
||||
$translation = $translation->getClone(null, $translation->getOriginal()
|
||||
."\n"
|
||||
.static::convertString($data));
|
||||
break;
|
||||
}
|
||||
|
||||
if ($append === 'PluralTranslation') {
|
||||
$p = $translation->getPluralTranslations();
|
||||
$p[] = array_pop($p)."\n".static::convertString($data);
|
||||
$translation->setPluralTranslations($p);
|
||||
break;
|
||||
}
|
||||
|
||||
$getMethod = 'get'.$append;
|
||||
$setMethod = 'set'.$append;
|
||||
$translation->$setMethod($translation->$getMethod()."\n".static::convertString($data));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($translation->hasOriginal() && !in_array($translation, iterator_to_array($translations))) {
|
||||
$translations[] = $translation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one string from multiline strings.
|
||||
*
|
||||
* @param string $line
|
||||
* @param array $lines
|
||||
* @param int &$i
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function fixMultiLines($line, array $lines, &$i)
|
||||
{
|
||||
for ($j = $i, $t = count($lines); $j < $t; ++$j) {
|
||||
if (substr($line, -1, 1) == '"'
|
||||
&& isset($lines[$j + 1])
|
||||
&& substr(trim($lines[$j + 1]), 0, 1) == '"'
|
||||
) {
|
||||
$line = substr($line, 0, -1).substr(trim($lines[$j + 1]), 1);
|
||||
} else {
|
||||
$i = $j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string from its PO representation.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertString($value)
|
||||
{
|
||||
if (!$value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($value[0] === '"') {
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
|
||||
return strtr(
|
||||
$value,
|
||||
[
|
||||
'\\\\' => '\\',
|
||||
'\\a' => "\x07",
|
||||
'\\b' => "\x08",
|
||||
'\\t' => "\t",
|
||||
'\\n' => "\n",
|
||||
'\\v' => "\x0b",
|
||||
'\\f' => "\x0c",
|
||||
'\\r' => "\r",
|
||||
'\\"' => '"',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Twig_Loader_Array;
|
||||
use Twig_Environment;
|
||||
use Twig_Source;
|
||||
use Twig_Extensions_Extension_I18n;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from twig files returning arrays.
|
||||
*/
|
||||
class Twig extends Extractor implements ExtractorInterface
|
||||
{
|
||||
public static $options = [
|
||||
'extractComments' => 'notes:',
|
||||
'twig' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
$twig = $options['twig'] ?: static::createTwig();
|
||||
|
||||
PhpCode::fromString($twig->compileSource(new Twig_Source($string, '')), $translations, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Twig instance.
|
||||
*
|
||||
* @return Twig_Environment
|
||||
*/
|
||||
protected static function createTwig()
|
||||
{
|
||||
$twig = new Twig_Environment(new Twig_Loader_Array(['' => '']));
|
||||
$twig->addExtension(new Twig_Extensions_Extension_I18n());
|
||||
|
||||
return static::$options['twig'] = $twig;
|
||||
}
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use DOMAttr;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use Exception;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\FunctionsScanner;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from VueJS template files.
|
||||
*/
|
||||
class VueJs extends Extractor implements ExtractorInterface, ExtractorMultiInterface
|
||||
{
|
||||
public static $options = [
|
||||
'constants' => [],
|
||||
|
||||
'functions' => [
|
||||
'gettext' => 'gettext',
|
||||
'__' => 'gettext',
|
||||
'ngettext' => 'ngettext',
|
||||
'n__' => 'ngettext',
|
||||
'pgettext' => 'pgettext',
|
||||
'p__' => 'pgettext',
|
||||
'dgettext' => 'dgettext',
|
||||
'd__' => 'dgettext',
|
||||
'dngettext' => 'dngettext',
|
||||
'dn__' => 'dngettext',
|
||||
'dpgettext' => 'dpgettext',
|
||||
'dp__' => 'dpgettext',
|
||||
'npgettext' => 'npgettext',
|
||||
'np__' => 'npgettext',
|
||||
'dnpgettext' => 'dnpgettext',
|
||||
'dnp__' => 'dnpgettext',
|
||||
'noop' => 'noop',
|
||||
'noop__' => 'noop',
|
||||
],
|
||||
];
|
||||
|
||||
protected static $functionsScannerClass = 'Gettext\Utils\JsFunctionsScanner';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromFileMultiple($file, array $translations, array $options = [])
|
||||
{
|
||||
foreach (static::getFiles($file) as $file) {
|
||||
$options['file'] = $file;
|
||||
static::fromStringMultiple(static::readFile($file), $translations, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
static::fromStringMultiple($string, [$translations], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromStringMultiple($string, array $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$options += [
|
||||
// HTML attribute prefixes we parse as JS which could contain translations (are JS expressions)
|
||||
'attributePrefixes' => [
|
||||
':',
|
||||
'v-bind:',
|
||||
'v-on:',
|
||||
'v-text',
|
||||
],
|
||||
// HTML Tags to parse
|
||||
'tagNames' => [
|
||||
'translate',
|
||||
],
|
||||
// HTML tags to parse when attribute exists
|
||||
'tagAttributes' => [
|
||||
'v-translate',
|
||||
],
|
||||
// Comments
|
||||
'commentAttributes' => [
|
||||
'translate-comment',
|
||||
],
|
||||
'contextAttributes' => [
|
||||
'translate-context',
|
||||
],
|
||||
// Attribute with plural content
|
||||
'pluralAttributes' => [
|
||||
'translate-plural',
|
||||
],
|
||||
];
|
||||
|
||||
// Ok, this is the weirdest hack, but let me explain:
|
||||
// On Linux (Mac is fine), when converting HTML to DOM, new lines get trimmed after the first tag.
|
||||
// So if there are new lines between <template> and next element, they are lost
|
||||
// So we insert a "." which is a text node, and it will prevent that newlines are stripped between elements.
|
||||
// Same thing happens between template and script tag.
|
||||
$string = str_replace('<template>', '<template>.', $string);
|
||||
$string = str_replace('</template>', '</template>.', $string);
|
||||
|
||||
// Normalize newlines
|
||||
$string = str_replace(["\r\n", "\n\r", "\r"], "\n", $string);
|
||||
|
||||
// VueJS files are valid HTML files, we will operate with the DOM here
|
||||
$dom = static::convertHtmlToDom($string);
|
||||
|
||||
$script = static::extractScriptTag($string);
|
||||
|
||||
// Parse the script part as a regular JS code
|
||||
if ($script) {
|
||||
$scriptLineNumber = $dom->getElementsByTagName('script')->item(0)->getLineNo();
|
||||
static::getScriptTranslationsFromString(
|
||||
$script,
|
||||
$translations,
|
||||
$options,
|
||||
$scriptLineNumber - 1
|
||||
);
|
||||
}
|
||||
|
||||
// Template part is parsed separately, all variables will be extracted
|
||||
// and handled as a regular JS code
|
||||
$template = $dom->getElementsByTagName('template')->item(0);
|
||||
if ($template) {
|
||||
static::getTemplateTranslations(
|
||||
$template,
|
||||
$translations,
|
||||
$options,
|
||||
$template->getLineNo() - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts script tag contents using regex instead of DOM operations.
|
||||
* If we parse using DOM, some contents may change, for example, tags within strings will be stripped
|
||||
*
|
||||
* @param $string
|
||||
* @return bool|string
|
||||
*/
|
||||
protected static function extractScriptTag($string)
|
||||
{
|
||||
if (preg_match('#<\s*?script\b[^>]*>(.*?)</script\b[^>]*>#s', $string, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $html
|
||||
* @return DOMDocument
|
||||
*/
|
||||
protected static function convertHtmlToDom($html)
|
||||
{
|
||||
$dom = new DOMDocument;
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
// Prepend xml encoding so DOMDocument document handles UTF8 correctly.
|
||||
// Assuming that vue template files will not have any xml encoding tags, because duplicate tags may be ignored.
|
||||
$dom->loadHTML('<?xml encoding="utf-8"?>' . $html);
|
||||
|
||||
libxml_clear_errors();
|
||||
|
||||
return $dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract translations from script part
|
||||
*
|
||||
* @param string $scriptContents Only script tag contents, not the whole template
|
||||
* @param Translations|Translations[] $translations One or multiple domain Translation objects
|
||||
* @param array $options
|
||||
* @param int $lineOffset Number of lines the script is offset in the vue template file
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function getScriptTranslationsFromString(
|
||||
$scriptContents,
|
||||
$translations,
|
||||
array $options = [],
|
||||
$lineOffset = 0
|
||||
) {
|
||||
/** @var FunctionsScanner $functions */
|
||||
$functions = new static::$functionsScannerClass($scriptContents);
|
||||
$options['lineOffset'] = $lineOffset;
|
||||
$functions->saveGettextFunctions($translations, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse template to extract all translations (element content and dynamic element attributes)
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @param Translations|Translations[] $translations One or multiple domain Translation objects
|
||||
* @param array $options
|
||||
* @param int $lineOffset Line number where the template part starts in the vue file
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function getTemplateTranslations(
|
||||
DOMNode $dom,
|
||||
$translations,
|
||||
array $options,
|
||||
$lineOffset = 0
|
||||
) {
|
||||
// Build a JS string from all template attribute expressions
|
||||
$fakeAttributeJs = static::getTemplateAttributeFakeJs($options, $dom);
|
||||
|
||||
// 1 line offset is necessary because parent template element was ignored when converting to DOM
|
||||
static::getScriptTranslationsFromString($fakeAttributeJs, $translations, $options, $lineOffset);
|
||||
|
||||
// Build a JS string from template element content expressions
|
||||
$fakeTemplateJs = static::getTemplateFakeJs($dom);
|
||||
static::getScriptTranslationsFromString($fakeTemplateJs, $translations, $options, $lineOffset);
|
||||
|
||||
static::getTagTranslations($options, $dom, $translations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @param DOMNode $dom
|
||||
* @param Translations|Translations[] $translations
|
||||
*/
|
||||
protected static function getTagTranslations(array $options, DOMNode $dom, $translations)
|
||||
{
|
||||
// Since tag scanning does not support domains, we always use the first translation given
|
||||
$translations = is_array($translations) ? reset($translations) : $translations;
|
||||
|
||||
$children = $dom->childNodes;
|
||||
for ($i = 0; $i < $children->length; $i++) {
|
||||
$node = $children->item($i);
|
||||
|
||||
if (!($node instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translatable = false;
|
||||
|
||||
if (in_array($node->tagName, $options['tagNames'], true)) {
|
||||
$translatable = true;
|
||||
}
|
||||
|
||||
$attrList = $node->attributes;
|
||||
$context = null;
|
||||
$plural = "";
|
||||
$comment = null;
|
||||
|
||||
for ($j = 0; $j < $attrList->length; $j++) {
|
||||
/** @var DOMAttr $domAttr */
|
||||
$domAttr = $attrList->item($j);
|
||||
// Check if this is a dynamic vue attribute
|
||||
if (in_array($domAttr->name, $options['tagAttributes'])) {
|
||||
$translatable = true;
|
||||
}
|
||||
if (in_array($domAttr->name, $options['contextAttributes'])) {
|
||||
$context = $domAttr->value;
|
||||
}
|
||||
if (in_array($domAttr->name, $options['pluralAttributes'])) {
|
||||
$plural = $domAttr->value;
|
||||
}
|
||||
if (in_array($domAttr->name, $options['commentAttributes'])) {
|
||||
$comment = $domAttr->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($translatable) {
|
||||
$translation = $translations->insert($context, trim($node->textContent), $plural);
|
||||
$translation->addReference($options['file'], $node->getLineNo());
|
||||
if ($comment) {
|
||||
$translation->addExtractedComment($comment);
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->hasChildNodes()) {
|
||||
static::getTagTranslations($options, $node, $translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JS expressions from element attribute bindings (excluding text within elements)
|
||||
* For example: <span :title="__('extract this')"> skip element content </span>
|
||||
*
|
||||
* @param array $options
|
||||
* @param DOMNode $dom
|
||||
* @return string JS code
|
||||
*/
|
||||
protected static function getTemplateAttributeFakeJs(array $options, DOMNode $dom)
|
||||
{
|
||||
$expressionsByLine = static::getVueAttributeExpressions($options['attributePrefixes'], $dom);
|
||||
|
||||
if (empty($expressionsByLine)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$maxLines = max(array_keys($expressionsByLine));
|
||||
$fakeJs = '';
|
||||
|
||||
for ($line = 1; $line <= $maxLines; $line++) {
|
||||
if (isset($expressionsByLine[$line])) {
|
||||
$fakeJs .= implode("; ", $expressionsByLine[$line]);
|
||||
}
|
||||
$fakeJs .= "\n";
|
||||
}
|
||||
|
||||
return $fakeJs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop DOM element recursively and parse out all dynamic vue attributes which are basically JS expressions
|
||||
*
|
||||
* @param array $attributePrefixes List of attribute prefixes we parse as JS (may contain translations)
|
||||
* @param DOMNode $dom
|
||||
* @param array $expressionByLine [lineNumber => [jsExpression, ..], ..]
|
||||
* @return array [lineNumber => [jsExpression, ..], ..]
|
||||
*/
|
||||
protected static function getVueAttributeExpressions(
|
||||
array $attributePrefixes,
|
||||
DOMNode $dom,
|
||||
array &$expressionByLine = []
|
||||
) {
|
||||
$children = $dom->childNodes;
|
||||
|
||||
for ($i = 0; $i < $children->length; $i++) {
|
||||
$node = $children->item($i);
|
||||
|
||||
if (!($node instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
$attrList = $node->attributes;
|
||||
|
||||
for ($j = 0; $j < $attrList->length; $j++) {
|
||||
/** @var DOMAttr $domAttr */
|
||||
$domAttr = $attrList->item($j);
|
||||
|
||||
// Check if this is a dynamic vue attribute
|
||||
if (static::isAttributeMatching($domAttr->name, $attributePrefixes)) {
|
||||
$line = $domAttr->getLineNo();
|
||||
$expressionByLine += [$line => []];
|
||||
$expressionByLine[$line][] = $domAttr->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->hasChildNodes()) {
|
||||
$expressionByLine = static::getVueAttributeExpressions($attributePrefixes, $node, $expressionByLine);
|
||||
}
|
||||
}
|
||||
|
||||
return $expressionByLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this attribute name should be parsed for translations
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string[] $attributePrefixes
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isAttributeMatching($attributeName, $attributePrefixes)
|
||||
{
|
||||
foreach ($attributePrefixes as $prefix) {
|
||||
if (strpos($attributeName, $prefix) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JS expressions from within template elements (excluding attributes)
|
||||
* For example: <span :title="skip attributes"> {{__("extract element content")}} </span>
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return string JS code
|
||||
*/
|
||||
protected static function getTemplateFakeJs(DOMNode $dom)
|
||||
{
|
||||
$fakeJs = '';
|
||||
$lines = explode("\n", $dom->textContent);
|
||||
|
||||
// Build a fake JS file from template by extracting JS expressions within each template line
|
||||
foreach ($lines as $line) {
|
||||
$expressionMatched = static::parseOneTemplateLine($line);
|
||||
|
||||
$fakeJs .= implode("; ", $expressionMatched) . "\n";
|
||||
}
|
||||
|
||||
return $fakeJs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match JS expressions in a template line
|
||||
*
|
||||
* @param string $line
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function parseOneTemplateLine($line)
|
||||
{
|
||||
$line = trim($line);
|
||||
|
||||
if (!$line) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$regex = '#\{\{(.*?)\}\}#';
|
||||
|
||||
preg_match_all($regex, $line, $matches);
|
||||
|
||||
$matched = array_map(function ($v) {
|
||||
return trim($v, '\'"{}');
|
||||
}, $matches[1]);
|
||||
|
||||
return $matched;
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Translation;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from xliff format.
|
||||
*/
|
||||
class Xliff extends Extractor implements ExtractorInterface
|
||||
{
|
||||
|
||||
public static $options = [
|
||||
'unitid_as_id' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
$xml = new SimpleXMLElement($string, null, false);
|
||||
|
||||
foreach ($xml->file as $file) {
|
||||
if (isset($file->notes)) {
|
||||
foreach ($file->notes->note as $note) {
|
||||
$translations->setHeader($note['id'], (string) $note);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file->unit as $unit) {
|
||||
foreach ($unit->segment as $segment) {
|
||||
$targets = [];
|
||||
|
||||
foreach ($segment->target as $target) {
|
||||
$targets[] = (string) $target;
|
||||
}
|
||||
|
||||
$translation = $translations->createNewTranslation(null, (string) $segment->source);
|
||||
if (isset($unit['id'])) {
|
||||
$unitId = (string) $unit['id'];
|
||||
$translation->addComment("XLIFF_UNIT_ID: $unitId");
|
||||
if ($options['unitid_as_id']) {
|
||||
$translation->setId($unitId);
|
||||
}
|
||||
}
|
||||
$translation->setTranslation(array_shift($targets));
|
||||
$translation->setPluralTranslations($targets);
|
||||
|
||||
if (isset($unit->notes)) {
|
||||
foreach ($unit->notes->note as $note) {
|
||||
switch ($note['category']) {
|
||||
case 'context':
|
||||
$translation = $translation->getClone((string) $note);
|
||||
break;
|
||||
|
||||
case 'extracted-comment':
|
||||
$translation->addExtractedComment((string) $note);
|
||||
break;
|
||||
|
||||
case 'flag':
|
||||
$translation->addFlag((string) $note);
|
||||
break;
|
||||
|
||||
case 'reference':
|
||||
$ref = explode(':', (string) $note, 2);
|
||||
$translation->addReference($ref[0], isset($ref[1]) ? $ref[1] : null);
|
||||
break;
|
||||
|
||||
default:
|
||||
$translation->addComment((string) $note);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$translations[] = $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
use Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from yaml.
|
||||
*/
|
||||
class Yaml extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$messages = YamlParser::parse($string);
|
||||
|
||||
if (is_array($messages)) {
|
||||
static::fromArray($messages, $translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Extractors;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\DictionaryTrait;
|
||||
use Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* Class to get gettext strings from yaml.
|
||||
*/
|
||||
class YamlDictionary extends Extractor implements ExtractorInterface
|
||||
{
|
||||
use DictionaryTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromString($string, Translations $translations, array $options = [])
|
||||
{
|
||||
$messages = YamlParser::parse($string);
|
||||
|
||||
if (is_array($messages)) {
|
||||
static::fromArray($messages, $translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\HeadersGeneratorTrait;
|
||||
use Gettext\Utils\CsvTrait;
|
||||
|
||||
/**
|
||||
* Class to export translations to csv.
|
||||
*/
|
||||
class Csv extends Generator implements GeneratorInterface
|
||||
{
|
||||
use HeadersGeneratorTrait;
|
||||
use CsvTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => false,
|
||||
'delimiter' => ",",
|
||||
'enclosure' => '"',
|
||||
'escape_char' => "\\"
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$handle = fopen('php://memory', 'w');
|
||||
|
||||
if ($options['includeHeaders']) {
|
||||
static::fputcsv($handle, ['', '', static::generateHeaders($translations)], $options);
|
||||
}
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
if ($translation->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line = [$translation->getContext(), $translation->getOriginal(), $translation->getTranslation()];
|
||||
|
||||
if ($translation->hasPluralTranslations(true)) {
|
||||
$line = array_merge($line, $translation->getPluralTranslations());
|
||||
}
|
||||
|
||||
static::fputcsv($handle, $line, $options);
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
$csv = stream_get_contents($handle);
|
||||
fclose($handle);
|
||||
|
||||
return $csv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\DictionaryTrait;
|
||||
use Gettext\Utils\CsvTrait;
|
||||
|
||||
class CsvDictionary extends Generator implements GeneratorInterface
|
||||
{
|
||||
use DictionaryTrait;
|
||||
use CsvTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => false,
|
||||
'delimiter' => ",",
|
||||
'enclosure' => '"',
|
||||
'escape_char' => "\\"
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$handle = fopen('php://memory', 'w');
|
||||
|
||||
foreach (static::toArray($translations, $options['includeHeaders']) as $original => $translation) {
|
||||
static::fputcsv($handle, [$original, $translation], $options);
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
$csv = stream_get_contents($handle);
|
||||
fclose($handle);
|
||||
|
||||
return $csv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
abstract class Generator implements GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toFile(Translations $translations, $file, array $options = [])
|
||||
{
|
||||
$content = static::toString($translations, $options);
|
||||
|
||||
if (file_put_contents($file, $content) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
interface GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Saves the translations in a file.
|
||||
*
|
||||
* @param Translations $translations
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function toFile(Translations $translations, $file, array $options = []);
|
||||
|
||||
/**
|
||||
* Generates a string with the translations ready to save in a file.
|
||||
*
|
||||
* @param Translations $translations
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = []);
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
class Jed extends Generator implements GeneratorInterface
|
||||
{
|
||||
public static $options = [
|
||||
'json' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$domain = $translations->getDomain() ?: 'messages';
|
||||
$options += static::$options;
|
||||
|
||||
return json_encode([
|
||||
$domain => [
|
||||
'' => [
|
||||
'domain' => $domain,
|
||||
'lang' => $translations->getLanguage() ?: 'en',
|
||||
'plural-forms' => $translations->getHeader('Plural-Forms') ?: 'nplurals=2; plural=(n != 1);',
|
||||
],
|
||||
] + static::buildMessages($translations),
|
||||
], $options['json']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array with all translations.
|
||||
*
|
||||
* @param Translations $translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function buildMessages(Translations $translations)
|
||||
{
|
||||
$pluralForm = $translations->getPluralForms();
|
||||
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
|
||||
$messages = [];
|
||||
$context_glue = '\u0004';
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
if ($translation->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = ($translation->hasContext() ? $translation->getContext().$context_glue : '')
|
||||
.$translation->getOriginal();
|
||||
|
||||
if ($translation->hasPluralTranslations(true)) {
|
||||
$message = $translation->getPluralTranslations($pluralSize);
|
||||
array_unshift($message, $translation->getTranslation());
|
||||
} else {
|
||||
$message = [$translation->getTranslation()];
|
||||
}
|
||||
|
||||
$messages[$key] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
|
||||
class Json extends Generator implements GeneratorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
public static $options = [
|
||||
'json' => 0,
|
||||
'includeHeaders' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
return json_encode(static::toArray($translations, $options['includeHeaders'], true), $options['json']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\DictionaryTrait;
|
||||
|
||||
class JsonDictionary extends Generator implements GeneratorInterface
|
||||
{
|
||||
use DictionaryTrait;
|
||||
|
||||
public static $options = [
|
||||
'json' => 0,
|
||||
'includeHeaders' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
return json_encode(static::toArray($translations, $options['includeHeaders']), $options['json']);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\HeadersGeneratorTrait;
|
||||
|
||||
class Mo extends Generator implements GeneratorInterface
|
||||
{
|
||||
use HeadersGeneratorTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
$messages = [];
|
||||
|
||||
if ($options['includeHeaders']) {
|
||||
$messages[''] = static::generateHeaders($translations);
|
||||
}
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
if (!$translation->hasTranslation() || $translation->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($translation->hasContext()) {
|
||||
$originalString = $translation->getContext()."\x04".$translation->getOriginal();
|
||||
} else {
|
||||
$originalString = $translation->getOriginal();
|
||||
}
|
||||
|
||||
$messages[$originalString] = $translation;
|
||||
}
|
||||
|
||||
ksort($messages);
|
||||
$numEntries = count($messages);
|
||||
$originalsTable = '';
|
||||
$translationsTable = '';
|
||||
$originalsIndex = [];
|
||||
$translationsIndex = [];
|
||||
$pluralForm = $translations->getPluralForms();
|
||||
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
|
||||
|
||||
foreach ($messages as $originalString => $translation) {
|
||||
if (is_string($translation)) {
|
||||
// Headers
|
||||
$translationString = $translation;
|
||||
} else {
|
||||
/* @var $translation \Gettext\Translation */
|
||||
if ($translation->hasPlural() && $translation->hasPluralTranslations(true)) {
|
||||
$originalString .= "\x00".$translation->getPlural();
|
||||
$translationString = $translation->getTranslation();
|
||||
$translationString .= "\x00".implode("\x00", $translation->getPluralTranslations($pluralSize));
|
||||
} else {
|
||||
$translationString = $translation->getTranslation();
|
||||
}
|
||||
}
|
||||
|
||||
$originalsIndex[] = [
|
||||
'relativeOffset' => strlen($originalsTable),
|
||||
'length' => strlen($originalString)
|
||||
];
|
||||
$originalsTable .= $originalString."\x00";
|
||||
$translationsIndex[] = [
|
||||
'relativeOffset' => strlen($translationsTable),
|
||||
'length' => strlen($translationString)
|
||||
];
|
||||
$translationsTable .= $translationString."\x00";
|
||||
}
|
||||
|
||||
// Offset of table with the original strings index: right after the header (which is 7 words)
|
||||
$originalsIndexOffset = 7 * 4;
|
||||
|
||||
// Size of table with the original strings index
|
||||
$originalsIndexSize = $numEntries * (4 + 4);
|
||||
|
||||
// Offset of table with the translation strings index: right after the original strings index table
|
||||
$translationsIndexOffset = $originalsIndexOffset + $originalsIndexSize;
|
||||
|
||||
// Size of table with the translation strings index
|
||||
$translationsIndexSize = $numEntries * (4 + 4);
|
||||
|
||||
// Hashing table starts after the header and after the index table
|
||||
$originalsStringsOffset = $translationsIndexOffset + $translationsIndexSize;
|
||||
|
||||
// Translations start after the keys
|
||||
$translationsStringsOffset = $originalsStringsOffset + strlen($originalsTable);
|
||||
|
||||
// Let's generate the .mo file binary data
|
||||
$mo = '';
|
||||
|
||||
// Magic number
|
||||
$mo .= pack('L', 0x950412de);
|
||||
|
||||
// File format revision
|
||||
$mo .= pack('L', 0);
|
||||
|
||||
// Number of strings
|
||||
$mo .= pack('L', $numEntries);
|
||||
|
||||
// Offset of table with original strings
|
||||
$mo .= pack('L', $originalsIndexOffset);
|
||||
|
||||
// Offset of table with translation strings
|
||||
$mo .= pack('L', $translationsIndexOffset);
|
||||
|
||||
// Size of hashing table: we don't use it.
|
||||
$mo .= pack('L', 0);
|
||||
|
||||
// Offset of hashing table: it would start right after the translations index table
|
||||
$mo .= pack('L', $translationsIndexOffset + $translationsIndexSize);
|
||||
|
||||
// Write the lengths & offsets of the original strings
|
||||
foreach ($originalsIndex as $info) {
|
||||
$mo .= pack('L', $info['length']);
|
||||
$mo .= pack('L', $originalsStringsOffset + $info['relativeOffset']);
|
||||
}
|
||||
|
||||
// Write the lengths & offsets of the translated strings
|
||||
foreach ($translationsIndex as $info) {
|
||||
$mo .= pack('L', $info['length']);
|
||||
$mo .= pack('L', $translationsStringsOffset + $info['relativeOffset']);
|
||||
}
|
||||
|
||||
// Write original strings
|
||||
$mo .= $originalsTable;
|
||||
|
||||
// Write translation strings
|
||||
$mo .= $translationsTable;
|
||||
|
||||
return $mo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
|
||||
class PhpArray extends Generator implements GeneratorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$array = static::generate($translations, $options);
|
||||
|
||||
return '<?php return '.var_export($array, true).';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array with the translations.
|
||||
*
|
||||
* @param Translations $translations
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function generate(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
return static::toArray($translations, $options['includeHeaders'], true);
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
class Po extends Generator implements GeneratorInterface
|
||||
{
|
||||
public static $options = [
|
||||
'noLocation' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@parentDoc}.
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
$pluralForm = $translations->getPluralForms();
|
||||
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
|
||||
$lines = ['msgid ""', 'msgstr ""'];
|
||||
|
||||
foreach ($translations->getHeaders() as $name => $value) {
|
||||
$lines[] = sprintf('"%s: %s\\n"', $name, $value);
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
|
||||
//Translations
|
||||
foreach ($translations as $translation) {
|
||||
if ($translation->hasComments()) {
|
||||
foreach ($translation->getComments() as $comment) {
|
||||
$lines[] = '# '.$comment;
|
||||
}
|
||||
}
|
||||
|
||||
if ($translation->hasExtractedComments()) {
|
||||
foreach ($translation->getExtractedComments() as $comment) {
|
||||
$lines[] = '#. '.$comment;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$options['noLocation'] && $translation->hasReferences()) {
|
||||
foreach ($translation->getReferences() as $reference) {
|
||||
$lines[] = '#: '.$reference[0].(!is_null($reference[1]) ? ':'.$reference[1] : null);
|
||||
}
|
||||
}
|
||||
|
||||
if ($translation->hasFlags()) {
|
||||
$lines[] = '#, '.implode(',', $translation->getFlags());
|
||||
}
|
||||
|
||||
$prefix = $translation->isDisabled() ? '#~ ' : '';
|
||||
|
||||
if ($translation->hasContext()) {
|
||||
$lines[] = $prefix.'msgctxt '.static::convertString($translation->getContext());
|
||||
}
|
||||
|
||||
static::addLines($lines, $prefix.'msgid', $translation->getOriginal());
|
||||
|
||||
if ($translation->hasPlural()) {
|
||||
static::addLines($lines, $prefix.'msgid_plural', $translation->getPlural());
|
||||
static::addLines($lines, $prefix.'msgstr[0]', $translation->getTranslation());
|
||||
|
||||
foreach ($translation->getPluralTranslations($pluralSize) as $k => $v) {
|
||||
static::addLines($lines, $prefix.'msgstr['.($k + 1).']', $v);
|
||||
}
|
||||
} else {
|
||||
static::addLines($lines, $prefix.'msgstr', $translation->getTranslation());
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
}
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes and adds double quotes to a string.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function multilineQuote($string)
|
||||
{
|
||||
$lines = explode("\n", $string);
|
||||
$last = count($lines) - 1;
|
||||
|
||||
foreach ($lines as $k => $line) {
|
||||
if ($k === $last) {
|
||||
$lines[$k] = static::convertString($line);
|
||||
} else {
|
||||
$lines[$k] = static::convertString($line."\n");
|
||||
}
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more lines depending whether the string is multiline or not.
|
||||
*
|
||||
* @param array &$lines
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*/
|
||||
protected static function addLines(array &$lines, $name, $value)
|
||||
{
|
||||
$newLines = static::multilineQuote($value);
|
||||
|
||||
if (count($newLines) === 1) {
|
||||
$lines[] = $name.' '.$newLines[0];
|
||||
} else {
|
||||
$lines[] = $name.' ""';
|
||||
|
||||
foreach ($newLines as $line) {
|
||||
$lines[] = $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to its PO representation.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertString($value)
|
||||
{
|
||||
return '"'.strtr(
|
||||
$value,
|
||||
[
|
||||
"\x00" => '',
|
||||
'\\' => '\\\\',
|
||||
"\t" => '\t',
|
||||
"\r" => '\r',
|
||||
"\n" => '\n',
|
||||
'"' => '\\"',
|
||||
]
|
||||
).'"';
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translation;
|
||||
use Gettext\Translations;
|
||||
use DOMDocument;
|
||||
|
||||
class Xliff extends Generator implements GeneratorInterface
|
||||
{
|
||||
const UNIT_ID_REGEXP = '/^XLIFF_UNIT_ID: (.*)$/';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$dom = new DOMDocument('1.0', 'utf-8');
|
||||
$dom->formatOutput = true;
|
||||
$xliff = $dom->appendChild($dom->createElement('xliff'));
|
||||
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
|
||||
$xliff->setAttribute('version', '2.0');
|
||||
$xliff->setAttribute('srcLang', $translations->getLanguage());
|
||||
$xliff->setAttribute('trgLang', $translations->getLanguage());
|
||||
$file = $xliff->appendChild($dom->createElement('file'));
|
||||
$file->setAttribute('id', $translations->getDomain().'.'.$translations->getLanguage());
|
||||
|
||||
//Save headers as notes
|
||||
$notes = $dom->createElement('notes');
|
||||
|
||||
foreach ($translations->getHeaders() as $name => $value) {
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $value))->setAttribute('id', $name);
|
||||
}
|
||||
|
||||
if ($notes->hasChildNodes()) {
|
||||
$file->appendChild($notes);
|
||||
}
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
//Find an XLIFF unit ID, if one is available; otherwise generate
|
||||
$unitId = static::getUnitID($translation)?:md5($translation->getContext().$translation->getOriginal());
|
||||
|
||||
$unit = $dom->createElement('unit');
|
||||
$unit->setAttribute('id', $unitId);
|
||||
|
||||
//Save comments as notes
|
||||
$notes = $dom->createElement('notes');
|
||||
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $translation->getContext()))
|
||||
->setAttribute('category', 'context');
|
||||
|
||||
foreach ($translation->getComments() as $comment) {
|
||||
//Skip XLIFF unit ID comments.
|
||||
if (preg_match(static::UNIT_ID_REGEXP, $comment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $comment))
|
||||
->setAttribute('category', 'comment');
|
||||
}
|
||||
|
||||
foreach ($translation->getExtractedComments() as $comment) {
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $comment))
|
||||
->setAttribute('category', 'extracted-comment');
|
||||
}
|
||||
|
||||
foreach ($translation->getFlags() as $flag) {
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $flag))
|
||||
->setAttribute('category', 'flag');
|
||||
}
|
||||
|
||||
foreach ($translation->getReferences() as $reference) {
|
||||
$notes->appendChild(static::createTextNode($dom, 'note', $reference[0].':'.$reference[1]))
|
||||
->setAttribute('category', 'reference');
|
||||
}
|
||||
|
||||
$unit->appendChild($notes);
|
||||
|
||||
$segment = $unit->appendChild($dom->createElement('segment'));
|
||||
$segment->appendChild(static::createTextNode($dom, 'source', $translation->getOriginal()));
|
||||
$segment->appendChild(static::createTextNode($dom, 'target', $translation->getTranslation()));
|
||||
|
||||
foreach ($translation->getPluralTranslations() as $plural) {
|
||||
if ($plural !== '') {
|
||||
$segment->appendChild(static::createTextNode($dom, 'target', $plural));
|
||||
}
|
||||
}
|
||||
|
||||
$file->appendChild($unit);
|
||||
}
|
||||
|
||||
return $dom->saveXML();
|
||||
}
|
||||
|
||||
protected static function createTextNode(DOMDocument $dom, $name, $string)
|
||||
{
|
||||
$node = $dom->createElement($name);
|
||||
$text = (preg_match('/[&<>]/', $string) === 1)
|
||||
? $dom->createCDATASection($string)
|
||||
: $dom->createTextNode($string);
|
||||
$node->appendChild($text);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translation's unit ID, if one is available.
|
||||
*
|
||||
* @param Translation $translation
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getUnitID(Translation $translation)
|
||||
{
|
||||
foreach ($translation->getComments() as $comment) {
|
||||
if (preg_match(static::UNIT_ID_REGEXP, $comment, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\MultidimensionalArrayTrait;
|
||||
use Symfony\Component\Yaml\Yaml as YamlDumper;
|
||||
|
||||
class Yaml extends Generator implements GeneratorInterface
|
||||
{
|
||||
use MultidimensionalArrayTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => false,
|
||||
'indent' => 2,
|
||||
'inline' => 4,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
return YamlDumper::dump(
|
||||
static::toArray($translations, $options['includeHeaders']),
|
||||
$options['inline'],
|
||||
$options['indent']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Generators;
|
||||
|
||||
use Gettext\Translations;
|
||||
use Gettext\Utils\DictionaryTrait;
|
||||
use Symfony\Component\Yaml\Yaml as YamlDumper;
|
||||
|
||||
class YamlDictionary extends Generator implements GeneratorInterface
|
||||
{
|
||||
use DictionaryTrait;
|
||||
|
||||
public static $options = [
|
||||
'includeHeaders' => false,
|
||||
'indent' => 2,
|
||||
'inline' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function toString(Translations $translations, array $options = [])
|
||||
{
|
||||
$options += static::$options;
|
||||
|
||||
return YamlDumper::dump(
|
||||
static::toArray($translations, $options['includeHeaders']),
|
||||
$options['inline'],
|
||||
$options['indent']
|
||||
);
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
class GettextTranslator extends BaseTranslator implements TranslatorInterface
|
||||
{
|
||||
/**
|
||||
* Constructor. Detects the current language using the environment variables.
|
||||
*
|
||||
* @param string $language
|
||||
*/
|
||||
public function __construct($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.
|
||||
*
|
||||
* @param string $language
|
||||
* @param int|null $category
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLanguage($language, $category = null)
|
||||
{
|
||||
if ($category === null) {
|
||||
$category = defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL;
|
||||
}
|
||||
|
||||
setlocale($category, $language);
|
||||
putenv('LANGUAGE='.$language);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a gettext domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $path
|
||||
* @param bool $default
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function loadDomain($domain, $path = null, $default = true)
|
||||
{
|
||||
bindtextdomain($domain, $path);
|
||||
bind_textdomain_codeset($domain, 'UTF-8');
|
||||
|
||||
if ($default) {
|
||||
textdomain($domain);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gettext($original)
|
||||
{
|
||||
return gettext($original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function ngettext($original, $plural, $value)
|
||||
{
|
||||
return ngettext($original, $plural, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dngettext($domain, $original, $plural, $value)
|
||||
{
|
||||
return dngettext($domain, $original, $plural, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function npgettext($context, $original, $plural, $value)
|
||||
{
|
||||
$message = $context."\x04".$original;
|
||||
$translation = ngettext($message, $plural, $value);
|
||||
|
||||
return ($translation === $message) ? $original : $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pgettext($context, $original)
|
||||
{
|
||||
$message = $context."\x04".$original;
|
||||
$translation = gettext($message);
|
||||
|
||||
return ($translation === $message) ? $original : $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dgettext($domain, $original)
|
||||
{
|
||||
return dgettext($domain, $original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dpgettext($domain, $context, $original)
|
||||
{
|
||||
$message = $context."\x04".$original;
|
||||
$translation = dgettext($domain, $message);
|
||||
|
||||
return ($translation === $message) ? $original : $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dnpgettext($domain, $context, $original, $plural, $value)
|
||||
{
|
||||
$message = $context."\x04".$original;
|
||||
$translation = dngettext($domain, $message, $plural, $value);
|
||||
|
||||
return ($translation === $message) ? $original : $translation;
|
||||
}
|
||||
}
|
||||
Vendored
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
/**
|
||||
* Static class with merge contants.
|
||||
*/
|
||||
final class Merge
|
||||
{
|
||||
const ADD = 1;
|
||||
const REMOVE = 2;
|
||||
|
||||
const HEADERS_ADD = 4;
|
||||
const HEADERS_REMOVE = 8;
|
||||
const HEADERS_OVERRIDE = 16;
|
||||
|
||||
const LANGUAGE_OVERRIDE = 32;
|
||||
const DOMAIN_OVERRIDE = 64;
|
||||
const TRANSLATION_OVERRIDE = 128;
|
||||
|
||||
const COMMENTS_OURS = 256;
|
||||
const COMMENTS_THEIRS = 512;
|
||||
|
||||
const EXTRACTED_COMMENTS_OURS = 1024;
|
||||
const EXTRACTED_COMMENTS_THEIRS = 2048;
|
||||
|
||||
const FLAGS_OURS = 4096;
|
||||
const FLAGS_THEIRS = 8192;
|
||||
|
||||
const REFERENCES_OURS = 16384;
|
||||
const REFERENCES_THEIRS = 32768;
|
||||
|
||||
const DEFAULTS = 5; //1 + 4
|
||||
|
||||
/**
|
||||
* Merge the flags of two translations.
|
||||
*
|
||||
* @param Translation $from
|
||||
* @param Translation $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeFlags(Translation $from, Translation $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::FLAGS_THEIRS) {
|
||||
$to->deleteFlags();
|
||||
}
|
||||
|
||||
if (!($options & self::FLAGS_OURS)) {
|
||||
foreach ($from->getFlags() as $flag) {
|
||||
$to->addFlag($flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the extracted comments of two translations.
|
||||
*
|
||||
* @param Translation $from
|
||||
* @param Translation $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeExtractedComments(Translation $from, Translation $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::EXTRACTED_COMMENTS_THEIRS) {
|
||||
$to->deleteExtractedComments();
|
||||
}
|
||||
|
||||
if (!($options & self::EXTRACTED_COMMENTS_OURS)) {
|
||||
foreach ($from->getExtractedComments() as $comment) {
|
||||
$to->addExtractedComment($comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the comments of two translations.
|
||||
*
|
||||
* @param Translation $from
|
||||
* @param Translation $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeComments(Translation $from, Translation $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::COMMENTS_THEIRS) {
|
||||
$to->deleteComments();
|
||||
}
|
||||
|
||||
if (!($options & self::COMMENTS_OURS)) {
|
||||
foreach ($from->getComments() as $comment) {
|
||||
$to->addComment($comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the references of two translations.
|
||||
*
|
||||
* @param Translation $from
|
||||
* @param Translation $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeReferences(Translation $from, Translation $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::REFERENCES_THEIRS) {
|
||||
$to->deleteReferences();
|
||||
}
|
||||
|
||||
if (!($options & self::REFERENCES_OURS)) {
|
||||
foreach ($from->getReferences() as $reference) {
|
||||
$to->addReference($reference[0], $reference[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the translations of two translations.
|
||||
*
|
||||
* @param Translation $from
|
||||
* @param Translation $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeTranslation(Translation $from, Translation $to, $options = self::DEFAULTS)
|
||||
{
|
||||
$override = (boolean) ($options & self::TRANSLATION_OVERRIDE);
|
||||
|
||||
if (!$to->hasTranslation() || ($from->hasTranslation() && $override)) {
|
||||
$to->setTranslation($from->getTranslation());
|
||||
}
|
||||
|
||||
if (!$to->hasPlural() || ($from->hasPlural() && $override)) {
|
||||
$to->setPlural($from->getPlural());
|
||||
}
|
||||
|
||||
if (!$to->hasPluralTranslations() || ($from->hasPluralTranslations() && $override)) {
|
||||
$to->setPluralTranslations($from->getPluralTranslations());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the translations of two translations.
|
||||
*
|
||||
* @param Translations $from
|
||||
* @param Translations $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeTranslations(Translations $from, Translations $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::REMOVE) {
|
||||
$filtered = [];
|
||||
|
||||
foreach ($to as $entry) {
|
||||
if ($from->find($entry)) {
|
||||
$filtered[$entry->getId()] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
$to->exchangeArray($filtered);
|
||||
}
|
||||
|
||||
foreach ($from as $entry) {
|
||||
if (($existing = $to->find($entry))) {
|
||||
$existing->mergeWith($entry, $options);
|
||||
} elseif ($options & self::ADD) {
|
||||
$to[] = $entry->getClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the headers of two translations.
|
||||
*
|
||||
* @param Translations $from
|
||||
* @param Translations $to
|
||||
* @param int $options
|
||||
*/
|
||||
public static function mergeHeaders(Translations $from, Translations $to, $options = self::DEFAULTS)
|
||||
{
|
||||
if ($options & self::HEADERS_REMOVE) {
|
||||
foreach (array_keys($to->getHeaders()) as $name) {
|
||||
if ($from->getHeader($name) === null) {
|
||||
$to->deleteHeader($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($from->getHeaders() as $name => $value) {
|
||||
$current = $to->getHeader($name);
|
||||
|
||||
if (empty($current)) {
|
||||
if ($options & self::HEADERS_ADD) {
|
||||
$to->setHeader($name, $value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($name) {
|
||||
case Translations::HEADER_LANGUAGE:
|
||||
case Translations::HEADER_PLURAL:
|
||||
if ($options & self::LANGUAGE_OVERRIDE) {
|
||||
$to->setHeader($name, $value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Translations::HEADER_DOMAIN:
|
||||
if ($options & self::DOMAIN_OVERRIDE) {
|
||||
$to->setHeader($name, $value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($options & self::HEADERS_OVERRIDE) {
|
||||
$to->setHeader($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+537
@@ -0,0 +1,537 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
/**
|
||||
* Class to manage a translation string.
|
||||
*/
|
||||
class Translation
|
||||
{
|
||||
protected $id;
|
||||
protected $context;
|
||||
protected $original;
|
||||
protected $translation = '';
|
||||
protected $plural;
|
||||
protected $pluralTranslation = [];
|
||||
protected $references = [];
|
||||
protected $comments = [];
|
||||
protected $extractedComments = [];
|
||||
protected $flags = [];
|
||||
protected $disabled = false;
|
||||
|
||||
/**
|
||||
* Generates the id of a translation (context + glue + original).
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateId($context, $original)
|
||||
{
|
||||
return "{$context}\004{$original}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of a Translation object.
|
||||
*
|
||||
* This is a factory method that will work even when Translation is extended.
|
||||
*
|
||||
* @param string $context The context of the translation
|
||||
* @param string $original The original string
|
||||
* @param string $plural The original plural string
|
||||
* @return static New Translation instance
|
||||
*/
|
||||
public static function create($context, $original, $plural = '')
|
||||
{
|
||||
return new static($context, $original, $plural);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param string $context The context of the translation
|
||||
* @param string $original The original string
|
||||
* @param string $plural The original plural string
|
||||
*/
|
||||
public function __construct($context, $original, $plural = '')
|
||||
{
|
||||
$this->context = (string) $context;
|
||||
$this->original = (string) $original;
|
||||
|
||||
$this->setPlural($plural);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones this translation.
|
||||
*
|
||||
* @param null|string $context Optional new context
|
||||
* @param null|string $original Optional new original
|
||||
*
|
||||
* @return Translation
|
||||
*/
|
||||
public function getClone($context = null, $original = null)
|
||||
{
|
||||
$new = clone $this;
|
||||
|
||||
if ($context !== null) {
|
||||
$new->context = (string) $context;
|
||||
}
|
||||
|
||||
if ($original !== null) {
|
||||
$new->original = (string) $original;
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of this translation.
|
||||
* @warning The use of this function to set a custom ID will prevent
|
||||
* Translations::find from matching this translation.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the id of this translation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
if ($this->id === null) {
|
||||
return static::generateId($this->context, $this->original);
|
||||
}
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the translation matches with the arguments.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is($context, $original = '')
|
||||
{
|
||||
return (($this->context === $context) && ($this->original === $original)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the translation
|
||||
*
|
||||
* @param bool $disabled
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDisabled($disabled)
|
||||
{
|
||||
$this->disabled = (bool) $disabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the translation is disabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled()
|
||||
{
|
||||
return $this->disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginal()
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the original string is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOriginal()
|
||||
{
|
||||
return ($this->original !== '') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the translation string.
|
||||
*
|
||||
* @param string $translation
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTranslation($translation)
|
||||
{
|
||||
$this->translation = (string) $translation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTranslation()
|
||||
{
|
||||
return $this->translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation string is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTranslation()
|
||||
{
|
||||
return ($this->translation !== '') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the plural translation string.
|
||||
*
|
||||
* @param string $plural
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPlural($plural)
|
||||
{
|
||||
$this->plural = (string) $plural;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plural translation string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPlural()
|
||||
{
|
||||
return $this->plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the plural translation string is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPlural()
|
||||
{
|
||||
return ($this->plural !== '') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new plural translation.
|
||||
*
|
||||
* @param array $plural
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPluralTranslations(array $plural)
|
||||
{
|
||||
$this->pluralTranslation = $plural;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all plural translations.
|
||||
*
|
||||
* @param int $size
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluralTranslations($size = null)
|
||||
{
|
||||
if ($size === null) {
|
||||
return $this->pluralTranslation;
|
||||
}
|
||||
|
||||
$current = count($this->pluralTranslation);
|
||||
|
||||
if ($size > $current) {
|
||||
return $this->pluralTranslation + array_fill(0, $size, '');
|
||||
}
|
||||
|
||||
if ($size < $current) {
|
||||
return array_slice($this->pluralTranslation, 0, $size);
|
||||
}
|
||||
|
||||
return $this->pluralTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any plural translation.
|
||||
*
|
||||
* @param bool $checkContent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPluralTranslations($checkContent = false)
|
||||
{
|
||||
if ($checkContent) {
|
||||
return implode('', $this->pluralTranslation) !== '';
|
||||
}
|
||||
|
||||
return !empty($this->pluralTranslation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all plural translations.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deletePluralTranslation()
|
||||
{
|
||||
$this->pluralTranslation = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context of this translation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the context is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasContext()
|
||||
{
|
||||
return (isset($this->context) && ($this->context !== '')) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new reference for this translation.
|
||||
*
|
||||
* @param string $filename The file path where the translation has been found
|
||||
* @param null|int $line The line number where the translation has been found
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addReference($filename, $line = null)
|
||||
{
|
||||
$key = "{$filename}:{$line}";
|
||||
$this->references[$key] = [$filename, $line];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation has any reference.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasReferences()
|
||||
{
|
||||
return !empty($this->references);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all references for this translation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReferences()
|
||||
{
|
||||
return array_values($this->references);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all references.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteReferences()
|
||||
{
|
||||
$this->references = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment for this translation.
|
||||
*
|
||||
* @param string $comment
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addComment($comment)
|
||||
{
|
||||
if (!in_array($comment, $this->comments, true)) {
|
||||
$this->comments[] = $comment;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation has any comment.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasComments()
|
||||
{
|
||||
return isset($this->comments[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all comments for this translation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getComments()
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all comments.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteComments()
|
||||
{
|
||||
$this->comments = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new extracted comment for this translation.
|
||||
*
|
||||
* @param string $comment
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addExtractedComment($comment)
|
||||
{
|
||||
if (!in_array($comment, $this->extractedComments, true)) {
|
||||
$this->extractedComments[] = $comment;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation has any extracted comment.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasExtractedComments()
|
||||
{
|
||||
return isset($this->extractedComments[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all extracted comments for this translation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtractedComments()
|
||||
{
|
||||
return $this->extractedComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all extracted comments.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteExtractedComments()
|
||||
{
|
||||
$this->extractedComments = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new flag for this translation.
|
||||
*
|
||||
* @param string $flag
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addFlag($flag)
|
||||
{
|
||||
if (!in_array($flag, $this->flags, true)) {
|
||||
$this->flags[] = $flag;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation has any flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFlags()
|
||||
{
|
||||
return isset($this->flags[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all extracted flags for this translation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all flags.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteFlags()
|
||||
{
|
||||
$this->flags = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this translation with other translation.
|
||||
*
|
||||
* @param Translation $translation The translation to merge with
|
||||
* @param int $options
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function mergeWith(Translation $translation, $options = Merge::DEFAULTS)
|
||||
{
|
||||
Merge::mergeTranslation($translation, $this, $options);
|
||||
Merge::mergeReferences($translation, $this, $options);
|
||||
Merge::mergeComments($translation, $this, $options);
|
||||
Merge::mergeExtractedComments($translation, $this, $options);
|
||||
Merge::mergeFlags($translation, $this, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
use Gettext\Languages\Language;
|
||||
use BadMethodCallException;
|
||||
use InvalidArgumentException;
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* Class to manage a collection of translations.
|
||||
*
|
||||
* @method static $this fromBladeFile(string $filename, array $options = [])
|
||||
* @method static $this fromBladeString(string $string, array $options = [])
|
||||
* @method $this addFromBladeFile(string $filename, array $options = [])
|
||||
* @method $this addFromBladeString(string $string, array $options = [])
|
||||
* @method static $this fromCsvFile(string $filename, array $options = [])
|
||||
* @method static $this fromCsvString(string $string, array $options = [])
|
||||
* @method $this addFromCsvFile(string $filename, array $options = [])
|
||||
* @method $this addFromCsvString(string $string, array $options = [])
|
||||
* @method bool toCsvFile(string $filename, array $options = [])
|
||||
* @method string toCsvString(array $options = [])
|
||||
* @method static $this fromCsvDictionaryFile(string $filename, array $options = [])
|
||||
* @method static $this fromCsvDictionaryString(string $string, array $options = [])
|
||||
* @method $this addFromCsvDictionaryFile(string $filename, array $options = [])
|
||||
* @method $this addFromCsvDictionaryString(string $string, array $options = [])
|
||||
* @method bool toCsvDictionaryFile(string $filename, array $options = [])
|
||||
* @method string toCsvDictionaryString(array $options = [])
|
||||
* @method static $this fromJedFile(string $filename, array $options = [])
|
||||
* @method static $this fromJedString(string $string, array $options = [])
|
||||
* @method $this addFromJedFile(string $filename, array $options = [])
|
||||
* @method $this addFromJedString(string $string, array $options = [])
|
||||
* @method bool toJedFile(string $filename, array $options = [])
|
||||
* @method string toJedString(array $options = [])
|
||||
* @method static $this fromJsCodeFile(string $filename, array $options = [])
|
||||
* @method static $this fromJsCodeString(string $string, array $options = [])
|
||||
* @method $this addFromJsCodeFile(string $filename, array $options = [])
|
||||
* @method $this addFromJsCodeString(string $string, array $options = [])
|
||||
* @method static $this fromJsonFile(string $filename, array $options = [])
|
||||
* @method static $this fromJsonString(string $string, array $options = [])
|
||||
* @method $this addFromJsonFile(string $filename, array $options = [])
|
||||
* @method $this addFromJsonString(string $string, array $options = [])
|
||||
* @method bool toJsonFile(string $filename, array $options = [])
|
||||
* @method string toJsonString(array $options = [])
|
||||
* @method static $this fromJsonDictionaryFile(string $filename, array $options = [])
|
||||
* @method static $this fromJsonDictionaryString(string $string, array $options = [])
|
||||
* @method $this addFromJsonDictionaryFile(string $filename, array $options = [])
|
||||
* @method $this addFromJsonDictionaryString(string $string, array $options = [])
|
||||
* @method bool toJsonDictionaryFile(string $filename, array $options = [])
|
||||
* @method string toJsonDictionaryString(array $options = [])
|
||||
* @method static $this fromMoFile(string $filename, array $options = [])
|
||||
* @method static $this fromMoString(string $string, array $options = [])
|
||||
* @method $this addFromMoFile(string $filename, array $options = [])
|
||||
* @method $this addFromMoString(string $string, array $options = [])
|
||||
* @method bool toMoFile(string $filename, array $options = [])
|
||||
* @method string toMoString(array $options = [])
|
||||
* @method static $this fromPhpArrayFile(string $filename, array $options = [])
|
||||
* @method static $this fromPhpArrayString(string $string, array $options = [])
|
||||
* @method $this addFromPhpArrayFile(string $filename, array $options = [])
|
||||
* @method $this addFromPhpArrayString(string $string, array $options = [])
|
||||
* @method bool toPhpArrayFile(string $filename, array $options = [])
|
||||
* @method string toPhpArrayString(array $options = [])
|
||||
* @method static $this fromPhpCodeFile(string $filename, array $options = [])
|
||||
* @method static $this fromPhpCodeString(string $string, array $options = [])
|
||||
* @method $this addFromPhpCodeFile(string $filename, array $options = [])
|
||||
* @method $this addFromPhpCodeString(string $string, array $options = [])
|
||||
* @method static $this fromPoFile(string $filename, array $options = [])
|
||||
* @method static $this fromPoString(string $string, array $options = [])
|
||||
* @method $this addFromPoFile(string $filename, array $options = [])
|
||||
* @method $this addFromPoString(string $string, array $options = [])
|
||||
* @method bool toPoFile(string $filename, array $options = [])
|
||||
* @method string toPoString(array $options = [])
|
||||
* @method static $this fromTwigFile(string $filename, array $options = [])
|
||||
* @method static $this fromTwigString(string $string, array $options = [])
|
||||
* @method $this addFromTwigFile(string $filename, array $options = [])
|
||||
* @method $this addFromTwigString(string $string, array $options = [])
|
||||
* @method static $this fromVueJsFile(string $filename, array $options = [])
|
||||
* @method static $this fromVueJsString(string $filename, array $options = [])
|
||||
* @method $this addFromVueJsFile(string $filename, array $options = [])
|
||||
* @method $this addFromVueJsString(string $filename, array $options = [])
|
||||
* @method static $this fromXliffFile(string $filename, array $options = [])
|
||||
* @method static $this fromXliffString(string $string, array $options = [])
|
||||
* @method $this addFromXliffFile(string $filename, array $options = [])
|
||||
* @method $this addFromXliffString(string $string, array $options = [])
|
||||
* @method bool toXliffFile(string $filename, array $options = [])
|
||||
* @method string toXliffString(array $options = [])
|
||||
* @method static $this fromYamlFile(string $filename, array $options = [])
|
||||
* @method static $this fromYamlString(string $string, array $options = [])
|
||||
* @method $this addFromYamlFile(string $filename, array $options = [])
|
||||
* @method $this addFromYamlString(string $string, array $options = [])
|
||||
* @method bool toYamlFile(string $filename, array $options = [])
|
||||
* @method string toYamlString(array $options = [])
|
||||
* @method static $this fromYamlDictionaryFile(string $filename, array $options = [])
|
||||
* @method static $this fromYamlDictionaryString(string $string, array $options = [])
|
||||
* @method $this addFromYamlDictionaryFile(string $filename, array $options = [])
|
||||
* @method $this addFromYamlDictionaryString(string $string, array $options = [])
|
||||
* @method bool toYamlDictionaryFile(string $filename, array $options = [])
|
||||
* @method string toYamlDictionaryString(array $options = [])
|
||||
*/
|
||||
class Translations extends ArrayObject
|
||||
{
|
||||
const HEADER_LANGUAGE = 'Language';
|
||||
const HEADER_PLURAL = 'Plural-Forms';
|
||||
const HEADER_DOMAIN = 'X-Domain';
|
||||
|
||||
public static $options = [
|
||||
'defaultHeaders' => [
|
||||
'Project-Id-Version' => '',
|
||||
'Report-Msgid-Bugs-To' => '',
|
||||
'Last-Translator' => '',
|
||||
'Language-Team' => '',
|
||||
'MIME-Version' => '1.0',
|
||||
'Content-Type' => 'text/plain; charset=UTF-8',
|
||||
'Content-Transfer-Encoding' => '8bit',
|
||||
],
|
||||
'headersSorting' => false,
|
||||
'defaultDateHeaders' => [
|
||||
'POT-Creation-Date',
|
||||
'PO-Revision-Date',
|
||||
],
|
||||
];
|
||||
|
||||
protected $headers;
|
||||
|
||||
protected $translationClass;
|
||||
|
||||
/**
|
||||
* @see ArrayObject::__construct()
|
||||
*/
|
||||
public function __construct(
|
||||
$input = [],
|
||||
$flags = 0,
|
||||
$iterator_class = 'ArrayIterator',
|
||||
$translationClass = 'Gettext\Translation'
|
||||
) {
|
||||
$this->headers = static::$options['defaultHeaders'];
|
||||
|
||||
foreach (static::$options['defaultDateHeaders'] as $header) {
|
||||
$this->headers[$header] = date('c');
|
||||
}
|
||||
|
||||
$this->headers[self::HEADER_LANGUAGE] = '';
|
||||
|
||||
$this->translationClass = $translationClass;
|
||||
|
||||
parent::__construct($input, $flags, $iterator_class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to create new instances using extractors
|
||||
* For example: Translations::fromMoFile($filename, $options);.
|
||||
*
|
||||
* @return Translations
|
||||
*/
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
if (!preg_match('/^from(\w+)(File|String)$/i', $name, $matches)) {
|
||||
throw new BadMethodCallException("The method $name does not exists");
|
||||
}
|
||||
|
||||
return call_user_func_array([new static(), 'add'.ucfirst($name)], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to import/export the translations to a specific format
|
||||
* For example: $translations->toMoFile($filename, $options);
|
||||
* For example: $translations->addFromMoFile($filename, $options);.
|
||||
*
|
||||
* @return self|bool
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (!preg_match('/^(addFrom|to)(\w+)(File|String)$/i', $name, $matches)) {
|
||||
throw new BadMethodCallException("The method $name does not exists");
|
||||
}
|
||||
|
||||
if ($matches[1] === 'addFrom') {
|
||||
$extractor = 'Gettext\\Extractors\\'.$matches[2].'::from'.$matches[3];
|
||||
$source = array_shift($arguments);
|
||||
$options = array_shift($arguments) ?: [];
|
||||
|
||||
call_user_func($extractor, $source, $this, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$generator = 'Gettext\\Generators\\'.$matches[2].'::to'.$matches[3];
|
||||
|
||||
array_unshift($arguments, $this);
|
||||
|
||||
return call_user_func_array($generator, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to clone each translation on clone the translations object.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($this as $key => $translation) {
|
||||
$array[$key] = clone $translation;
|
||||
}
|
||||
|
||||
$this->exchangeArray($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the new translations added.
|
||||
*
|
||||
* @param mixed $index
|
||||
* @param Translation $value
|
||||
*
|
||||
* @throws InvalidArgumentException If the value is not an instance of Gettext\Translation
|
||||
*
|
||||
* @return Translation
|
||||
*/
|
||||
public function offsetSet($index, $value)
|
||||
{
|
||||
if (!($value instanceof Translation)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Only instances of Gettext\\Translation must be added to a Gettext\\Translations'
|
||||
);
|
||||
}
|
||||
|
||||
$id = $value->getId();
|
||||
|
||||
if ($this->offsetExists($id)) {
|
||||
$this[$id]->mergeWith($value);
|
||||
|
||||
return $this[$id];
|
||||
}
|
||||
|
||||
parent::offsetSet($id, $value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plural definition.
|
||||
*
|
||||
* @param int $count
|
||||
* @param string $rule
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPluralForms($count, $rule)
|
||||
{
|
||||
if (preg_match('/[a-z]/i', str_replace('n', '', $rule))) {
|
||||
throw new \InvalidArgumentException('Invalid Plural form: ' . $rule);
|
||||
}
|
||||
$this->setHeader(self::HEADER_PLURAL, "nplurals={$count}; plural={$rule};");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parsed plural definition.
|
||||
*
|
||||
* @param null|array [count, rule]
|
||||
*/
|
||||
public function getPluralForms()
|
||||
{
|
||||
$header = $this->getHeader(self::HEADER_PLURAL);
|
||||
|
||||
if (!empty($header)
|
||||
&& preg_match('/^nplurals\s*=\s*(\d+)\s*;\s*plural\s*=\s*([^;]+)\s*;$/', $header, $matches)
|
||||
) {
|
||||
return [intval($matches[1]), $matches[2]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new header.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setHeader($name, $value)
|
||||
{
|
||||
$name = trim($name);
|
||||
$this->headers[$name] = trim($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header value.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getHeader($name)
|
||||
{
|
||||
return isset($this->headers[$name]) ? $this->headers[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all header for this translations (in alphabetic order).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
if (static::$options['headersSorting']) {
|
||||
ksort($this->headers);
|
||||
}
|
||||
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all headers.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteHeaders()
|
||||
{
|
||||
$this->headers = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one header.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function deleteHeader($name)
|
||||
{
|
||||
unset($this->headers[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language value.
|
||||
*
|
||||
* @return string $language
|
||||
*/
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->getHeader(self::HEADER_LANGUAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language and the plural forms.
|
||||
*
|
||||
* @param string $language
|
||||
*
|
||||
* @throws InvalidArgumentException if the language hasn't been recognized
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLanguage($language)
|
||||
{
|
||||
$this->setHeader(self::HEADER_LANGUAGE, trim($language));
|
||||
|
||||
if (($info = Language::getById($language))) {
|
||||
return $this->setPluralForms(count($info->categories), $info->formula);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The language "%s" is not valid', $language));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the language is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLanguage()
|
||||
{
|
||||
$language = $this->getLanguage();
|
||||
|
||||
return (is_string($language) && ($language !== '')) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new domain for this translations.
|
||||
*
|
||||
* @param string $domain
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDomain($domain)
|
||||
{
|
||||
$this->setHeader(self::HEADER_DOMAIN, trim($domain));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDomain()
|
||||
{
|
||||
return $this->getHeader(self::HEADER_DOMAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the domain is empty or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDomain()
|
||||
{
|
||||
$domain = $this->getDomain();
|
||||
|
||||
return (is_string($domain) && ($domain !== '')) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a specific translation.
|
||||
*
|
||||
* @param string|Translation $context The context of the translation or a translation instance
|
||||
* @param string $original The original string
|
||||
* @warning Translations with custom identifiers (e.g. XLIFF unit IDs) cannot be found using this function.
|
||||
*
|
||||
* @return Translation|false
|
||||
*/
|
||||
public function find($context, $original = '')
|
||||
{
|
||||
if ($context instanceof Translation) {
|
||||
$id = $context->getId();
|
||||
} else {
|
||||
$id = Translation::generateId($context, $original);
|
||||
}
|
||||
|
||||
return $this->offsetExists($id) ? $this[$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all elements translated
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function countTranslated()
|
||||
{
|
||||
$c = 0;
|
||||
foreach ($this as $v) {
|
||||
if ($v->hasTranslation()) {
|
||||
$c++;
|
||||
}
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and insert/merges a new translation.
|
||||
*
|
||||
* @param string $context The translation context
|
||||
* @param string $original The translation original string
|
||||
* @param string $plural The translation original plural string
|
||||
*
|
||||
* @return Translation The translation created
|
||||
*/
|
||||
public function insert($context, $original, $plural = '')
|
||||
{
|
||||
return $this->offsetSet(null, $this->createNewTranslation($context, $original, $plural));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this translations with other translations.
|
||||
*
|
||||
* @param Translations $translations The translations instance to merge with
|
||||
* @param int $options
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function mergeWith(Translations $translations, $options = Merge::DEFAULTS)
|
||||
{
|
||||
Merge::mergeHeaders($translations, $this, $options);
|
||||
Merge::mergeTranslations($translations, $this, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of a Translation object.
|
||||
*
|
||||
* @param string $context The context of the translation
|
||||
* @param string $original The original string
|
||||
* @param string $plural The original plural string
|
||||
* @return Translation New Translation instance
|
||||
*/
|
||||
public function createNewTranslation($context, $original, $plural = '')
|
||||
{
|
||||
$class = $this->translationClass;
|
||||
return $class::create($context, $original, $plural);
|
||||
}
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
use Gettext\Generators\PhpArray;
|
||||
|
||||
class Translator extends BaseTranslator implements TranslatorInterface
|
||||
{
|
||||
protected $domain;
|
||||
protected $dictionary = [];
|
||||
protected $plurals = [];
|
||||
|
||||
/**
|
||||
* Loads translation from a Translations instance, a file on an array.
|
||||
*
|
||||
* @param Translations|string|array $translations
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function loadTranslations($translations)
|
||||
{
|
||||
if (is_object($translations) && $translations instanceof Translations) {
|
||||
$translations = PhpArray::generate($translations, ['includeHeaders' => false]);
|
||||
} elseif (is_string($translations) && is_file($translations)) {
|
||||
$translations = include $translations;
|
||||
} elseif (!is_array($translations)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid Translator: only arrays, files or instance of Translations are allowed'
|
||||
);
|
||||
}
|
||||
|
||||
$this->addTranslations($translations);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default domain.
|
||||
*
|
||||
* @param string $domain
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function defaultDomain($domain)
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gettext($original)
|
||||
{
|
||||
return $this->dpgettext($this->domain, null, $original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function ngettext($original, $plural, $value)
|
||||
{
|
||||
return $this->dnpgettext($this->domain, null, $original, $plural, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dngettext($domain, $original, $plural, $value)
|
||||
{
|
||||
return $this->dnpgettext($domain, null, $original, $plural, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function npgettext($context, $original, $plural, $value)
|
||||
{
|
||||
return $this->dnpgettext($this->domain, $context, $original, $plural, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pgettext($context, $original)
|
||||
{
|
||||
return $this->dpgettext($this->domain, $context, $original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dgettext($domain, $original)
|
||||
{
|
||||
return $this->dpgettext($domain, null, $original);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dpgettext($domain, $context, $original)
|
||||
{
|
||||
$translation = $this->getTranslation($domain, $context, $original);
|
||||
|
||||
if (isset($translation[0]) && $translation[0] !== '') {
|
||||
return $translation[0];
|
||||
}
|
||||
|
||||
return $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dnpgettext($domain, $context, $original, $plural, $value)
|
||||
{
|
||||
$translation = $this->getTranslation($domain, $context, $original);
|
||||
$key = $this->getPluralIndex($domain, $value, $translation === false);
|
||||
|
||||
if (isset($translation[$key]) && $translation[$key] !== '') {
|
||||
return $translation[$key];
|
||||
}
|
||||
|
||||
return ($key === 0) ? $original : $plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new translations to the dictionary.
|
||||
*
|
||||
* @param array $translations
|
||||
*/
|
||||
protected function addTranslations(array $translations)
|
||||
{
|
||||
$domain = isset($translations['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;
|
||||
}
|
||||
|
||||
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' => str_replace('plural=', 'return ', str_replace('n', '$n', $code)).';',
|
||||
];
|
||||
}
|
||||
|
||||
$this->dictionary[$domain] = $translations['messages'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and returns a translation.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
protected function getTranslation($domain, $context, $original)
|
||||
{
|
||||
return isset($this->dictionary[$domain][$context][$original])
|
||||
? $this->dictionary[$domain][$context][$original]
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the plural decision code given the number to decide which
|
||||
* plural version to take.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $n
|
||||
* @param bool $fallback set to true to get fallback plural index
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getPluralIndex($domain, $n, $fallback)
|
||||
{
|
||||
//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 = static::fixTerseIfs($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 will recursively wrap failure states in brackets if they contain a nested terse if.
|
||||
*
|
||||
* This because PHP can not handle nested terse if's unless they are wrapped in brackets.
|
||||
*
|
||||
* This code probably only works for the gettext plural decision codes.
|
||||
*
|
||||
* return ($n==1 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2);
|
||||
* becomes
|
||||
* return ($n==1 ? 0 : ($n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2));
|
||||
*
|
||||
* @param string $code the terse if string
|
||||
* @param bool $inner If inner is true we wrap it in brackets
|
||||
*
|
||||
* @return string A formatted terse If that PHP can work with.
|
||||
*/
|
||||
private static function fixTerseIfs($code, $inner = false)
|
||||
{
|
||||
/*
|
||||
* (?P<expression>[^?]+) Capture everything up to ? as 'expression'
|
||||
* \? ?
|
||||
* (?P<success>[^:]+) Capture everything up to : as 'success'
|
||||
* : :
|
||||
* (?P<failure>[^;]+) Capture everything up to ; as 'failure'
|
||||
*/
|
||||
preg_match('/(?P<expression>[^?]+)\?(?P<success>[^:]+):(?P<failure>[^;]+)/', $code, $matches);
|
||||
|
||||
// If no match was found then no terse if was present
|
||||
if (!isset($matches[0])) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
$expression = $matches['expression'];
|
||||
$success = $matches['success'];
|
||||
$failure = $matches['failure'];
|
||||
|
||||
// Go look for another terse if in the failure state.
|
||||
$failure = static::fixTerseIfs($failure, true);
|
||||
$code = $expression.' ? '.$success.' : '.$failure;
|
||||
|
||||
if ($inner) {
|
||||
return "($code)";
|
||||
}
|
||||
|
||||
// note the semicolon. We need that for executing the code.
|
||||
return "$code;";
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext;
|
||||
|
||||
/**
|
||||
* Interface used by all translators.
|
||||
*/
|
||||
interface TranslatorInterface
|
||||
{
|
||||
/**
|
||||
* Register this translator as global, to use with the gettext functions __(), p__(), etc.
|
||||
* Returns the previous translator if exists.
|
||||
*
|
||||
* @return TranslatorInterface|null
|
||||
*/
|
||||
public function register();
|
||||
|
||||
/**
|
||||
* Noop, marks the string for translation but returns it unchanged.
|
||||
*
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function noop($original);
|
||||
|
||||
/**
|
||||
* Gets a translation using the original string.
|
||||
*
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function gettext($original);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the plural form.
|
||||
*
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function ngettext($original, $plural, $value);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the domain and the plural form.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dngettext($domain, $original, $plural, $value);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the context and the plural form.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function npgettext($context, $original, $plural, $value);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the context.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pgettext($context, $original);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dgettext($domain, $original);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the domain and context.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dpgettext($domain, $context, $original);
|
||||
|
||||
/**
|
||||
* Gets a translation checking the domain, the context and the plural form.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*/
|
||||
public function dnpgettext($domain, $context, $original, $plural, $value);
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
/*
|
||||
* Trait to provide the functionality of read/write csv.
|
||||
*/
|
||||
trait CsvTrait
|
||||
{
|
||||
protected static $csvEscapeChar;
|
||||
|
||||
/**
|
||||
* Check whether support the escape_char argument to fgetcsv/fputcsv or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function supportsCsvEscapeChar()
|
||||
{
|
||||
if (static::$csvEscapeChar === null) {
|
||||
static::$csvEscapeChar = version_compare(PHP_VERSION, '5.5.4') >= 0;
|
||||
}
|
||||
|
||||
return static::$csvEscapeChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function fgetcsv($handle, $options)
|
||||
{
|
||||
if (static::supportsCsvEscapeChar()) {
|
||||
return fgetcsv($handle, 0, $options['delimiter'], $options['enclosure'], $options['escape_char']);
|
||||
}
|
||||
|
||||
return fgetcsv($handle, 0, $options['delimiter'], $options['enclosure']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @param array $fields
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
protected static function fputcsv($handle, $fields, $options)
|
||||
{
|
||||
if (static::supportsCsvEscapeChar()) {
|
||||
return fputcsv($handle, $fields, $options['delimiter'], $options['enclosure'], $options['escape_char']);
|
||||
}
|
||||
|
||||
return fputcsv($handle, $fields, $options['delimiter'], $options['enclosure']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
/**
|
||||
* Trait used by all generators that exports the translations to plain dictionary (original => singular-translation).
|
||||
*/
|
||||
trait DictionaryTrait
|
||||
{
|
||||
use HeadersGeneratorTrait;
|
||||
use HeadersExtractorTrait;
|
||||
|
||||
/**
|
||||
* Returns a plain dictionary with the format [original => translation].
|
||||
*
|
||||
* @param Translations $translations
|
||||
* @param bool $includeHeaders
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function toArray(Translations $translations, $includeHeaders)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if ($includeHeaders) {
|
||||
$messages[''] = static::generateHeaders($translations);
|
||||
}
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
if ($translation->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$messages[$translation->getOriginal()] = $translation->getTranslation();
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the entries from a dictionary.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param Translations $translations
|
||||
*/
|
||||
protected static function fromArray(array $messages, Translations $translations)
|
||||
{
|
||||
foreach ($messages as $original => $translation) {
|
||||
if ($original === '') {
|
||||
static::extractHeaders($translation, $translations);
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations->insert(null, $original)->setTranslation($translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Exception;
|
||||
use Gettext\Translations;
|
||||
|
||||
abstract class FunctionsScanner
|
||||
{
|
||||
/**
|
||||
* Scan and returns the functions and the arguments.
|
||||
*
|
||||
* @param array $constants Constants used in the code to replace
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFunctions(array $constants = []);
|
||||
|
||||
/**
|
||||
* Search for specific functions and create translations.
|
||||
*
|
||||
* You can pass multiple translation with different domains and value found will be sorted respectively.
|
||||
*
|
||||
* @param Translations|Translations[] $translations Multiple domain translations instances where to save the values
|
||||
* @param array $options The extractor options
|
||||
* @throws Exception
|
||||
*/
|
||||
public function saveGettextFunctions($translations, array $options)
|
||||
{
|
||||
$translations = is_array($translations) ? $translations : [$translations];
|
||||
|
||||
/** @var Translations[] $translationByDomain [domain => translations, ..] */
|
||||
$translationByDomain = array_reduce($translations, function (&$carry, Translations $translations) {
|
||||
$carry[$translations->getDomain()] = $translations;
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
$functions = $options['functions'];
|
||||
$file = $options['file'];
|
||||
|
||||
foreach ($this->getFunctions($options['constants']) as $function) {
|
||||
list($name, $line, $args) = $function;
|
||||
|
||||
if (isset($options['lineOffset'])) {
|
||||
$line += $options['lineOffset'];
|
||||
}
|
||||
|
||||
if (!isset($functions[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$deconstructed = $this->deconstructArgs($functions[$name], $args);
|
||||
|
||||
if (!$deconstructed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($domain, $context, $original, $plural) = $deconstructed;
|
||||
|
||||
if ((string)$original === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isDefaultDomain = $domain === null;
|
||||
|
||||
$domainTranslations = isset($translationByDomain[$domain]) ? $translationByDomain[$domain] : false;
|
||||
|
||||
if (!empty($options['domainOnly']) && $isDefaultDomain) {
|
||||
// If we want to find translations for a specific domain, skip default domain messages
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$domainTranslations) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation = $domainTranslations->insert($context, $original, $plural);
|
||||
$translation->addReference($file, $line);
|
||||
|
||||
if (isset($function[3])) {
|
||||
foreach ($function[3] as $extractedComment) {
|
||||
$translation->addExtractedComment($extractedComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deconstruct arguments to translation values
|
||||
*
|
||||
* @param $function
|
||||
* @param $args
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function deconstructArgs($function, $args)
|
||||
{
|
||||
$domain = null;
|
||||
$context = null;
|
||||
$original = null;
|
||||
$plural = null;
|
||||
|
||||
switch ($function) {
|
||||
case 'noop':
|
||||
case 'gettext':
|
||||
if (!isset($args[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$original = $args[0];
|
||||
break;
|
||||
case 'ngettext':
|
||||
if (!isset($args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($original, $plural) = $args;
|
||||
break;
|
||||
case 'pgettext':
|
||||
if (!isset($args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($context, $original) = $args;
|
||||
break;
|
||||
case 'dgettext':
|
||||
if (!isset($args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($domain, $original) = $args;
|
||||
break;
|
||||
case 'dpgettext':
|
||||
if (!isset($args[2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($domain, $context, $original) = $args;
|
||||
break;
|
||||
case 'npgettext':
|
||||
if (!isset($args[2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($context, $original, $plural) = $args;
|
||||
break;
|
||||
case 'dnpgettext':
|
||||
if (!isset($args[3])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($domain, $context, $original, $plural) = $args;
|
||||
break;
|
||||
case 'dngettext':
|
||||
if (!isset($args[2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($domain, $original, $plural) = $args;
|
||||
break;
|
||||
default:
|
||||
throw new Exception(sprintf('Not valid function %s', $function));
|
||||
}
|
||||
|
||||
return [$domain, $context, $original, $plural];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
/**
|
||||
* Trait to provide the functionality of extracting headers.
|
||||
*/
|
||||
trait HeadersExtractorTrait
|
||||
{
|
||||
/**
|
||||
* Add the headers found to the translations instance.
|
||||
*
|
||||
* @param string $headers
|
||||
* @param Translations $translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function extractHeaders($headers, Translations $translations)
|
||||
{
|
||||
$headers = explode("\n", $headers);
|
||||
$currentHeader = null;
|
||||
|
||||
foreach ($headers as $line) {
|
||||
$line = static::convertString($line);
|
||||
|
||||
if ($line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (static::isHeaderDefinition($line)) {
|
||||
$header = array_map('trim', explode(':', $line, 2));
|
||||
$currentHeader = $header[0];
|
||||
$translations->setHeader($currentHeader, $header[1]);
|
||||
} else {
|
||||
$entry = $translations->getHeader($currentHeader);
|
||||
$translations->setHeader($currentHeader, $entry.$line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is a header definition line. Useful for distguishing between header definitions
|
||||
* and possible continuations of a header entry.
|
||||
*
|
||||
* @param string $line Line to parse
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isHeaderDefinition($line)
|
||||
{
|
||||
return (bool) preg_match('/^[\w-]+:/', $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a string.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertString($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
/**
|
||||
* Trait to provide the functionality of extracting headers.
|
||||
*/
|
||||
trait HeadersGeneratorTrait
|
||||
{
|
||||
/**
|
||||
* Returns the headers as a string.
|
||||
*
|
||||
* @param Translations $translations
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function generateHeaders(Translations $translations)
|
||||
{
|
||||
$headers = '';
|
||||
|
||||
foreach ($translations->getHeaders() as $name => $value) {
|
||||
$headers .= sprintf("%s: %s\n", $name, $value);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
class JsFunctionsScanner extends FunctionsScanner
|
||||
{
|
||||
protected $code;
|
||||
protected $status = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $code The php code to scan
|
||||
*/
|
||||
public function __construct($code)
|
||||
{
|
||||
// Normalize newline characters
|
||||
$this->code = str_replace(["\r\n", "\n\r", "\r"], "\n", $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions(array $constants = [])
|
||||
{
|
||||
$length = strlen($this->code);
|
||||
$line = 1;
|
||||
$buffer = '';
|
||||
$functions = [];
|
||||
$bufferFunctions = [];
|
||||
$char = null;
|
||||
|
||||
for ($pos = 0; $pos < $length; ++$pos) {
|
||||
$prev = $char;
|
||||
$char = $this->code[$pos];
|
||||
$next = isset($this->code[$pos + 1]) ? $this->code[$pos + 1] : null;
|
||||
|
||||
switch ($char) {
|
||||
case '\\':
|
||||
switch ($this->status()) {
|
||||
case 'simple-quote':
|
||||
if ($next !== "'") {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'double-quote':
|
||||
if ($next !== '"') {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'back-tick':
|
||||
if ($next !== '`') {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$prev = $char;
|
||||
$char = $next;
|
||||
$pos++;
|
||||
$next = isset($this->code[$pos]) ? $this->code[$pos] : null;
|
||||
break;
|
||||
|
||||
case "\n":
|
||||
++$line;
|
||||
|
||||
if ($this->status('line-comment')) {
|
||||
$this->upStatus();
|
||||
}
|
||||
break;
|
||||
|
||||
case '/':
|
||||
switch ($this->status()) {
|
||||
case 'simple-quote':
|
||||
case 'double-quote':
|
||||
case 'back-tick':
|
||||
case 'line-comment':
|
||||
break;
|
||||
|
||||
case 'block-comment':
|
||||
if ($prev === '*') {
|
||||
$this->upStatus();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($next === '/') {
|
||||
$this->downStatus('line-comment');
|
||||
} elseif ($next === '*') {
|
||||
$this->downStatus('block-comment');
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "'":
|
||||
switch ($this->status()) {
|
||||
case 'simple-quote':
|
||||
$this->upStatus();
|
||||
break;
|
||||
|
||||
case 'line-comment':
|
||||
case 'block-comment':
|
||||
case 'double-quote':
|
||||
case 'back-tick':
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->downStatus('simple-quote');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
switch ($this->status()) {
|
||||
case 'double-quote':
|
||||
$this->upStatus();
|
||||
break;
|
||||
|
||||
case 'line-comment':
|
||||
case 'block-comment':
|
||||
case 'simple-quote':
|
||||
case 'back-tick':
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->downStatus('double-quote');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '`':
|
||||
switch ($this->status()) {
|
||||
case 'back-tick':
|
||||
$this->upStatus();
|
||||
break;
|
||||
|
||||
case 'line-comment':
|
||||
case 'block-comment':
|
||||
case 'simple-quote':
|
||||
case 'double-quote':
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->downStatus('back-tick');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '(':
|
||||
switch ($this->status()) {
|
||||
case 'simple-quote':
|
||||
case 'double-quote':
|
||||
case 'back-tick':
|
||||
case 'line-comment':
|
||||
case 'block-comment':
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($buffer && preg_match('/(\w+)$/', $buffer, $matches)) {
|
||||
$this->downStatus('function');
|
||||
array_unshift($bufferFunctions, [$matches[1], $line, []]);
|
||||
$buffer = '';
|
||||
continue 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ')':
|
||||
switch ($this->status()) {
|
||||
case 'function':
|
||||
if (($argument = static::prepareArgument($buffer))) {
|
||||
$bufferFunctions[0][2][] = $argument;
|
||||
}
|
||||
|
||||
if (!empty($bufferFunctions)) {
|
||||
$functions[] = array_shift($bufferFunctions);
|
||||
}
|
||||
|
||||
$this->upStatus();
|
||||
$buffer = '';
|
||||
continue 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
switch ($this->status()) {
|
||||
case 'function':
|
||||
if (($argument = static::prepareArgument($buffer))) {
|
||||
$bufferFunctions[0][2][] = $argument;
|
||||
}
|
||||
|
||||
$buffer = '';
|
||||
continue 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
switch ($this->status()) {
|
||||
case 'double-quote':
|
||||
case 'simple-quote':
|
||||
case 'back-tick':
|
||||
break;
|
||||
|
||||
default:
|
||||
$buffer = '';
|
||||
continue 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($this->status()) {
|
||||
case 'line-comment':
|
||||
case 'block-comment':
|
||||
break;
|
||||
|
||||
default:
|
||||
$buffer .= $char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current context of the scan.
|
||||
*
|
||||
* @param null|string $match To check whether the current status is this value
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function status($match = null)
|
||||
{
|
||||
$status = isset($this->status[0]) ? $this->status[0] : null;
|
||||
|
||||
if ($match !== null) {
|
||||
return $status === $match;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new status to the stack.
|
||||
*
|
||||
* @param string $status
|
||||
*/
|
||||
protected function downStatus($status)
|
||||
{
|
||||
array_unshift($this->status, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and return the current status.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function upStatus()
|
||||
{
|
||||
return array_shift($this->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the arguments found in functions.
|
||||
*
|
||||
* @param string $argument
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function prepareArgument($argument)
|
||||
{
|
||||
if ($argument && in_array($argument[0], ['"', "'", '`'], true)) {
|
||||
return static::convertString(substr($argument, 1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string with an argument.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function convertString($value)
|
||||
{
|
||||
if (strpos($value, '\\') === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/\\\(n|r|t|v|e|f|"|\\\)/',
|
||||
function ($match) {
|
||||
switch ($match[1][0]) {
|
||||
case 'n':
|
||||
return "\n";
|
||||
case 'r':
|
||||
return "\r";
|
||||
case 't':
|
||||
return "\t";
|
||||
case 'v':
|
||||
return "\v";
|
||||
case 'e':
|
||||
return "\e";
|
||||
case 'f':
|
||||
return "\f";
|
||||
case '"':
|
||||
return '"';
|
||||
case '\\':
|
||||
return '\\';
|
||||
}
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Gettext\Translations;
|
||||
|
||||
/**
|
||||
* Trait used by all generators that exports the translations to multidimensional arrays
|
||||
* (context => [original => [translation, plural1, pluraln...]]).
|
||||
*/
|
||||
trait MultidimensionalArrayTrait
|
||||
{
|
||||
use HeadersGeneratorTrait;
|
||||
use HeadersExtractorTrait;
|
||||
|
||||
/**
|
||||
* Returns a multidimensional array.
|
||||
*
|
||||
* @param Translations $translations
|
||||
* @param bool $includeHeaders
|
||||
* @param bool $forceArray
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function toArray(Translations $translations, $includeHeaders, $forceArray = false)
|
||||
{
|
||||
$pluralForm = $translations->getPluralForms();
|
||||
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
|
||||
$messages = [];
|
||||
|
||||
if ($includeHeaders) {
|
||||
$messages[''] = [
|
||||
'' => [static::generateHeaders($translations)],
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($translations as $translation) {
|
||||
if ($translation->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$context = $translation->getContext();
|
||||
$original = $translation->getOriginal();
|
||||
|
||||
if (!isset($messages[$context])) {
|
||||
$messages[$context] = [];
|
||||
}
|
||||
|
||||
if ($translation->hasPluralTranslations(true)) {
|
||||
$messages[$context][$original] = $translation->getPluralTranslations($pluralSize);
|
||||
array_unshift($messages[$context][$original], $translation->getTranslation());
|
||||
} elseif ($forceArray) {
|
||||
$messages[$context][$original] = [$translation->getTranslation()];
|
||||
} else {
|
||||
$messages[$context][$original] = $translation->getTranslation();
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'domain' => $translations->getDomain(),
|
||||
'plural-forms' => $translations->getHeader('Plural-Forms'),
|
||||
'messages' => $messages,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the entries from a multidimensional array.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param Translations $translations
|
||||
*/
|
||||
protected static function fromArray(array $messages, Translations $translations)
|
||||
{
|
||||
if (!empty($messages['domain'])) {
|
||||
$translations->setDomain($messages['domain']);
|
||||
}
|
||||
|
||||
if (!empty($messages['plural-forms'])) {
|
||||
$translations->setHeader(Translations::HEADER_PLURAL, $messages['plural-forms']);
|
||||
}
|
||||
|
||||
foreach ($messages['messages'] as $context => $contextTranslations) {
|
||||
foreach ($contextTranslations as $original => $value) {
|
||||
if ($context === '' && $original === '') {
|
||||
static::extractHeaders(is_array($value) ? array_shift($value) : $value, $translations);
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation = $translations->insert($context, $original);
|
||||
|
||||
if (is_array($value)) {
|
||||
$translation->setTranslation(array_shift($value));
|
||||
$translation->setPluralTranslations($value);
|
||||
} else {
|
||||
$translation->setTranslation($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
/**
|
||||
* Comment parsed by PhpFunctionsScanner.
|
||||
*/
|
||||
class ParsedComment
|
||||
{
|
||||
/**
|
||||
* The comment itself.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $comment;
|
||||
|
||||
/**
|
||||
* The line where the comment starts.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $firstLine;
|
||||
|
||||
/**
|
||||
* The line where the comment ends.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lastLine;
|
||||
|
||||
/**
|
||||
* Initializes the instance.
|
||||
*
|
||||
* @param string $comment The comment itself.
|
||||
* @param int $firstLine The line where the comment starts.
|
||||
* @param int $lastLine The line where the comment ends.
|
||||
*/
|
||||
public function __construct($comment, $firstLine, $lastLine)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
$this->firstLine = $firstLine;
|
||||
$this->lastLine = $lastLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new object from raw comment data.
|
||||
*
|
||||
* @param string $value The PHP comment string.
|
||||
* @param int $line The line where the comment starts.
|
||||
*
|
||||
* @return static The parsed comment.
|
||||
*/
|
||||
public static function create($value, $line)
|
||||
{
|
||||
$lastLine = $line + substr_count($value, "\n");
|
||||
|
||||
$lines = array_map(function ($line) {
|
||||
if ('' === trim($line)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$line = ltrim($line, "#*/ \t");
|
||||
$line = rtrim($line, "#*/ \t");
|
||||
|
||||
return trim($line);
|
||||
}, explode("\n", $value));
|
||||
|
||||
// Remove empty lines.
|
||||
$lines = array_filter($lines);
|
||||
$value = implode(' ', $lines);
|
||||
|
||||
return new static($value, $line, $lastLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the line where the comment starts.
|
||||
*
|
||||
* @return int Line number.
|
||||
*/
|
||||
public function getFirstLine()
|
||||
{
|
||||
return $this->firstLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the line where the comment ends.
|
||||
*
|
||||
* @return int Line number.
|
||||
*/
|
||||
public function getLastLine()
|
||||
{
|
||||
return $this->lastLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual comment string.
|
||||
*
|
||||
* @return string The comment.
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this comment is related with a given function.
|
||||
*
|
||||
* @param ParsedFunction $function The function to check.
|
||||
* @return bool Whether the comment is related or not.
|
||||
*/
|
||||
public function isRelatedWith(ParsedFunction $function)
|
||||
{
|
||||
return $this->getLastLine() === $function->getLine() || $this->getLastLine() === $function->getLine() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the comment matches the required prefixes.
|
||||
*
|
||||
* @param array $prefixes An array of prefixes to check.
|
||||
* @return bool Whether the comment matches the prefixes or not.
|
||||
*/
|
||||
public function checkPrefixes(array $prefixes)
|
||||
{
|
||||
if ('' === $this->comment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($prefixes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
if (strpos($this->comment, $prefix) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
/**
|
||||
* Function parsed by PhpFunctionsScanner.
|
||||
*/
|
||||
class ParsedFunction
|
||||
{
|
||||
/**
|
||||
* The function name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The line where the function starts.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $line;
|
||||
|
||||
/**
|
||||
* The strings extracted from the function arguments.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
/**
|
||||
* The current index of the function (-1 if no arguments).
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $argumentIndex;
|
||||
|
||||
/**
|
||||
* Shall we stop adding string chunks to the current argument?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $argumentStopped;
|
||||
|
||||
/**
|
||||
* Extracted comments.
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
protected $comments;
|
||||
|
||||
/**
|
||||
* Initializes the instance.
|
||||
*
|
||||
* @param string $name The function name.
|
||||
* @param int $line The line where the function starts.
|
||||
*/
|
||||
public function __construct($name, $line)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->line = $line;
|
||||
$this->arguments = [];
|
||||
$this->argumentIndex = -1;
|
||||
$this->argumentStopped = false;
|
||||
$this->comments = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop extracting strings from the current argument (because we found something that's not a string).
|
||||
*/
|
||||
public function stopArgument()
|
||||
{
|
||||
if ($this->argumentIndex === -1) {
|
||||
$this->argumentIndex = 0;
|
||||
}
|
||||
$this->argumentStopped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the next argument because we a comma was found.
|
||||
*/
|
||||
public function nextArgument()
|
||||
{
|
||||
if ($this->argumentIndex === -1) {
|
||||
// This should neve occur, but let's stay safe - During test/development an Exception should be thrown.
|
||||
$this->argumentIndex = 1;
|
||||
} else {
|
||||
++$this->argumentIndex;
|
||||
}
|
||||
$this->argumentStopped = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to the current argument.
|
||||
*
|
||||
* @param string|null $chunk
|
||||
*/
|
||||
public function addArgumentChunk($chunk)
|
||||
{
|
||||
if ($this->argumentStopped === false) {
|
||||
if ($this->argumentIndex === -1) {
|
||||
$this->argumentIndex = 0;
|
||||
}
|
||||
if (isset($this->arguments[$this->argumentIndex])) {
|
||||
$this->arguments[$this->argumentIndex] .= $chunk;
|
||||
} else {
|
||||
$this->arguments[$this->argumentIndex] = $chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment associated to this function.
|
||||
*
|
||||
* @param string $comment
|
||||
*/
|
||||
public function addComment($comment)
|
||||
{
|
||||
if ($this->comments === null) {
|
||||
$this->comments = [];
|
||||
}
|
||||
$this->comments[] = $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the line the function starts.
|
||||
*
|
||||
* @return int Line number.
|
||||
*/
|
||||
public function getLine()
|
||||
{
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
/**
|
||||
* A closing parenthesis was found: return the final data.
|
||||
* The array returned has the following values:
|
||||
* 0 => string The function name.
|
||||
* 1 => int The line where the function starts.
|
||||
* 2 => string[] the strings extracted from the function arguments.
|
||||
* 3 => string[] the comments associated to the function.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$arguments = [];
|
||||
for ($i = 0; $i <= $this->argumentIndex; ++$i) {
|
||||
$arguments[$i] = isset($this->arguments[$i]) ? $this->arguments[$i] : null;
|
||||
}
|
||||
|
||||
return [
|
||||
$this->name,
|
||||
$this->line,
|
||||
$arguments,
|
||||
$this->comments,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
use Gettext\Extractors\PhpCode;
|
||||
|
||||
class PhpFunctionsScanner extends FunctionsScanner
|
||||
{
|
||||
/**
|
||||
* PHP tokens of the code to be parsed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens;
|
||||
|
||||
/**
|
||||
* If not false, comments will be extracted.
|
||||
*
|
||||
* @var string|false|array
|
||||
*/
|
||||
protected $extractComments = false;
|
||||
|
||||
/**
|
||||
* Enable extracting comments that start with a tag (if $tag is empty all the comments will be extracted).
|
||||
*
|
||||
* @param mixed $tag
|
||||
*/
|
||||
public function enableCommentsExtraction($tag = '')
|
||||
{
|
||||
$this->extractComments = $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable comments extraction.
|
||||
*/
|
||||
public function disableCommentsExtraction()
|
||||
{
|
||||
$this->extractComments = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $code The php code to scan
|
||||
*/
|
||||
public function __construct($code)
|
||||
{
|
||||
$this->tokens = array_values(
|
||||
array_filter(
|
||||
token_get_all($code),
|
||||
function ($token) {
|
||||
return !is_array($token) || $token[0] !== T_WHITESPACE;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions(array $constants = [])
|
||||
{
|
||||
$count = count($this->tokens);
|
||||
/* @var ParsedFunction[] $bufferFunctions */
|
||||
$bufferFunctions = [];
|
||||
/* @var ParsedComment[] $bufferComments */
|
||||
$bufferComments = [];
|
||||
/* @var array $functions */
|
||||
$functions = [];
|
||||
|
||||
for ($k = 0; $k < $count; ++$k) {
|
||||
$value = $this->tokens[$k];
|
||||
|
||||
if (is_string($value)) {
|
||||
if (isset($bufferFunctions[0])) {
|
||||
switch ($value) {
|
||||
case ',':
|
||||
$bufferFunctions[0]->nextArgument();
|
||||
break;
|
||||
case ')':
|
||||
$functions[] = array_shift($bufferFunctions)->close();
|
||||
break;
|
||||
case '.':
|
||||
break;
|
||||
default:
|
||||
$bufferFunctions[0]->stopArgument();
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($value[0]) {
|
||||
case T_CONSTANT_ENCAPSED_STRING:
|
||||
//add an argument to the current function
|
||||
if (isset($bufferFunctions[0])) {
|
||||
$bufferFunctions[0]->addArgumentChunk(PhpCode::convertString($value[1]));
|
||||
}
|
||||
break;
|
||||
|
||||
case T_STRING:
|
||||
if (isset($bufferFunctions[0])) {
|
||||
if (isset($constants[$value[1]])) {
|
||||
$bufferFunctions[0]->addArgumentChunk($constants[$value[1]]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strtolower($value[1]) === 'null') {
|
||||
$bufferFunctions[0]->addArgumentChunk(null);
|
||||
break;
|
||||
}
|
||||
|
||||
$bufferFunctions[0]->stopArgument();
|
||||
}
|
||||
|
||||
//new function found
|
||||
for ($j = $k + 1; $j < $count; ++$j) {
|
||||
$nextToken = $this->tokens[$j];
|
||||
|
||||
if (is_array($nextToken) && $nextToken[0] === T_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($nextToken === '(') {
|
||||
$newFunction = new ParsedFunction($value[1], $value[2]);
|
||||
|
||||
// add comment that was on the line before.
|
||||
if (isset($bufferComments[0])) {
|
||||
$comment = $bufferComments[0];
|
||||
|
||||
if ($comment->isRelatedWith($newFunction)) {
|
||||
$newFunction->addComment($comment->getComment());
|
||||
}
|
||||
}
|
||||
|
||||
array_unshift($bufferFunctions, $newFunction);
|
||||
$k = $j;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_COMMENT:
|
||||
$comment = $this->parsePhpComment($value[1], $value[2]);
|
||||
|
||||
if ($comment) {
|
||||
array_unshift($bufferComments, $comment);
|
||||
|
||||
// The comment is inside the function call.
|
||||
if (isset($bufferFunctions[0])) {
|
||||
$bufferFunctions[0]->addComment($comment->getComment());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isset($bufferFunctions[0])) {
|
||||
$bufferFunctions[0]->stopArgument();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the actual text from a PHP comment.
|
||||
*
|
||||
* If set, only returns comments that match the prefix(es).
|
||||
*
|
||||
* @param string $value The PHP comment.
|
||||
* @param int $line Line number.
|
||||
*
|
||||
* @return null|ParsedComment Comment or null if comment extraction is disabled or if there is a prefix mismatch.
|
||||
*/
|
||||
protected function parsePhpComment($value, $line)
|
||||
{
|
||||
if ($this->extractComments === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//this returns a comment or null
|
||||
$comment = ParsedComment::create($value, $line);
|
||||
|
||||
$prefixes = array_filter((array) $this->extractComments);
|
||||
|
||||
if ($comment && $comment->checkPrefixes($prefixes)) {
|
||||
return $comment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Utils;
|
||||
|
||||
class StringReader
|
||||
{
|
||||
public $pos;
|
||||
public $str;
|
||||
public $strlen;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $str The string to read
|
||||
*/
|
||||
public function __construct($str)
|
||||
{
|
||||
$this->str = $str;
|
||||
$this->strlen = strlen($this->str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and returns a part of the string.
|
||||
*
|
||||
* @param int $bytes The number of bytes to read
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function read($bytes)
|
||||
{
|
||||
$data = substr($this->str, $this->pos, $bytes);
|
||||
|
||||
$this->seekto($this->pos + $bytes);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to a specific position.
|
||||
*
|
||||
* @param int $pos The amount of bytes to move
|
||||
*
|
||||
* @return int The new position
|
||||
*/
|
||||
public function seekto($pos)
|
||||
{
|
||||
$this->pos = ($this->strlen < $pos) ? $this->strlen : $pos;
|
||||
|
||||
return $this->pos;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
spl_autoload_register(function ($class) {
|
||||
if (strpos($class, 'Gettext\\') !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = __DIR__.str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen('Gettext'))).'.php';
|
||||
|
||||
if (is_file($file)) {
|
||||
require_once $file;
|
||||
}
|
||||
});
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
use Gettext\BaseTranslator;
|
||||
|
||||
/**
|
||||
* Returns the translation of a string.
|
||||
*
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function __($original)
|
||||
{
|
||||
$text = BaseTranslator::$current->gettext($original);
|
||||
|
||||
if (func_num_args() === 1) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 1);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Noop, marks the string for translation but returns it unchanged.
|
||||
*
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function noop__($original)
|
||||
{
|
||||
return $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singular/plural translation of a string.
|
||||
*
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function n__($original, $plural, $value)
|
||||
{
|
||||
$text = BaseTranslator::$current->ngettext($original, $plural, $value);
|
||||
|
||||
if (func_num_args() === 3) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 3);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation of a string in a specific context.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function p__($context, $original)
|
||||
{
|
||||
$text = BaseTranslator::$current->pgettext($context, $original);
|
||||
|
||||
if (func_num_args() === 2) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 2);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation of a string in a specific domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function d__($domain, $original)
|
||||
{
|
||||
$text = BaseTranslator::$current->dgettext($domain, $original);
|
||||
|
||||
if (func_num_args() === 2) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 2);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation of a string in a specific domain and context.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dp__($domain, $context, $original)
|
||||
{
|
||||
$text = BaseTranslator::$current->dpgettext($domain, $context, $original);
|
||||
|
||||
if (func_num_args() === 3) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 3);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singular/plural translation of a string in a specific domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dn__($domain, $original, $plural, $value)
|
||||
{
|
||||
$text = BaseTranslator::$current->dngettext($domain, $original, $plural, $value);
|
||||
|
||||
if (func_num_args() === 4) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 4);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singular/plural translation of a string in a specific context.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function np__($context, $original, $plural, $value)
|
||||
{
|
||||
$text = BaseTranslator::$current->npgettext($context, $original, $plural, $value);
|
||||
|
||||
if (func_num_args() === 4) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 4);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singular/plural translation of a string in a specific domain and context.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $context
|
||||
* @param string $original
|
||||
* @param string $plural
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dnp__($domain, $context, $original, $plural, $value)
|
||||
{
|
||||
$text = BaseTranslator::$current->dnpgettext($domain, $context, $original, $plural, $value);
|
||||
|
||||
if (func_num_args() === 5) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$args = array_slice(func_get_args(), 5);
|
||||
|
||||
return is_array($args[0]) ? strtr($text, $args[0]) : vsprintf($text, $args);
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Michele Locati
|
||||
|
||||
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.
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
|
||||
|
||||
See Terms of Use for definitions of Unicode Inc.'s
|
||||
Data Files and Software.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement.
|
||||
BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
|
||||
DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"),
|
||||
YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
|
||||
TERMS AND CONDITIONS OF THIS AGREEMENT.
|
||||
IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE
|
||||
THE DATA FILES OR SOFTWARE.
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 1991-2019 Unicode, Inc. All rights reserved.
|
||||
Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Unicode data files and any associated documentation
|
||||
(the "Data Files") or Unicode software and any associated documentation
|
||||
(the "Software") to deal in the Data Files or Software
|
||||
without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, and/or sell copies of
|
||||
the Data Files or Software, and to permit persons to whom the Data Files
|
||||
or Software are furnished to do so, provided that either
|
||||
(a) this copyright and permission notice appear with all copies
|
||||
of the Data Files or Software, or
|
||||
(b) this copyright and permission notice appear in associated
|
||||
Documentation.
|
||||
|
||||
THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
||||
NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
||||
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder
|
||||
shall not be used in advertising or otherwise to promote the sale,
|
||||
use or other dealings in these Data Files or Software without prior
|
||||
written authorization of the copyright holder.
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
use Gettext\Languages\Exporter\Exporter;
|
||||
use Gettext\Languages\Language;
|
||||
|
||||
// Let's start by imposing that we don't accept any error or warning.
|
||||
// This is a really life-saving approach.
|
||||
error_reporting(E_ALL);
|
||||
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
|
||||
fprintf(STDERR, "${errstr}\nFile: ${errfile}\nLine: ${errline}\nCode: ${errno}\n");
|
||||
die(5);
|
||||
});
|
||||
|
||||
require_once dirname(__DIR__) . '/src/autoloader.php';
|
||||
|
||||
// Parse the command line options
|
||||
Enviro::initialize();
|
||||
|
||||
try {
|
||||
if (isset(Enviro::$languages)) {
|
||||
$languages = array();
|
||||
foreach (Enviro::$languages as $languageId) {
|
||||
$language = Language::getById($languageId);
|
||||
if (!isset($language)) {
|
||||
throw new Exception("Unable to find the language with id '${languageId}'");
|
||||
}
|
||||
$languages[] = $language;
|
||||
}
|
||||
} else {
|
||||
$languages = Language::getAll();
|
||||
}
|
||||
if (Enviro::$reduce) {
|
||||
$languages = Enviro::reduce($languages);
|
||||
}
|
||||
if (Enviro::$noExtraParenthesis) {
|
||||
$languages = array_map(
|
||||
function (Language $language) {
|
||||
$language->formula = $language->buildFormula(true);
|
||||
|
||||
return $language;
|
||||
},
|
||||
$languages
|
||||
);
|
||||
}
|
||||
if (isset(Enviro::$outputFilename)) {
|
||||
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toFile'), $languages, Enviro::$outputFilename, array('us-ascii' => Enviro::$outputUSAscii));
|
||||
} else {
|
||||
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toString'), $languages, array('us-ascii' => Enviro::$outputUSAscii));
|
||||
}
|
||||
} catch (Exception $x) {
|
||||
fprintf(STDERR, $x->getMessage() . "\n");
|
||||
fprintf(STDERR, "Trace:\n");
|
||||
fprintf(STDERR, $x->getTraceAsString() . "\n");
|
||||
die(4);
|
||||
}
|
||||
|
||||
die(0);
|
||||
|
||||
/**
|
||||
* Helper class to handle command line options.
|
||||
*/
|
||||
class Enviro
|
||||
{
|
||||
/**
|
||||
* Shall the output contain only US-ASCII characters?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $outputUSAscii;
|
||||
|
||||
/**
|
||||
* The output format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $outputFormat;
|
||||
|
||||
/**
|
||||
* Output file name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $outputFilename;
|
||||
|
||||
/**
|
||||
* List of wanted language IDs; it not set: all languages will be returned.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
public static $languages;
|
||||
|
||||
/**
|
||||
* Reduce the language list to the minimum common denominator.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $reduce;
|
||||
|
||||
/**
|
||||
* Omit extra parenthesis in plural rule formulas.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $noExtraParenthesis;
|
||||
|
||||
/**
|
||||
* Parse the command line options.
|
||||
*/
|
||||
public static function initialize()
|
||||
{
|
||||
global $argv;
|
||||
self::$outputUSAscii = false;
|
||||
self::$outputFormat = null;
|
||||
self::$outputFilename = null;
|
||||
self::$languages = null;
|
||||
self::$reduce = null;
|
||||
self::$noExtraParenthesis = false;
|
||||
$exporters = Exporter::getExporters();
|
||||
if (isset($argv) && is_array($argv)) {
|
||||
foreach ($argv as $argi => $arg) {
|
||||
if ($argi === 0) {
|
||||
continue;
|
||||
}
|
||||
if (is_string($arg)) {
|
||||
$argLC = trim(strtolower($arg));
|
||||
switch ($argLC) {
|
||||
case '-h':
|
||||
case '--help':
|
||||
self::showSyntax();
|
||||
die(0);
|
||||
case '--us-ascii':
|
||||
self::$outputUSAscii = true;
|
||||
break;
|
||||
case '--reduce=yes':
|
||||
self::$reduce = true;
|
||||
break;
|
||||
case '--reduce=no':
|
||||
self::$reduce = false;
|
||||
break;
|
||||
case '--parenthesis=yes':
|
||||
self::$noExtraParenthesis = false;
|
||||
break;
|
||||
case '--parenthesis=no':
|
||||
self::$noExtraParenthesis = true;
|
||||
break;
|
||||
default:
|
||||
if (preg_match('/^--output=.+$/', $argLC)) {
|
||||
if (isset(self::$outputFilename)) {
|
||||
fprintf(STDERR, "The output file name has been specified more than once!\n");
|
||||
self::showSyntax();
|
||||
die(3);
|
||||
}
|
||||
list(, self::$outputFilename) = explode('=', $arg, 2);
|
||||
self::$outputFilename = trim(self::$outputFilename);
|
||||
} elseif (preg_match('/^--languages?=.+$/', $argLC)) {
|
||||
list(, $s) = explode('=', $arg, 2);
|
||||
$list = explode(',', $s);
|
||||
if (is_array(self::$languages)) {
|
||||
self::$languages = array_merge(self::$languages, $list);
|
||||
} else {
|
||||
self::$languages = $list;
|
||||
}
|
||||
} elseif (isset($exporters[$argLC])) {
|
||||
if (isset(self::$outputFormat)) {
|
||||
fprintf(STDERR, "The output format has been specified more than once!\n");
|
||||
self::showSyntax();
|
||||
die(3);
|
||||
}
|
||||
self::$outputFormat = $argLC;
|
||||
} else {
|
||||
fprintf(STDERR, "Unknown option: ${arg}\n");
|
||||
self::showSyntax();
|
||||
die(2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset(self::$outputFormat)) {
|
||||
self::showSyntax();
|
||||
die(1);
|
||||
}
|
||||
if (isset(self::$languages)) {
|
||||
self::$languages = array_values(array_unique(self::$languages));
|
||||
}
|
||||
if (!isset(self::$reduce)) {
|
||||
self::$reduce = isset(self::$languages) ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the syntax.
|
||||
*/
|
||||
public static function showSyntax()
|
||||
{
|
||||
$basename = basename(__FILE__);
|
||||
$exporters = array_keys(Exporter::getExporters(true));
|
||||
$exporterList = implode('|', $exporters);
|
||||
fprintf(STDERR, <<<EOT
|
||||
Syntax:
|
||||
${basename} [-h|--help] [--us-ascii] [--languages=<LanguageId>[,<LanguageId>,...]] [--reduce=yes|no] [--parenthesis=yes|no] [--output=<file name>] <${exporterList}>
|
||||
|
||||
Where:
|
||||
--help
|
||||
show this help message.
|
||||
|
||||
--us-ascii
|
||||
if specified, the output will contain only US-ASCII characters.
|
||||
|
||||
--languages(or --language)
|
||||
export only the specified language codes.
|
||||
Separate languages with commas; you can also use this argument
|
||||
more than once; it's case insensitive and accepts both '_' and
|
||||
'-' as locale chunks separator (eg we accept 'it_IT' as well as
|
||||
'it-it').
|
||||
--reduce
|
||||
if set to yes the output won't contain languages with the same
|
||||
base language and rules.
|
||||
For instance nl_BE ('Flemish') will be omitted because it's the
|
||||
same as nl ('Dutch').
|
||||
Defaults to 'no' if --languages is specified, to 'yes' otherwise.
|
||||
--parenthesis
|
||||
if set to no, extra parenthesis will be omitted in generated
|
||||
plural rules formulas.
|
||||
Those extra parenthesis are needed to create a PHP-compatible
|
||||
formula.
|
||||
Defaults to 'yes'
|
||||
--output
|
||||
if specified, the output will be saved to <file name>. If not
|
||||
specified we'll output to standard output.
|
||||
|
||||
Output formats
|
||||
|
||||
EOT
|
||||
);
|
||||
$len = max(array_map('strlen', $exporters));
|
||||
foreach ($exporters as $exporter) {
|
||||
fprintf(STDERR, ' ' . str_pad($exporter, $len) . ': ' . Exporter::getExporterDescription($exporter) . "\n");
|
||||
}
|
||||
fprintf(STDERR, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce a language list to the minimum common denominator.
|
||||
*
|
||||
* @param Language[] $languages
|
||||
*
|
||||
* @return Language[]
|
||||
*/
|
||||
public static function reduce($languages)
|
||||
{
|
||||
for ($numChunks = 3; $numChunks >= 2; $numChunks--) {
|
||||
$filtered = array();
|
||||
foreach ($languages as $language) {
|
||||
$chunks = explode('_', $language->id);
|
||||
$compatibleFound = false;
|
||||
if (count($chunks) === $numChunks) {
|
||||
$categoriesHash = serialize($language->categories);
|
||||
$otherIds = array();
|
||||
$otherIds[] = $chunks[0];
|
||||
for ($k = 2; $k < $numChunks; $k++) {
|
||||
$otherIds[] = $chunks[0] . '_' . $chunks[$numChunks - 1];
|
||||
}
|
||||
|
||||
foreach ($languages as $check) {
|
||||
foreach ($otherIds as $otherId) {
|
||||
if (($check->id === $otherId) && ($check->formula === $language->formula) && (serialize($check->categories) === $categoriesHash)) {
|
||||
$compatibleFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($compatibleFound === true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$compatibleFound) {
|
||||
$filtered[] = $language;
|
||||
}
|
||||
}
|
||||
$languages = $filtered;
|
||||
}
|
||||
|
||||
return $languages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@php "%~dpn0" %*
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "gettext/languages",
|
||||
"description": "gettext languages with plural rules",
|
||||
"keywords": [
|
||||
"localization",
|
||||
"l10n",
|
||||
"internationalization",
|
||||
"i18n",
|
||||
"translations",
|
||||
"translate",
|
||||
"php",
|
||||
"unicode",
|
||||
"cldr",
|
||||
"language",
|
||||
"languages",
|
||||
"plural",
|
||||
"plurals",
|
||||
"plural rules"
|
||||
],
|
||||
"homepage": "https://github.com/php-gettext/Languages",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michele Locati",
|
||||
"email": "mlocati@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Gettext\\Languages\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Gettext\\Languages\\Test\\": "tests/test/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4",
|
||||
"friendsofphp/php-cs-fixer": "^2.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"cs-test": "php-cs-fixer fix --config=.php_cs.dist --path-mode=intersection --verbose --dry-run --diff",
|
||||
"cs-fix": "php-cs-fixer fix --config=.php_cs.dist --path-mode=intersection"
|
||||
},
|
||||
"bin": [
|
||||
"bin/export-plural-rules"
|
||||
]
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* A helper class that handles a single category rules (eg 'zero', 'one', ...) and its formula and examples.
|
||||
*/
|
||||
class Category
|
||||
{
|
||||
/**
|
||||
* The category identifier (eg 'zero', 'one', ..., 'other').
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The gettext formula that identifies this category (null if and only if the category is 'other').
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $formula;
|
||||
|
||||
/**
|
||||
* The CLDR representation of some exemplar numeric ranges that satisfy this category.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $examples;
|
||||
|
||||
/**
|
||||
* Initialize the instance and parse the formula.
|
||||
*
|
||||
* @param string $cldrCategoryId the CLDR category identifier (eg 'pluralRule-count-one')
|
||||
* @param string $cldrFormulaAndExamples the CLDR formula and examples (eg 'i = 1 and v = 0 @integer 1')
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($cldrCategoryId, $cldrFormulaAndExamples)
|
||||
{
|
||||
$matches = array();
|
||||
if (!preg_match('/^pluralRule-count-(.+)$/', $cldrCategoryId, $matches)) {
|
||||
throw new Exception("Invalid CLDR category: '${cldrCategoryId}'");
|
||||
}
|
||||
if (!in_array($matches[1], CldrData::$categories)) {
|
||||
throw new Exception("Invalid CLDR category: '${cldrCategoryId}'");
|
||||
}
|
||||
$this->id = $matches[1];
|
||||
$cldrFormulaAndExamplesNormalized = trim(preg_replace('/\s+/', ' ', $cldrFormulaAndExamples));
|
||||
if (!preg_match('/^([^@]*)(?:@integer([^@]+))?(?:@decimal(?:[^@]+))?$/', $cldrFormulaAndExamplesNormalized, $matches)) {
|
||||
throw new Exception("Invalid CLDR category rule: ${cldrFormulaAndExamples}");
|
||||
}
|
||||
$cldrFormula = trim($matches[1]);
|
||||
$s = isset($matches[2]) ? trim($matches[2]) : '';
|
||||
$this->examples = ($s === '') ? null : $s;
|
||||
switch ($this->id) {
|
||||
case CldrData::OTHER_CATEGORY:
|
||||
if ($cldrFormula !== '') {
|
||||
throw new Exception("The '" . CldrData::OTHER_CATEGORY . "' category should not have any formula, but it has '${cldrFormula}'");
|
||||
}
|
||||
$this->formula = null;
|
||||
break;
|
||||
default:
|
||||
if ($cldrFormula === '') {
|
||||
throw new Exception("The '{$this->id}' category does not have a formula");
|
||||
}
|
||||
$this->formula = FormulaConverter::convertFormula($cldrFormula);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of numbers corresponding to the $examples value.
|
||||
*
|
||||
* @throws \Exception throws an Exception if we weren't able to expand the examples
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getExampleIntegers()
|
||||
{
|
||||
return self::expandExamples($this->examples);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a list of examples as defined by CLDR.
|
||||
*
|
||||
* @param string $examples A string like '1, 2, 5...7, …'.
|
||||
*
|
||||
* @throws \Exception throws an Exception if we weren't able to expand $examples
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function expandExamples($examples)
|
||||
{
|
||||
$result = array();
|
||||
$m = null;
|
||||
if (substr($examples, -strlen(', …')) === ', …') {
|
||||
$examples = substr($examples, 0, strlen($examples) - strlen(', …'));
|
||||
}
|
||||
foreach (explode(',', str_replace(' ', '', $examples)) as $range) {
|
||||
if (preg_match('/^\d+$/', $range)) {
|
||||
$result[] = (int) $range;
|
||||
} elseif (preg_match('/^(\d+)~(\d+)$/', $range, $m)) {
|
||||
$from = (int) $m[1];
|
||||
$to = (int) $m[2];
|
||||
$delta = $to - $from;
|
||||
$step = (int) max(1, $delta / 100);
|
||||
for ($i = $from; $i < $to; $i += $step) {
|
||||
$result[] = $i;
|
||||
}
|
||||
$result[] = $to;
|
||||
} else {
|
||||
throw new Exception("Unhandled test range '${range}' in '${examples}'");
|
||||
}
|
||||
}
|
||||
if (empty($result)) {
|
||||
throw new Exception("No test numbers from '${examples}'");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Holds the CLDR data.
|
||||
*/
|
||||
class CldrData
|
||||
{
|
||||
/**
|
||||
* Super-special plural category: this should always be present for any language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OTHER_CATEGORY = 'other';
|
||||
|
||||
/**
|
||||
* The list of the plural categories, sorted from 'zero' to 'other'.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $categories = array('zero', 'one', 'two', 'few', 'many', self::OTHER_CATEGORY);
|
||||
|
||||
/**
|
||||
* The loaded CLDR data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $data;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $plurals;
|
||||
|
||||
/**
|
||||
* Returns a dictionary containing the language names.
|
||||
* The keys are the language identifiers.
|
||||
* The values are the language names in US English.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getLanguageNames()
|
||||
{
|
||||
return self::getData('languages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a dictionary containing the territory names (in US English).
|
||||
* The keys are the territory identifiers.
|
||||
* The values are the territory names in US English.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getTerritoryNames()
|
||||
{
|
||||
return self::getData('territories');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a dictionary containing the script names (in US English).
|
||||
* The keys are the script identifiers.
|
||||
* The values are the script names in US English.
|
||||
*
|
||||
* @param bool $standAlone set to true to retrieve the stand-alone script names, false otherwise
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getScriptNames($standAlone)
|
||||
{
|
||||
return self::getData($standAlone ? 'standAloneScripts' : 'scripts');
|
||||
}
|
||||
|
||||
/**
|
||||
* A dictionary containing the plural rules.
|
||||
* The keys are the language identifiers.
|
||||
* The values are arrays whose keys are the CLDR category names and the values are the CLDR category definition.
|
||||
*
|
||||
* @example The English key-value pair is somethink like this:
|
||||
* <code><pre>
|
||||
* "en": {
|
||||
* "pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
||||
* "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
||||
* }
|
||||
* </pre></code>
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPlurals()
|
||||
{
|
||||
return self::getData('plurals');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of superseded language codes.
|
||||
*
|
||||
* @return array keys are the former language codes, values are the new language/locale codes
|
||||
*/
|
||||
public static function getSupersededLanguages()
|
||||
{
|
||||
return self::getData('supersededLanguages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of a language, as well as if a language code is deprecated in favor of another language code.
|
||||
*
|
||||
* @param string $id the language identifier
|
||||
*
|
||||
* @return array|null Returns an array with the keys 'id' (normalized), 'name', 'supersededBy' (optional), 'territory' (optional), 'script' (optional), 'baseLanguage' (optional), 'categories'. If $id is not valid returns null.
|
||||
*/
|
||||
public static function getLanguageInfo($id)
|
||||
{
|
||||
$result = null;
|
||||
$matches = array();
|
||||
if (preg_match('/^([a-z]{2,3})(?:[_\-]([a-z]{4}))?(?:[_\-]([a-z]{2}|[0-9]{3}))?(?:$|-)/i', $id, $matches)) {
|
||||
$languageId = strtolower($matches[1]);
|
||||
$scriptId = (isset($matches[2]) && ($matches[2] !== '')) ? ucfirst(strtolower($matches[2])) : null;
|
||||
$territoryId = (isset($matches[3]) && ($matches[3] !== '')) ? strtoupper($matches[3]) : null;
|
||||
$normalizedId = $languageId;
|
||||
if (isset($scriptId)) {
|
||||
$normalizedId .= '_' . $scriptId;
|
||||
}
|
||||
if (isset($territoryId)) {
|
||||
$normalizedId .= '_' . $territoryId;
|
||||
}
|
||||
// Structure precedence: see Likely Subtags - http://www.unicode.org/reports/tr35/tr35-31/tr35.html#Likely_Subtags
|
||||
$variants = array();
|
||||
$variantsWithScript = array();
|
||||
$variantsWithTerritory = array();
|
||||
if (isset($scriptId) && isset($territoryId)) {
|
||||
$variantsWithTerritory[] = $variantsWithScript[] = $variants[] = "{$languageId}_{$scriptId}_{$territoryId}";
|
||||
}
|
||||
if (isset($scriptId)) {
|
||||
$variantsWithScript[] = $variants[] = "{$languageId}_{$scriptId}";
|
||||
}
|
||||
if (isset($territoryId)) {
|
||||
$variantsWithTerritory[] = $variants[] = "{$languageId}_{$territoryId}";
|
||||
}
|
||||
$variants[] = $languageId;
|
||||
$allGood = true;
|
||||
$scriptName = null;
|
||||
$scriptStandAloneName = null;
|
||||
if (isset($scriptId)) {
|
||||
$scriptNames = self::getScriptNames(false);
|
||||
if (isset($scriptNames[$scriptId])) {
|
||||
$scriptName = $scriptNames[$scriptId];
|
||||
$scriptStandAloneNames = self::getScriptNames(true);
|
||||
$scriptStandAloneName = $scriptStandAloneNames[$scriptId];
|
||||
} else {
|
||||
$allGood = false;
|
||||
}
|
||||
}
|
||||
$territoryName = null;
|
||||
if (isset($territoryId)) {
|
||||
$territoryNames = self::getTerritoryNames();
|
||||
if (isset($territoryNames[$territoryId])) {
|
||||
if ($territoryId !== '001') {
|
||||
$territoryName = $territoryNames[$territoryId];
|
||||
}
|
||||
} else {
|
||||
$allGood = false;
|
||||
}
|
||||
}
|
||||
$languageName = null;
|
||||
$languageNames = self::getLanguageNames();
|
||||
foreach ($variants as $variant) {
|
||||
if (isset($languageNames[$variant])) {
|
||||
$languageName = $languageNames[$variant];
|
||||
if (isset($scriptName) && (!in_array($variant, $variantsWithScript))) {
|
||||
$languageName = $scriptName . ' ' . $languageName;
|
||||
}
|
||||
if (isset($territoryName) && (!in_array($variant, $variantsWithTerritory))) {
|
||||
$languageName .= ' (' . $territoryNames[$territoryId] . ')';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isset($languageName)) {
|
||||
$allGood = false;
|
||||
}
|
||||
$baseLanguage = null;
|
||||
if (isset($scriptId) || isset($territoryId)) {
|
||||
if (isset($languageNames[$languageId]) && ($languageNames[$languageId] !== $languageName)) {
|
||||
$baseLanguage = $languageNames[$languageId];
|
||||
}
|
||||
}
|
||||
$plural = null;
|
||||
$plurals = self::getPlurals();
|
||||
foreach ($variants as $variant) {
|
||||
if (isset($plurals[$variant])) {
|
||||
$plural = $plurals[$variant];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isset($plural)) {
|
||||
$allGood = false;
|
||||
}
|
||||
$supersededBy = null;
|
||||
$supersededBys = self::getSupersededLanguages();
|
||||
foreach ($variants as $variant) {
|
||||
if (isset($supersededBys[$variant])) {
|
||||
$supersededBy = $supersededBys[$variant];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allGood) {
|
||||
$result = array();
|
||||
$result['id'] = $normalizedId;
|
||||
$result['name'] = $languageName;
|
||||
if (isset($supersededBy)) {
|
||||
$result['supersededBy'] = $supersededBy;
|
||||
}
|
||||
if (isset($scriptStandAloneName)) {
|
||||
$result['script'] = $scriptStandAloneName;
|
||||
}
|
||||
if (isset($territoryName)) {
|
||||
$result['territory'] = $territoryName;
|
||||
}
|
||||
if (isset($baseLanguage)) {
|
||||
$result['baseLanguage'] = $baseLanguage;
|
||||
}
|
||||
$result['categories'] = $plural;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loaded CLDR data.
|
||||
*
|
||||
* @param string $key Can be 'languages', 'territories', 'plurals', 'supersededLanguages', 'scripts', 'standAloneScripts'
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getData($key)
|
||||
{
|
||||
if (!isset(self::$data)) {
|
||||
$fixKeys = function ($list, &$standAlone = null) {
|
||||
$result = array();
|
||||
$standAlone = array();
|
||||
$match = null;
|
||||
foreach ($list as $key => $value) {
|
||||
$variant = '';
|
||||
if (preg_match('/^(.+)-alt-(short|variant|stand-alone|long|menu)$/', $key, $match)) {
|
||||
$key = $match[1];
|
||||
$variant = $match[2];
|
||||
}
|
||||
$key = str_replace('-', '_', $key);
|
||||
switch ($key) {
|
||||
case 'root': // Language: Root
|
||||
case 'und': // Language: Unknown Language
|
||||
case 'zxx': // Language: No linguistic content
|
||||
case 'ZZ': // Territory: Unknown Region
|
||||
case 'Zinh': // Script: Inherited
|
||||
case 'Zmth': // Script: Mathematical Notation
|
||||
case 'Zsym': // Script: Symbols
|
||||
case 'Zxxx': // Script: Unwritten
|
||||
case 'Zyyy': // Script: Common
|
||||
case 'Zzzz': // Script: Unknown Script
|
||||
break;
|
||||
default:
|
||||
switch ($variant) {
|
||||
case 'stand-alone':
|
||||
$standAlone[$key] = $value;
|
||||
break;
|
||||
case '':
|
||||
$result[$key] = $value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
};
|
||||
$data = array();
|
||||
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/languages.json'), true);
|
||||
$data['languages'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['languages']);
|
||||
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/territories.json'), true);
|
||||
$data['territories'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['territories']);
|
||||
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/supplemental/plurals.json'), true);
|
||||
$data['plurals'] = $fixKeys($json['supplemental']['plurals-type-cardinal']);
|
||||
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/scripts.json'), true);
|
||||
$data['scripts'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['scripts'], $data['standAloneScripts']);
|
||||
$data['standAloneScripts'] = array_merge($data['scripts'], $data['standAloneScripts']);
|
||||
$data['scripts'] = array_merge($data['standAloneScripts'], $data['scripts']);
|
||||
$data['supersededLanguages'] = array();
|
||||
// Remove the languages for which we don't have plurals
|
||||
$m = null;
|
||||
foreach (array_keys(array_diff_key($data['languages'], $data['plurals'])) as $missingPlural) {
|
||||
if (preg_match('/^([a-z]{2,3})_/', $missingPlural, $m)) {
|
||||
if (!isset($data['plurals'][$m[1]])) {
|
||||
unset($data['languages'][$missingPlural]);
|
||||
}
|
||||
} else {
|
||||
unset($data['languages'][$missingPlural]);
|
||||
}
|
||||
}
|
||||
// Fix the languages for which we have plurals
|
||||
$formerCodes = array(
|
||||
'in' => 'id', // former Indonesian
|
||||
'iw' => 'he', // former Hebrew
|
||||
'ji' => 'yi', // former Yiddish
|
||||
'jw' => 'jv', // former Javanese
|
||||
'mo' => 'ro_MD', // former Moldavian
|
||||
);
|
||||
$knownMissingLanguages = array(
|
||||
'guw' => 'Gun',
|
||||
'nah' => 'Nahuatl',
|
||||
'smi' => 'Sami',
|
||||
);
|
||||
foreach (array_keys(array_diff_key($data['plurals'], $data['languages'])) as $missingLanguage) {
|
||||
if (isset($formerCodes[$missingLanguage]) && isset($data['languages'][$formerCodes[$missingLanguage]])) {
|
||||
$data['languages'][$missingLanguage] = $data['languages'][$formerCodes[$missingLanguage]];
|
||||
$data['supersededLanguages'][$missingLanguage] = $formerCodes[$missingLanguage];
|
||||
} else {
|
||||
if (isset($knownMissingLanguages[$missingLanguage])) {
|
||||
$data['languages'][$missingLanguage] = $knownMissingLanguages[$missingLanguage];
|
||||
} else {
|
||||
throw new Exception("We have the plural rule for the language '${missingLanguage}' but we don't have its language name");
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($data['languages'], SORT_STRING);
|
||||
ksort($data['territories'], SORT_STRING);
|
||||
ksort($data['plurals'], SORT_STRING);
|
||||
ksort($data['scripts'], SORT_STRING);
|
||||
ksort($data['standAloneScripts'], SORT_STRING);
|
||||
ksort($data['supersededLanguages'], SORT_STRING);
|
||||
self::$data = $data;
|
||||
}
|
||||
if (!isset(self::$data[$key])) {
|
||||
throw new Exception("Invalid CLDR data key: '${key}'");
|
||||
}
|
||||
|
||||
return self::$data[$key];
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
class Docs extends Html
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::isForPublicUse()
|
||||
*/
|
||||
public static function isForPublicUse()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return 'Build the page https://php-gettext.github.io/Languages/';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
$result = <<<EOT
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="Michele Locati">
|
||||
<title>gettext plural rules - built from CLDR</title>
|
||||
<meta name="description" content="List of all language rules for gettext .po files automatically generated from the Unicode CLDR data" />
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/php-gettext/Languages" class="hidden-xs"><img style="position: fixed; top: 0; right: 0; border: 0; z-index: 2000" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
|
||||
<div class="container-fluid">
|
||||
|
||||
EOT;
|
||||
$result .= static::buildTable($languages, true);
|
||||
$result .= <<<EOT
|
||||
|
||||
</div>
|
||||
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Base class for all the exporters.
|
||||
*/
|
||||
abstract class Exporter
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $exporters;
|
||||
|
||||
/**
|
||||
* Return the list of all the available exporters. Keys are the exporter handles, values are the exporter class names.
|
||||
*
|
||||
* @param bool $onlyForPublicUse if true, internal exporters will be omitted
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
final public static function getExporters($onlyForPublicUse = false)
|
||||
{
|
||||
if (!isset(self::$exporters)) {
|
||||
$exporters = array();
|
||||
$m = null;
|
||||
foreach (scandir(__DIR__) as $f) {
|
||||
if (preg_match('/^(\w+)\.php$/', $f, $m)) {
|
||||
if ($f !== basename(__FILE__)) {
|
||||
$exporters[strtolower($m[1])] = $m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
self::$exporters = $exporters;
|
||||
}
|
||||
if ($onlyForPublicUse) {
|
||||
$result = array();
|
||||
foreach (self::$exporters as $handle => $class) {
|
||||
if (call_user_func(self::getExporterClassName($handle) . '::isForPublicUse') === true) {
|
||||
$result[$handle] = $class;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = self::$exporters;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the description of a specific exporter.
|
||||
*
|
||||
* @param string $exporterHandle the handle of the exporter
|
||||
*
|
||||
* @throws \Exception throws an Exception if $exporterHandle is not valid
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public static function getExporterDescription($exporterHandle)
|
||||
{
|
||||
$exporters = self::getExporters();
|
||||
if (!isset($exporters[$exporterHandle])) {
|
||||
throw new Exception("Invalid exporter handle: '${exporterHandle}'");
|
||||
}
|
||||
|
||||
return call_user_func(self::getExporterClassName($exporterHandle) . '::getDescription');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully qualified class name of a exporter given its handle.
|
||||
*
|
||||
* @param string $exporterHandle the exporter class handle
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public static function getExporterClassName($exporterHandle)
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . ucfirst(strtolower($exporterHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of Language instances to string.
|
||||
*
|
||||
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public static function toString($languages, $options = null)
|
||||
{
|
||||
if (isset($options) && is_array($options)) {
|
||||
if (isset($options['us-ascii']) && $options['us-ascii']) {
|
||||
$asciiList = array();
|
||||
foreach ($languages as $language) {
|
||||
$asciiList[] = $language->getUSAsciiClone();
|
||||
}
|
||||
$languages = $asciiList;
|
||||
}
|
||||
}
|
||||
|
||||
return static::toStringDo($languages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the Language instances to a file.
|
||||
*
|
||||
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
final public static function toFile($languages, $filename, $options = null)
|
||||
{
|
||||
$data = self::toString($languages, $options);
|
||||
if (@file_put_contents($filename, $data) === false) {
|
||||
throw new Exception("Error writing data to '${filename}'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this exporter for public use?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isForPublicUse()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a short description of the exporter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
throw new Exception(get_called_class() . ' does not implement the method ' . __FUNCTION__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of Language instances to string.
|
||||
*
|
||||
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
throw new Exception(get_called_class() . ' does not implement the method ' . __FUNCTION__);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
class Html extends Exporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return 'Build a HTML table';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
return self::buildTable($languages, false);
|
||||
}
|
||||
|
||||
protected static function h($str)
|
||||
{
|
||||
return htmlspecialchars($str, ENT_COMPAT, 'UTF-8');
|
||||
}
|
||||
|
||||
protected static function buildTable($languages, $forDocs)
|
||||
{
|
||||
$prefix = $forDocs ? ' ' : '';
|
||||
$lines = array();
|
||||
$lines[] = $prefix . '<table' . ($forDocs ? ' class="table table-bordered table-condensed table-striped"' : '') . '>';
|
||||
$lines[] = $prefix . ' <thead>';
|
||||
$lines[] = $prefix . ' <tr>';
|
||||
$lines[] = $prefix . ' <th>Language code</th>';
|
||||
$lines[] = $prefix . ' <th>Language name</th>';
|
||||
$lines[] = $prefix . ' <th># plurals</th>';
|
||||
$lines[] = $prefix . ' <th>Formula</th>';
|
||||
$lines[] = $prefix . ' <th>Plurals</th>';
|
||||
$lines[] = $prefix . ' </tr>';
|
||||
$lines[] = $prefix . ' </thead>';
|
||||
$lines[] = $prefix . ' <tbody>';
|
||||
foreach ($languages as $lc) {
|
||||
$lines[] = $prefix . ' <tr>';
|
||||
$lines[] = $prefix . ' <td>' . $lc->id . '</td>';
|
||||
$name = self::h($lc->name);
|
||||
if (isset($lc->supersededBy)) {
|
||||
$name .= '<br /><small><span>Superseded by</span> ' . $lc->supersededBy . '</small>';
|
||||
}
|
||||
$lines[] = $prefix . ' <td>' . $name . '</td>';
|
||||
$lines[] = $prefix . ' <td>' . count($lc->categories) . '</td>';
|
||||
$lines[] = $prefix . ' <td>' . self::h($lc->formula) . '</td>';
|
||||
$cases = array();
|
||||
foreach ($lc->categories as $c) {
|
||||
$cases[] = '<li><span>' . $c->id . '</span><code>' . self::h($c->examples) . '</code></li>';
|
||||
}
|
||||
$lines[] = $prefix . ' <td><ol' . ($forDocs ? ' class="cases"' : '') . ' start="0">' . implode('', $cases) . '</ol></td>';
|
||||
$lines[] = $prefix . ' </tr>';
|
||||
}
|
||||
$lines[] = $prefix . ' </tbody>';
|
||||
$lines[] = $prefix . '</table>';
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
class Json extends Exporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return 'Build a compressed JSON-encoded file';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the options for json_encode.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected static function getEncodeOptions()
|
||||
{
|
||||
$result = 0;
|
||||
if (defined('\JSON_UNESCAPED_SLASHES')) {
|
||||
$result |= \JSON_UNESCAPED_SLASHES;
|
||||
}
|
||||
if (defined('\JSON_UNESCAPED_UNICODE')) {
|
||||
$result |= \JSON_UNESCAPED_UNICODE;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
$list = array();
|
||||
foreach ($languages as $language) {
|
||||
$item = array();
|
||||
$item['name'] = $language->name;
|
||||
if (isset($language->supersededBy)) {
|
||||
$item['supersededBy'] = $language->supersededBy;
|
||||
}
|
||||
if (isset($language->script)) {
|
||||
$item['script'] = $language->script;
|
||||
}
|
||||
if (isset($language->territory)) {
|
||||
$item['territory'] = $language->territory;
|
||||
}
|
||||
if (isset($language->baseLanguage)) {
|
||||
$item['baseLanguage'] = $language->baseLanguage;
|
||||
}
|
||||
$item['formula'] = $language->formula;
|
||||
$item['plurals'] = count($language->categories);
|
||||
$item['cases'] = array();
|
||||
$item['examples'] = array();
|
||||
foreach ($language->categories as $category) {
|
||||
$item['cases'][] = $category->id;
|
||||
$item['examples'][$category->id] = $category->examples;
|
||||
}
|
||||
$list[$language->id] = $item;
|
||||
}
|
||||
|
||||
return json_encode($list, static::getEncodeOptions());
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
class Php extends Exporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return 'Build a PHP array';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
$lines = array();
|
||||
$lines[] = '<?php';
|
||||
$lines[] = 'return array(';
|
||||
foreach ($languages as $lc) {
|
||||
$lines[] = ' \'' . $lc->id . '\' => array(';
|
||||
$lines[] = ' \'name\' => \'' . addslashes($lc->name) . '\',';
|
||||
if (isset($lc->supersededBy)) {
|
||||
$lines[] = ' \'supersededBy\' => \'' . $lc->supersededBy . '\',';
|
||||
}
|
||||
if (isset($lc->script)) {
|
||||
$lines[] = ' \'script\' => \'' . addslashes($lc->script) . '\',';
|
||||
}
|
||||
if (isset($lc->territory)) {
|
||||
$lines[] = ' \'territory\' => \'' . addslashes($lc->territory) . '\',';
|
||||
}
|
||||
if (isset($lc->baseLanguage)) {
|
||||
$lines[] = ' \'baseLanguage\' => \'' . addslashes($lc->baseLanguage) . '\',';
|
||||
}
|
||||
$lines[] = ' \'formula\' => \'' . $lc->formula . '\',';
|
||||
$lines[] = ' \'plurals\' => ' . count($lc->categories) . ',';
|
||||
$catNames = array();
|
||||
foreach ($lc->categories as $c) {
|
||||
$catNames[] = "'{$c->id}'";
|
||||
}
|
||||
$lines[] = ' \'cases\' => array(' . implode(', ', $catNames) . '),';
|
||||
$lines[] = ' \'examples\' => array(';
|
||||
foreach ($lc->categories as $c) {
|
||||
$lines[] = ' \'' . $c->id . '\' => \'' . $c->examples . '\',';
|
||||
}
|
||||
$lines[] = ' ),';
|
||||
$lines[] = ' ),';
|
||||
}
|
||||
$lines[] = ');';
|
||||
$lines[] = '';
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Gettext\Languages\Exporter;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Po extends Exporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return 'Build a string to be used for gettext .po files';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
|
||||
*/
|
||||
protected static function toStringDo($languages)
|
||||
{
|
||||
if (count($languages) !== 1) {
|
||||
throw new Exception('The ' . get_called_class() . ' exporter can only export one language');
|
||||
}
|
||||
$language = $languages[0];
|
||||
$lines = array();
|
||||
$lines[] = '"Language: ' . $language->id . '\n"';
|
||||
$lines[] = '"Plural-Forms: nplurals=' . count($language->categories) . '; plural=' . $language->formula . '\n"';
|
||||
$lines[] = '';
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user