init
This commit is contained in:
189
vendor/yiisoft/yii2/i18n/DbMessageSource.php
vendored
Normal file
189
vendor/yiisoft/yii2/i18n/DbMessageSource.php
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\caching\CacheInterface;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Expression;
|
||||
use yii\db\Query;
|
||||
use yii\di\Instance;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* DbMessageSource extends [[MessageSource]] and represents a message source that stores translated
|
||||
* messages in database.
|
||||
*
|
||||
* The database must contain the following two tables: source_message and message.
|
||||
*
|
||||
* The `source_message` table stores the messages to be translated, and the `message` table stores
|
||||
* the translated messages. The name of these two tables can be customized by setting [[sourceMessageTable]]
|
||||
* and [[messageTable]], respectively.
|
||||
*
|
||||
* The database connection is specified by [[db]]. Database schema could be initialized by applying migration:
|
||||
*
|
||||
* ```
|
||||
* yii migrate --migrationPath=@yii/i18n/migrations/
|
||||
* ```
|
||||
*
|
||||
* If you don't want to use migration and need SQL instead, files for all databases are in migrations directory.
|
||||
*
|
||||
* @author resurtm <resurtm@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class DbMessageSource extends MessageSource
|
||||
{
|
||||
/**
|
||||
* Prefix which would be used when generating cache key.
|
||||
* @deprecated This constant has never been used and will be removed in 2.1.0.
|
||||
*/
|
||||
const CACHE_KEY_PREFIX = 'DbMessageSource';
|
||||
|
||||
/**
|
||||
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
|
||||
*
|
||||
* After the DbMessageSource object is created, if you want to change this property, you should only assign
|
||||
* it with a DB connection object.
|
||||
*
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'db';
|
||||
/**
|
||||
* @var CacheInterface|array|string the cache object or the application component ID of the cache object.
|
||||
* The messages data will be cached using this cache object.
|
||||
* Note, that to enable caching you have to set [[enableCaching]] to `true`, otherwise setting this property has no effect.
|
||||
*
|
||||
* After the DbMessageSource object is created, if you want to change this property, you should only assign
|
||||
* it with a cache object.
|
||||
*
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
* @see cachingDuration
|
||||
* @see enableCaching
|
||||
*/
|
||||
public $cache = 'cache';
|
||||
/**
|
||||
* @var string the name of the source message table.
|
||||
*/
|
||||
public $sourceMessageTable = '{{%source_message}}';
|
||||
/**
|
||||
* @var string the name of the translated message table.
|
||||
*/
|
||||
public $messageTable = '{{%message}}';
|
||||
/**
|
||||
* @var int the time in seconds that the messages can remain valid in cache.
|
||||
* Use 0 to indicate that the cached data will never expire.
|
||||
* @see enableCaching
|
||||
*/
|
||||
public $cachingDuration = 0;
|
||||
/**
|
||||
* @var bool whether to enable caching translated messages
|
||||
*/
|
||||
public $enableCaching = false;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the DbMessageSource component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
|
||||
* Configured [[cache]] component would also be initialized.
|
||||
* @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
if ($this->enableCaching) {
|
||||
$this->cache = Instance::ensure($this->cache, 'yii\caching\CacheInterface');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified language and category.
|
||||
* If translation for specific locale code such as `en-US` isn't found it
|
||||
* tries more generic `en`.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return array the loaded messages. The keys are original messages, and the values
|
||||
* are translated messages.
|
||||
*/
|
||||
protected function loadMessages($category, $language)
|
||||
{
|
||||
if ($this->enableCaching) {
|
||||
$key = [
|
||||
__CLASS__,
|
||||
$category,
|
||||
$language,
|
||||
];
|
||||
$messages = $this->cache->get($key);
|
||||
if ($messages === false) {
|
||||
$messages = $this->loadMessagesFromDb($category, $language);
|
||||
$this->cache->set($key, $messages, $this->cachingDuration);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
return $this->loadMessagesFromDb($category, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the messages from database.
|
||||
* You may override this method to customize the message storage in the database.
|
||||
* @param string $category the message category.
|
||||
* @param string $language the target language.
|
||||
* @return array the messages loaded from database.
|
||||
*/
|
||||
protected function loadMessagesFromDb($category, $language)
|
||||
{
|
||||
$mainQuery = (new Query())->select(['message' => 't1.message', 'translation' => 't2.translation'])
|
||||
->from(['t1' => $this->sourceMessageTable, 't2' => $this->messageTable])
|
||||
->where([
|
||||
't1.id' => new Expression('[[t2.id]]'),
|
||||
't1.category' => $category,
|
||||
't2.language' => $language,
|
||||
]);
|
||||
|
||||
$fallbackLanguage = substr($language, 0, 2);
|
||||
$fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
|
||||
|
||||
if ($fallbackLanguage !== $language) {
|
||||
$mainQuery->union($this->createFallbackQuery($category, $language, $fallbackLanguage), true);
|
||||
} elseif ($language === $fallbackSourceLanguage) {
|
||||
$mainQuery->union($this->createFallbackQuery($category, $language, $fallbackSourceLanguage), true);
|
||||
}
|
||||
|
||||
$messages = $mainQuery->createCommand($this->db)->queryAll();
|
||||
|
||||
return ArrayHelper::map($messages, 'message', 'translation');
|
||||
}
|
||||
|
||||
/**
|
||||
* The method builds the [[Query]] object for the fallback language messages search.
|
||||
* Normally is called from [[loadMessagesFromDb]].
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the originally requested language
|
||||
* @param string $fallbackLanguage the target fallback language
|
||||
* @return Query
|
||||
* @see loadMessagesFromDb
|
||||
* @since 2.0.7
|
||||
*/
|
||||
protected function createFallbackQuery($category, $language, $fallbackLanguage)
|
||||
{
|
||||
return (new Query())->select(['message' => 't1.message', 'translation' => 't2.translation'])
|
||||
->from(['t1' => $this->sourceMessageTable, 't2' => $this->messageTable])
|
||||
->where([
|
||||
't1.id' => new Expression('[[t2.id]]'),
|
||||
't1.category' => $category,
|
||||
't2.language' => $fallbackLanguage,
|
||||
])->andWhere([
|
||||
'NOT IN', 't2.id', (new Query())->select('[[id]]')->from($this->messageTable)->where(['language' => $language]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
1740
vendor/yiisoft/yii2/i18n/Formatter.php
vendored
Normal file
1740
vendor/yiisoft/yii2/i18n/Formatter.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
37
vendor/yiisoft/yii2/i18n/GettextFile.php
vendored
Normal file
37
vendor/yiisoft/yii2/i18n/GettextFile.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use yii\base\Component;
|
||||
|
||||
/**
|
||||
* GettextFile is the base class for representing a Gettext message file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class GettextFile extends Component
|
||||
{
|
||||
/**
|
||||
* Loads messages from a file.
|
||||
* @param string $filePath file path
|
||||
* @param string $context message context
|
||||
* @return array message translations. Array keys are source messages and array values are translated messages:
|
||||
* source message => translated message.
|
||||
*/
|
||||
abstract public function load($filePath, $context);
|
||||
|
||||
/**
|
||||
* Saves messages to a file.
|
||||
* @param string $filePath file path
|
||||
* @param array $messages message translations. Array keys are source messages and array values are
|
||||
* translated messages: source message => translated message. Note if the message has a context,
|
||||
* the message ID must be prefixed with the context with chr(4) as the separator.
|
||||
*/
|
||||
abstract public function save($filePath, $messages);
|
||||
}
|
||||
169
vendor/yiisoft/yii2/i18n/GettextMessageSource.php
vendored
Normal file
169
vendor/yiisoft/yii2/i18n/GettextMessageSource.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* GettextMessageSource represents a message source that is based on GNU Gettext.
|
||||
*
|
||||
* Each GettextMessageSource instance represents the message translations
|
||||
* for a single domain. And each message category represents a message context
|
||||
* in Gettext. Translated messages are stored as either a MO or PO file,
|
||||
* depending on the [[useMoFile]] property value.
|
||||
*
|
||||
* All translations are saved under the [[basePath]] directory.
|
||||
*
|
||||
* Translations in one language are kept as MO or PO files under an individual
|
||||
* subdirectory whose name is the language ID. The file name is specified via
|
||||
* [[catalog]] property, which defaults to 'messages'.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class GettextMessageSource extends MessageSource
|
||||
{
|
||||
const MO_FILE_EXT = '.mo';
|
||||
const PO_FILE_EXT = '.po';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $basePath = '@app/messages';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $catalog = 'messages';
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $useMoFile = true;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $useBigEndian = false;
|
||||
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified $language and $category.
|
||||
* If translation for specific locale code such as `en-US` isn't found it
|
||||
* tries more generic `en`. When both are present, the `en-US` messages will be merged
|
||||
* over `en`. See [[loadFallbackMessages]] for details.
|
||||
* If the $language is less specific than [[sourceLanguage]], the method will try to
|
||||
* load the messages for [[sourceLanguage]]. For example: [[sourceLanguage]] is `en-GB`,
|
||||
* $language is `en`. The method will load the messages for `en` and merge them over `en-GB`.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return array the loaded messages. The keys are original messages, and the values are translated messages.
|
||||
* @see loadFallbackMessages
|
||||
* @see sourceLanguage
|
||||
*/
|
||||
protected function loadMessages($category, $language)
|
||||
{
|
||||
$messageFile = $this->getMessageFilePath($language);
|
||||
$messages = $this->loadMessagesFromFile($messageFile, $category);
|
||||
|
||||
$fallbackLanguage = substr($language, 0, 2);
|
||||
$fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
|
||||
|
||||
if ($fallbackLanguage !== $language) {
|
||||
$messages = $this->loadFallbackMessages($category, $fallbackLanguage, $messages, $messageFile);
|
||||
} elseif ($language === $fallbackSourceLanguage) {
|
||||
$messages = $this->loadFallbackMessages($category, $this->sourceLanguage, $messages, $messageFile);
|
||||
} else {
|
||||
if ($messages === null) {
|
||||
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
return (array) $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method is normally called by [[loadMessages]] to load the fallback messages for the language.
|
||||
* Method tries to load the $category messages for the $fallbackLanguage and adds them to the $messages array.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $fallbackLanguage the target fallback language
|
||||
* @param array $messages the array of previously loaded translation messages.
|
||||
* The keys are original messages, and the values are the translated messages.
|
||||
* @param string $originalMessageFile the path to the file with messages. Used to log an error message
|
||||
* in case when no translations were found.
|
||||
* @return array the loaded messages. The keys are original messages, and the values are the translated messages.
|
||||
* @since 2.0.7
|
||||
*/
|
||||
protected function loadFallbackMessages($category, $fallbackLanguage, $messages, $originalMessageFile)
|
||||
{
|
||||
$fallbackMessageFile = $this->getMessageFilePath($fallbackLanguage);
|
||||
$fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile, $category);
|
||||
|
||||
if (
|
||||
$messages === null && $fallbackMessages === null
|
||||
&& $fallbackLanguage !== $this->sourceLanguage
|
||||
&& $fallbackLanguage !== substr($this->sourceLanguage, 0, 2)
|
||||
) {
|
||||
Yii::error("The message file for category '$category' does not exist: $originalMessageFile "
|
||||
. "Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
|
||||
} elseif (empty($messages)) {
|
||||
return $fallbackMessages;
|
||||
} elseif (!empty($fallbackMessages)) {
|
||||
foreach ($fallbackMessages as $key => $value) {
|
||||
if (!empty($value) && empty($messages[$key])) {
|
||||
$messages[$key] = $fallbackMessages[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (array) $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message file path for the specified language and category.
|
||||
*
|
||||
* @param string $language the target language
|
||||
* @return string path to message file
|
||||
*/
|
||||
protected function getMessageFilePath($language)
|
||||
{
|
||||
$messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog;
|
||||
if ($this->useMoFile) {
|
||||
$messageFile .= self::MO_FILE_EXT;
|
||||
} else {
|
||||
$messageFile .= self::PO_FILE_EXT;
|
||||
}
|
||||
|
||||
return $messageFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified language and category or returns null if file doesn't exist.
|
||||
*
|
||||
* @param string $messageFile path to message file
|
||||
* @param string $category the message category
|
||||
* @return array|null array of messages or null if file not found
|
||||
*/
|
||||
protected function loadMessagesFromFile($messageFile, $category)
|
||||
{
|
||||
if (is_file($messageFile)) {
|
||||
if ($this->useMoFile) {
|
||||
$gettextFile = new GettextMoFile(['useBigEndian' => $this->useBigEndian]);
|
||||
} else {
|
||||
$gettextFile = new GettextPoFile();
|
||||
}
|
||||
$messages = $gettextFile->load($messageFile, $category);
|
||||
if (!is_array($messages)) {
|
||||
$messages = [];
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
275
vendor/yiisoft/yii2/i18n/GettextMoFile.php
vendored
Normal file
275
vendor/yiisoft/yii2/i18n/GettextMoFile.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use yii\base\Exception;
|
||||
|
||||
/**
|
||||
* GettextMoFile represents an MO Gettext message file.
|
||||
*
|
||||
* This class is written by adapting Michael's Gettext_MO class in PEAR.
|
||||
* Please refer to the following license terms.
|
||||
*
|
||||
* Copyright (c) 2004-2005, Michael Wallner <mike@iworks.at>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class GettextMoFile extends GettextFile
|
||||
{
|
||||
/**
|
||||
* @var bool whether to use big-endian when reading and writing an integer.
|
||||
*/
|
||||
public $useBigEndian = false;
|
||||
|
||||
|
||||
/**
|
||||
* Loads messages from an MO file.
|
||||
* @param string $filePath file path
|
||||
* @param string $context message context
|
||||
* @return array message translations. Array keys are source messages and array values are translated messages:
|
||||
* source message => translated message.
|
||||
* @throws Exception if unable to read the MO file
|
||||
*/
|
||||
public function load($filePath, $context)
|
||||
{
|
||||
if (false === ($fileHandle = @fopen($filePath, 'rb'))) {
|
||||
throw new Exception('Unable to read file "' . $filePath . '".');
|
||||
}
|
||||
if (false === @flock($fileHandle, LOCK_SH)) {
|
||||
throw new Exception('Unable to lock file "' . $filePath . '" for reading.');
|
||||
}
|
||||
|
||||
// magic
|
||||
$array = unpack('c', $this->readBytes($fileHandle, 4));
|
||||
$magic = current($array);
|
||||
if ($magic == -34) {
|
||||
$this->useBigEndian = false;
|
||||
} elseif ($magic == -107) {
|
||||
$this->useBigEndian = true;
|
||||
} else {
|
||||
throw new Exception('Invalid MO file: ' . $filePath . ' (magic: ' . $magic . ').');
|
||||
}
|
||||
|
||||
// revision
|
||||
$revision = $this->readInteger($fileHandle);
|
||||
if ($revision !== 0) {
|
||||
throw new Exception('Invalid MO file revision: ' . $revision . '.');
|
||||
}
|
||||
|
||||
$count = $this->readInteger($fileHandle);
|
||||
$sourceOffset = $this->readInteger($fileHandle);
|
||||
$targetOffset = $this->readInteger($fileHandle);
|
||||
|
||||
$sourceLengths = [];
|
||||
$sourceOffsets = [];
|
||||
fseek($fileHandle, $sourceOffset);
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$sourceLengths[] = $this->readInteger($fileHandle);
|
||||
$sourceOffsets[] = $this->readInteger($fileHandle);
|
||||
}
|
||||
|
||||
$targetLengths = [];
|
||||
$targetOffsets = [];
|
||||
fseek($fileHandle, $targetOffset);
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$targetLengths[] = $this->readInteger($fileHandle);
|
||||
$targetOffsets[] = $this->readInteger($fileHandle);
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$id = $this->readString($fileHandle, $sourceLengths[$i], $sourceOffsets[$i]);
|
||||
$separatorPosition = strpos($id, chr(4));
|
||||
|
||||
|
||||
if ((!$context && $separatorPosition === false) || ($context && $separatorPosition !== false && strncmp($id, $context, $separatorPosition) === 0)) {
|
||||
if ($separatorPosition !== false) {
|
||||
$id = substr($id, $separatorPosition + 1);
|
||||
}
|
||||
|
||||
$message = $this->readString($fileHandle, $targetLengths[$i], $targetOffsets[$i]);
|
||||
$messages[$id] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
@flock($fileHandle, LOCK_UN);
|
||||
@fclose($fileHandle);
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves messages to an MO file.
|
||||
* @param string $filePath file path
|
||||
* @param array $messages message translations. Array keys are source messages and array values are
|
||||
* translated messages: source message => translated message. Note if the message has a context,
|
||||
* the message ID must be prefixed with the context with chr(4) as the separator.
|
||||
* @throws Exception if unable to save the MO file
|
||||
*/
|
||||
public function save($filePath, $messages)
|
||||
{
|
||||
if (false === ($fileHandle = @fopen($filePath, 'wb'))) {
|
||||
throw new Exception('Unable to write file "' . $filePath . '".');
|
||||
}
|
||||
if (false === @flock($fileHandle, LOCK_EX)) {
|
||||
throw new Exception('Unable to lock file "' . $filePath . '" for reading.');
|
||||
}
|
||||
|
||||
// magic
|
||||
if ($this->useBigEndian) {
|
||||
$this->writeBytes($fileHandle, pack('c*', 0x95, 0x04, 0x12, 0xde)); // -107
|
||||
} else {
|
||||
$this->writeBytes($fileHandle, pack('c*', 0xde, 0x12, 0x04, 0x95)); // -34
|
||||
}
|
||||
|
||||
// revision
|
||||
$this->writeInteger($fileHandle, 0);
|
||||
|
||||
// message count
|
||||
$messageCount = count($messages);
|
||||
$this->writeInteger($fileHandle, $messageCount);
|
||||
|
||||
// offset of source message table
|
||||
$offset = 28;
|
||||
$this->writeInteger($fileHandle, $offset);
|
||||
$offset += $messageCount * 8;
|
||||
$this->writeInteger($fileHandle, $offset);
|
||||
|
||||
// hashtable size, omitted
|
||||
$this->writeInteger($fileHandle, 0);
|
||||
$offset += $messageCount * 8;
|
||||
$this->writeInteger($fileHandle, $offset);
|
||||
|
||||
// length and offsets for source messages
|
||||
foreach (array_keys($messages) as $id) {
|
||||
$length = strlen($id);
|
||||
$this->writeInteger($fileHandle, $length);
|
||||
$this->writeInteger($fileHandle, $offset);
|
||||
$offset += $length + 1;
|
||||
}
|
||||
|
||||
// length and offsets for target messages
|
||||
foreach ($messages as $message) {
|
||||
$length = strlen($message);
|
||||
$this->writeInteger($fileHandle, $length);
|
||||
$this->writeInteger($fileHandle, $offset);
|
||||
$offset += $length + 1;
|
||||
}
|
||||
|
||||
// source messages
|
||||
foreach (array_keys($messages) as $id) {
|
||||
$this->writeString($fileHandle, $id);
|
||||
}
|
||||
|
||||
// target messages
|
||||
foreach ($messages as $message) {
|
||||
$this->writeString($fileHandle, $message);
|
||||
}
|
||||
|
||||
@flock($fileHandle, LOCK_UN);
|
||||
@fclose($fileHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one or several bytes.
|
||||
* @param resource $fileHandle to read from
|
||||
* @param int $byteCount to be read
|
||||
* @return string bytes
|
||||
*/
|
||||
protected function readBytes($fileHandle, $byteCount = 1)
|
||||
{
|
||||
if ($byteCount > 0) {
|
||||
return fread($fileHandle, $byteCount);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes.
|
||||
* @param resource $fileHandle to write to
|
||||
* @param string $bytes to be written
|
||||
* @return int how many bytes are written
|
||||
*/
|
||||
protected function writeBytes($fileHandle, $bytes)
|
||||
{
|
||||
return fwrite($fileHandle, $bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 4-byte integer.
|
||||
* @param resource $fileHandle to read from
|
||||
* @return int the result
|
||||
*/
|
||||
protected function readInteger($fileHandle)
|
||||
{
|
||||
$array = unpack($this->useBigEndian ? 'N' : 'V', $this->readBytes($fileHandle, 4));
|
||||
|
||||
return current($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 4-byte integer.
|
||||
* @param resource $fileHandle to write to
|
||||
* @param int $integer to be written
|
||||
* @return int how many bytes are written
|
||||
*/
|
||||
protected function writeInteger($fileHandle, $integer)
|
||||
{
|
||||
return $this->writeBytes($fileHandle, pack($this->useBigEndian ? 'N' : 'V', (int) $integer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string.
|
||||
* @param resource $fileHandle file handle
|
||||
* @param int $length of the string
|
||||
* @param int $offset of the string in the file. If null, it reads from the current position.
|
||||
* @return string the result
|
||||
*/
|
||||
protected function readString($fileHandle, $length, $offset = null)
|
||||
{
|
||||
if ($offset !== null) {
|
||||
fseek($fileHandle, $offset);
|
||||
}
|
||||
|
||||
return $this->readBytes($fileHandle, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string.
|
||||
* @param resource $fileHandle to write to
|
||||
* @param string $string to be written
|
||||
* @return int how many bytes are written
|
||||
*/
|
||||
protected function writeString($fileHandle, $string)
|
||||
{
|
||||
return $this->writeBytes($fileHandle, $string . "\0");
|
||||
}
|
||||
}
|
||||
113
vendor/yiisoft/yii2/i18n/GettextPoFile.php
vendored
Normal file
113
vendor/yiisoft/yii2/i18n/GettextPoFile.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* GettextPoFile represents a PO Gettext message file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class GettextPoFile extends GettextFile
|
||||
{
|
||||
/**
|
||||
* Loads messages from a PO file.
|
||||
* @param string $filePath file path
|
||||
* @param string $context message context
|
||||
* @return array message translations. Array keys are source messages and array values are translated messages:
|
||||
* source message => translated message.
|
||||
*/
|
||||
public function load($filePath, $context)
|
||||
{
|
||||
$pattern = '/(msgctxt\s+"(.*?(?<!\\\\))")?\s+' // context
|
||||
. 'msgid\s+((?:".*(?<!\\\\)"\s*)+)\s+' // message ID, i.e. original string
|
||||
. 'msgstr\s+((?:".*(?<!\\\\)"\s*)+)/'; // translated string
|
||||
$content = file_get_contents($filePath);
|
||||
$matches = [];
|
||||
$matchCount = preg_match_all($pattern, $content, $matches);
|
||||
|
||||
$messages = [];
|
||||
for ($i = 0; $i < $matchCount; ++$i) {
|
||||
if ($matches[2][$i] === $context) {
|
||||
$id = $this->decode($matches[3][$i]);
|
||||
$message = $this->decode($matches[4][$i]);
|
||||
$messages[$id] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves messages to a PO file.
|
||||
* @param string $filePath file path
|
||||
* @param array $messages message translations. Array keys are source messages and array values are
|
||||
* translated messages: source message => translated message. Note if the message has a context,
|
||||
* the message ID must be prefixed with the context with chr(4) as the separator.
|
||||
*/
|
||||
public function save($filePath, $messages)
|
||||
{
|
||||
$language = str_replace('-', '_', basename(dirname($filePath)));
|
||||
$headers = [
|
||||
'msgid ""',
|
||||
'msgstr ""',
|
||||
'"Project-Id-Version: \n"',
|
||||
'"POT-Creation-Date: \n"',
|
||||
'"PO-Revision-Date: \n"',
|
||||
'"Last-Translator: \n"',
|
||||
'"Language-Team: \n"',
|
||||
'"Language: ' . $language . '\n"',
|
||||
'"MIME-Version: 1.0\n"',
|
||||
'"Content-Type: text/plain; charset=' . Yii::$app->charset . '\n"',
|
||||
'"Content-Transfer-Encoding: 8bit\n"',
|
||||
];
|
||||
$content = implode("\n", $headers) . "\n\n";
|
||||
foreach ($messages as $id => $message) {
|
||||
$separatorPosition = strpos($id, chr(4));
|
||||
if ($separatorPosition !== false) {
|
||||
$content .= 'msgctxt "' . substr($id, 0, $separatorPosition) . "\"\n";
|
||||
$id = substr($id, $separatorPosition + 1);
|
||||
}
|
||||
$content .= 'msgid "' . $this->encode($id) . "\"\n";
|
||||
$content .= 'msgstr "' . $this->encode($message) . "\"\n\n";
|
||||
}
|
||||
file_put_contents($filePath, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes special characters in a message.
|
||||
* @param string $string message to be encoded
|
||||
* @return string the encoded message
|
||||
*/
|
||||
protected function encode($string)
|
||||
{
|
||||
return str_replace(
|
||||
['"', "\n", "\t", "\r"],
|
||||
['\\"', '\\n', '\\t', '\\r'],
|
||||
$string
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes special characters in a message.
|
||||
* @param string $string message to be decoded
|
||||
* @return string the decoded message
|
||||
*/
|
||||
protected function decode($string)
|
||||
{
|
||||
$string = preg_replace(
|
||||
['/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\\"/'],
|
||||
['', "\n", "\r", "\t", '"'],
|
||||
$string
|
||||
);
|
||||
|
||||
return substr(rtrim($string), 1, -1);
|
||||
}
|
||||
}
|
||||
203
vendor/yiisoft/yii2/i18n/I18N.php
vendored
Normal file
203
vendor/yiisoft/yii2/i18n/I18N.php
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* I18N provides features related with internationalization (I18N) and localization (L10N).
|
||||
*
|
||||
* I18N is configured as an application component in [[\yii\base\Application]] by default.
|
||||
* You can access that instance via `Yii::$app->i18n`.
|
||||
*
|
||||
* @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU
|
||||
* message format. Note that the type of this property differs in getter and setter. See
|
||||
* [[getMessageFormatter()]] and [[setMessageFormatter()]] for details.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class I18N extends Component
|
||||
{
|
||||
/**
|
||||
* @var array list of [[MessageSource]] configurations or objects. The array keys are message
|
||||
* category patterns, and the array values are the corresponding [[MessageSource]] objects or the configurations
|
||||
* for creating the [[MessageSource]] objects.
|
||||
*
|
||||
* The message category patterns can contain the wildcard `*` at the end to match multiple categories with the same prefix.
|
||||
* For example, `app/*` matches both `app/cat1` and `app/cat2`.
|
||||
*
|
||||
* The `*` category pattern will match all categories that do not match any other category patterns.
|
||||
*
|
||||
* This property may be modified on the fly by extensions who want to have their own message sources
|
||||
* registered under their own namespaces.
|
||||
*
|
||||
* The category `yii` and `app` are always defined. The former refers to the messages used in the Yii core
|
||||
* framework code, while the latter refers to the default message category for custom application code.
|
||||
* By default, both of these categories use [[PhpMessageSource]] and the corresponding message files are
|
||||
* stored under `@yii/messages` and `@app/messages`, respectively.
|
||||
*
|
||||
* You may override the configuration of both categories.
|
||||
*/
|
||||
public $translations;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the component by configuring the default message categories.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if (!isset($this->translations['yii']) && !isset($this->translations['yii*'])) {
|
||||
$this->translations['yii'] = [
|
||||
'class' => 'yii\i18n\PhpMessageSource',
|
||||
'sourceLanguage' => 'en-US',
|
||||
'basePath' => '@yii/messages',
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($this->translations['app']) && !isset($this->translations['app*'])) {
|
||||
$this->translations['app'] = [
|
||||
'class' => 'yii\i18n\PhpMessageSource',
|
||||
'sourceLanguage' => Yii::$app->sourceLanguage,
|
||||
'basePath' => '@app/messages',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a message to the specified language.
|
||||
*
|
||||
* After translation the message will be formatted using [[MessageFormatter]] if it contains
|
||||
* ICU message format and `$params` are not empty.
|
||||
*
|
||||
* @param string $category the message category.
|
||||
* @param string $message the message to be translated.
|
||||
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
|
||||
* @param string $language the language code (e.g. `en-US`, `en`).
|
||||
* @return string the translated and formatted message.
|
||||
*/
|
||||
public function translate($category, $message, $params, $language)
|
||||
{
|
||||
$messageSource = $this->getMessageSource($category);
|
||||
$translation = $messageSource->translate($category, $message, $language);
|
||||
if ($translation === false) {
|
||||
return $this->format($message, $params, $messageSource->sourceLanguage);
|
||||
}
|
||||
|
||||
return $this->format($translation, $params, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a message using [[MessageFormatter]].
|
||||
*
|
||||
* @param string $message the message to be formatted.
|
||||
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
|
||||
* @param string $language the language code (e.g. `en-US`, `en`).
|
||||
* @return string the formatted message.
|
||||
*/
|
||||
public function format($message, $params, $language)
|
||||
{
|
||||
$params = (array) $params;
|
||||
if ($params === []) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (preg_match('~{\s*[\w.]+\s*,~u', $message)) {
|
||||
$formatter = $this->getMessageFormatter();
|
||||
$result = $formatter->format($message, $params, $language);
|
||||
if ($result === false) {
|
||||
$errorMessage = $formatter->getErrorMessage();
|
||||
Yii::warning("Formatting message for language '$language' failed with error: $errorMessage. The message being formatted was: $message.", __METHOD__);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$p = [];
|
||||
foreach ($params as $name => $value) {
|
||||
$p['{' . $name . '}'] = $value;
|
||||
}
|
||||
|
||||
return strtr($message, $p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string|array|MessageFormatter
|
||||
*/
|
||||
private $_messageFormatter;
|
||||
|
||||
/**
|
||||
* Returns the message formatter instance.
|
||||
* @return MessageFormatter the message formatter to be used to format message via ICU message format.
|
||||
*/
|
||||
public function getMessageFormatter()
|
||||
{
|
||||
if ($this->_messageFormatter === null) {
|
||||
$this->_messageFormatter = new MessageFormatter();
|
||||
} elseif (is_array($this->_messageFormatter) || is_string($this->_messageFormatter)) {
|
||||
$this->_messageFormatter = Yii::createObject($this->_messageFormatter);
|
||||
}
|
||||
|
||||
return $this->_messageFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array|MessageFormatter $value the message formatter to be used to format message via ICU message format.
|
||||
* Can be given as array or string configuration that will be given to [[Yii::createObject]] to create an instance
|
||||
* or a [[MessageFormatter]] instance.
|
||||
*/
|
||||
public function setMessageFormatter($value)
|
||||
{
|
||||
$this->_messageFormatter = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message source for the given category.
|
||||
* @param string $category the category name.
|
||||
* @return MessageSource the message source for the given category.
|
||||
* @throws InvalidConfigException if there is no message source available for the specified category.
|
||||
*/
|
||||
public function getMessageSource($category)
|
||||
{
|
||||
if (isset($this->translations[$category])) {
|
||||
$source = $this->translations[$category];
|
||||
if ($source instanceof MessageSource) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
return $this->translations[$category] = Yii::createObject($source);
|
||||
}
|
||||
// try wildcard matching
|
||||
foreach ($this->translations as $pattern => $source) {
|
||||
if (strpos($pattern, '*') > 0 && strpos($category, rtrim($pattern, '*')) === 0) {
|
||||
if ($source instanceof MessageSource) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
return $this->translations[$category] = $this->translations[$pattern] = Yii::createObject($source);
|
||||
}
|
||||
}
|
||||
|
||||
// match '*' in the last
|
||||
if (isset($this->translations['*'])) {
|
||||
$source = $this->translations['*'];
|
||||
if ($source instanceof MessageSource) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
return $this->translations[$category] = $this->translations['*'] = Yii::createObject($source);
|
||||
}
|
||||
|
||||
throw new InvalidConfigException("Unable to locate message source for category '$category'.");
|
||||
}
|
||||
}
|
||||
64
vendor/yiisoft/yii2/i18n/Locale.php
vendored
Normal file
64
vendor/yiisoft/yii2/i18n/Locale.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* Locale provides various locale information via convenient methods.
|
||||
*
|
||||
* The class requires [PHP intl extension](http://php.net/manual/en/book.intl.php) to be installed.
|
||||
* @since 2.0.14
|
||||
*
|
||||
* @property string $currencySymbol This property is read-only.
|
||||
*
|
||||
*/
|
||||
class Locale extends Component
|
||||
{
|
||||
/**
|
||||
* @var string the locale ID.
|
||||
* If not set, [[\yii\base\Application::language]] will be used.
|
||||
*/
|
||||
public $locale;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new InvalidConfigException('Locale component requires PHP intl extension to be installed.');
|
||||
}
|
||||
|
||||
if ($this->locale === null) {
|
||||
$this->locale = Yii::$app->language;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a currency symbol
|
||||
*
|
||||
* @param string $currencyCode the 3-letter ISO 4217 currency code to get symbol for. If null,
|
||||
* method will attempt using currency code from [[locale]].
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrencySymbol($currencyCode = null)
|
||||
{
|
||||
$locale = $this->locale;
|
||||
|
||||
if ($currencyCode !== null) {
|
||||
$locale .= '@currency=' . $currencyCode;
|
||||
}
|
||||
|
||||
$formatter = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
|
||||
return $formatter->getSymbol(\NumberFormatter::CURRENCY_SYMBOL);
|
||||
}
|
||||
}
|
||||
440
vendor/yiisoft/yii2/i18n/MessageFormatter.php
vendored
Normal file
440
vendor/yiisoft/yii2/i18n/MessageFormatter.php
vendored
Normal file
@@ -0,0 +1,440 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\NotSupportedException;
|
||||
|
||||
/**
|
||||
* MessageFormatter allows formatting messages via [ICU message format](http://userguide.icu-project.org/formatparse/messages).
|
||||
*
|
||||
* This class enhances the message formatter class provided by the PHP intl extension.
|
||||
*
|
||||
* The following enhancements are provided:
|
||||
*
|
||||
* - It accepts named arguments and mixed numeric and named arguments.
|
||||
* - Issues no error when an insufficient number of arguments have been provided. Instead, the placeholders will not be
|
||||
* substituted.
|
||||
* - Fixes PHP 5.5 weird placeholder replacement in case no arguments are provided at all (https://bugs.php.net/bug.php?id=65920).
|
||||
* - Offers limited support for message formatting in case PHP intl extension is not installed.
|
||||
* However it is highly recommended that you install [PHP intl extension](http://php.net/manual/en/book.intl.php) if you want
|
||||
* to use MessageFormatter features.
|
||||
*
|
||||
* The fallback implementation only supports the following message formats:
|
||||
* - plural formatting for english ('one' and 'other' selectors)
|
||||
* - select format
|
||||
* - simple parameters
|
||||
* - integer number parameters
|
||||
*
|
||||
* The fallback implementation does NOT support the ['apostrophe-friendly' syntax](http://www.php.net/manual/en/messageformatter.formatmessage.php).
|
||||
* Also messages that are working with the fallback implementation are not necessarily compatible with the
|
||||
* PHP intl MessageFormatter so do not rely on the fallback if you are able to install intl extension somehow.
|
||||
*
|
||||
* @property string $errorCode Code of the last error. This property is read-only.
|
||||
* @property string $errorMessage Description of the last error. This property is read-only.
|
||||
*
|
||||
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class MessageFormatter extends Component
|
||||
{
|
||||
private $_errorCode = 0;
|
||||
private $_errorMessage = '';
|
||||
|
||||
|
||||
/**
|
||||
* Get the error code from the last operation.
|
||||
* @link http://php.net/manual/en/messageformatter.geterrorcode.php
|
||||
* @return string Code of the last error.
|
||||
*/
|
||||
public function getErrorCode()
|
||||
{
|
||||
return $this->_errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error text from the last operation.
|
||||
* @link http://php.net/manual/en/messageformatter.geterrormessage.php
|
||||
* @return string Description of the last error.
|
||||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
return $this->_errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a message via [ICU message format](http://userguide.icu-project.org/formatparse/messages).
|
||||
*
|
||||
* It uses the PHP intl extension's [MessageFormatter](http://www.php.net/manual/en/class.messageformatter.php)
|
||||
* and works around some issues.
|
||||
* If PHP intl is not installed a fallback will be used that supports a subset of the ICU message format.
|
||||
*
|
||||
* @param string $pattern The pattern string to insert parameters into.
|
||||
* @param array $params The array of name value pairs to insert into the format string.
|
||||
* @param string $language The locale to use for formatting locale-dependent parts
|
||||
* @return string|false The formatted pattern string or `false` if an error occurred
|
||||
*/
|
||||
public function format($pattern, $params, $language)
|
||||
{
|
||||
$this->_errorCode = 0;
|
||||
$this->_errorMessage = '';
|
||||
|
||||
if ($params === []) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
if (!class_exists('MessageFormatter', false)) {
|
||||
return $this->fallbackFormat($pattern, $params, $language);
|
||||
}
|
||||
|
||||
// replace named arguments (https://github.com/yiisoft/yii2/issues/9678)
|
||||
$newParams = [];
|
||||
$pattern = $this->replaceNamedArguments($pattern, $params, $newParams);
|
||||
$params = $newParams;
|
||||
|
||||
try {
|
||||
$formatter = new \MessageFormatter($language, $pattern);
|
||||
|
||||
if ($formatter === null) {
|
||||
// formatter may be null in PHP 5.x
|
||||
$this->_errorCode = intl_get_error_code();
|
||||
$this->_errorMessage = 'Message pattern is invalid: ' . intl_get_error_message();
|
||||
return false;
|
||||
}
|
||||
} catch (\IntlException $e) {
|
||||
// IntlException is thrown since PHP 7
|
||||
$this->_errorCode = $e->getCode();
|
||||
$this->_errorMessage = 'Message pattern is invalid: ' . $e->getMessage();
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
// Exception is thrown by HHVM
|
||||
$this->_errorCode = $e->getCode();
|
||||
$this->_errorMessage = 'Message pattern is invalid: ' . $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $formatter->format($params);
|
||||
|
||||
if ($result === false) {
|
||||
$this->_errorCode = $formatter->getErrorCode();
|
||||
$this->_errorMessage = $formatter->getErrorMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an input string according to an [ICU message format](http://userguide.icu-project.org/formatparse/messages) pattern.
|
||||
*
|
||||
* It uses the PHP intl extension's [MessageFormatter::parse()](http://www.php.net/manual/en/messageformatter.parsemessage.php)
|
||||
* and adds support for named arguments.
|
||||
* Usage of this method requires PHP intl extension to be installed.
|
||||
*
|
||||
* @param string $pattern The pattern to use for parsing the message.
|
||||
* @param string $message The message to parse, conforming to the pattern.
|
||||
* @param string $language The locale to use for formatting locale-dependent parts
|
||||
* @return array|bool An array containing items extracted, or `FALSE` on error.
|
||||
* @throws \yii\base\NotSupportedException when PHP intl extension is not installed.
|
||||
*/
|
||||
public function parse($pattern, $message, $language)
|
||||
{
|
||||
$this->_errorCode = 0;
|
||||
$this->_errorMessage = '';
|
||||
|
||||
if (!class_exists('MessageFormatter', false)) {
|
||||
throw new NotSupportedException('You have to install PHP intl extension to use this feature.');
|
||||
}
|
||||
|
||||
// replace named arguments
|
||||
if (($tokens = self::tokenizePattern($pattern)) === false) {
|
||||
$this->_errorCode = -1;
|
||||
$this->_errorMessage = 'Message pattern is invalid.';
|
||||
|
||||
return false;
|
||||
}
|
||||
$map = [];
|
||||
foreach ($tokens as $i => $token) {
|
||||
if (is_array($token)) {
|
||||
$param = trim($token[0]);
|
||||
if (!isset($map[$param])) {
|
||||
$map[$param] = count($map);
|
||||
}
|
||||
$token[0] = $map[$param];
|
||||
$tokens[$i] = '{' . implode(',', $token) . '}';
|
||||
}
|
||||
}
|
||||
$pattern = implode('', $tokens);
|
||||
$map = array_flip($map);
|
||||
|
||||
$formatter = new \MessageFormatter($language, $pattern);
|
||||
if ($formatter === null) {
|
||||
$this->_errorCode = -1;
|
||||
$this->_errorMessage = 'Message pattern is invalid.';
|
||||
|
||||
return false;
|
||||
}
|
||||
$result = $formatter->parse($message);
|
||||
if ($result === false) {
|
||||
$this->_errorCode = $formatter->getErrorCode();
|
||||
$this->_errorMessage = $formatter->getErrorMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($result as $key => $value) {
|
||||
$values[$map[$key]] = $value;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace named placeholders with numeric placeholders and quote unused.
|
||||
*
|
||||
* @param string $pattern The pattern string to replace things into.
|
||||
* @param array $givenParams The array of values to insert into the format string.
|
||||
* @param array $resultingParams Modified array of parameters.
|
||||
* @param array $map
|
||||
* @return string The pattern string with placeholders replaced.
|
||||
*/
|
||||
private function replaceNamedArguments($pattern, $givenParams, &$resultingParams = [], &$map = [])
|
||||
{
|
||||
if (($tokens = self::tokenizePattern($pattern)) === false) {
|
||||
return false;
|
||||
}
|
||||
foreach ($tokens as $i => $token) {
|
||||
if (!is_array($token)) {
|
||||
continue;
|
||||
}
|
||||
$param = trim($token[0]);
|
||||
if (array_key_exists($param, $givenParams)) {
|
||||
// if param is given, replace it with a number
|
||||
if (!isset($map[$param])) {
|
||||
$map[$param] = count($map);
|
||||
// make sure only used params are passed to format method
|
||||
$resultingParams[$map[$param]] = $givenParams[$param];
|
||||
}
|
||||
$token[0] = $map[$param];
|
||||
$quote = '';
|
||||
} else {
|
||||
// quote unused token
|
||||
$quote = "'";
|
||||
}
|
||||
$type = isset($token[1]) ? trim($token[1]) : 'none';
|
||||
// replace plural and select format recursively
|
||||
if ($type === 'plural' || $type === 'select') {
|
||||
if (!isset($token[2])) {
|
||||
return false;
|
||||
}
|
||||
if (($subtokens = self::tokenizePattern($token[2])) === false) {
|
||||
return false;
|
||||
}
|
||||
$c = count($subtokens);
|
||||
for ($k = 0; $k + 1 < $c; $k++) {
|
||||
if (is_array($subtokens[$k]) || !is_array($subtokens[++$k])) {
|
||||
return false;
|
||||
}
|
||||
$subpattern = $this->replaceNamedArguments(implode(',', $subtokens[$k]), $givenParams, $resultingParams, $map);
|
||||
$subtokens[$k] = $quote . '{' . $quote . $subpattern . $quote . '}' . $quote;
|
||||
}
|
||||
$token[2] = implode('', $subtokens);
|
||||
}
|
||||
$tokens[$i] = $quote . '{' . $quote . implode(',', $token) . $quote . '}' . $quote;
|
||||
}
|
||||
|
||||
return implode('', $tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback implementation for MessageFormatter::formatMessage.
|
||||
* @param string $pattern The pattern string to insert things into.
|
||||
* @param array $args The array of values to insert into the format string
|
||||
* @param string $locale The locale to use for formatting locale-dependent parts
|
||||
* @return false|string The formatted pattern string or `false` if an error occurred
|
||||
*/
|
||||
protected function fallbackFormat($pattern, $args, $locale)
|
||||
{
|
||||
if (($tokens = self::tokenizePattern($pattern)) === false) {
|
||||
$this->_errorCode = -1;
|
||||
$this->_errorMessage = 'Message pattern is invalid.';
|
||||
|
||||
return false;
|
||||
}
|
||||
foreach ($tokens as $i => $token) {
|
||||
if (is_array($token)) {
|
||||
if (($tokens[$i] = $this->parseToken($token, $args, $locale)) === false) {
|
||||
$this->_errorCode = -1;
|
||||
$this->_errorMessage = 'Message pattern is invalid.';
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode('', $tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a pattern by separating normal text from replaceable patterns.
|
||||
* @param string $pattern patter to tokenize
|
||||
* @return array|bool array of tokens or false on failure
|
||||
*/
|
||||
private static function tokenizePattern($pattern)
|
||||
{
|
||||
$charset = Yii::$app ? Yii::$app->charset : 'UTF-8';
|
||||
$depth = 1;
|
||||
if (($start = $pos = mb_strpos($pattern, '{', 0, $charset)) === false) {
|
||||
return [$pattern];
|
||||
}
|
||||
$tokens = [mb_substr($pattern, 0, $pos, $charset)];
|
||||
while (true) {
|
||||
$open = mb_strpos($pattern, '{', $pos + 1, $charset);
|
||||
$close = mb_strpos($pattern, '}', $pos + 1, $charset);
|
||||
if ($open === false && $close === false) {
|
||||
break;
|
||||
}
|
||||
if ($open === false) {
|
||||
$open = mb_strlen($pattern, $charset);
|
||||
}
|
||||
if ($close > $open) {
|
||||
$depth++;
|
||||
$pos = $open;
|
||||
} else {
|
||||
$depth--;
|
||||
$pos = $close;
|
||||
}
|
||||
if ($depth === 0) {
|
||||
$tokens[] = explode(',', mb_substr($pattern, $start + 1, $pos - $start - 1, $charset), 3);
|
||||
$start = $pos + 1;
|
||||
$tokens[] = mb_substr($pattern, $start, $open - $start, $charset);
|
||||
$start = $open;
|
||||
}
|
||||
|
||||
if ($depth !== 0 && ($open === false || $close === false)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($depth !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a token.
|
||||
* @param array $token the token to parse
|
||||
* @param array $args arguments to replace
|
||||
* @param string $locale the locale
|
||||
* @return bool|string parsed token or false on failure
|
||||
* @throws \yii\base\NotSupportedException when unsupported formatting is used.
|
||||
*/
|
||||
private function parseToken($token, $args, $locale)
|
||||
{
|
||||
// parsing pattern based on ICU grammar:
|
||||
// http://icu-project.org/apiref/icu4c/classMessageFormat.html#details
|
||||
$charset = Yii::$app ? Yii::$app->charset : 'UTF-8';
|
||||
$param = trim($token[0]);
|
||||
if (isset($args[$param])) {
|
||||
$arg = $args[$param];
|
||||
} else {
|
||||
return '{' . implode(',', $token) . '}';
|
||||
}
|
||||
$type = isset($token[1]) ? trim($token[1]) : 'none';
|
||||
switch ($type) {
|
||||
case 'date':
|
||||
case 'time':
|
||||
case 'spellout':
|
||||
case 'ordinal':
|
||||
case 'duration':
|
||||
case 'choice':
|
||||
case 'selectordinal':
|
||||
throw new NotSupportedException("Message format '$type' is not supported. You have to install PHP intl extension to use this feature.");
|
||||
case 'number':
|
||||
$format = isset($token[2]) ? trim($token[2]) : null;
|
||||
if (is_numeric($arg) && ($format === null || $format === 'integer')) {
|
||||
$number = number_format($arg);
|
||||
if ($format === null && ($pos = strpos($arg, '.')) !== false) {
|
||||
// add decimals with unknown length
|
||||
$number .= '.' . substr($arg, $pos + 1);
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
throw new NotSupportedException("Message format 'number' is only supported for integer values. You have to install PHP intl extension to use this feature.");
|
||||
case 'none':
|
||||
return $arg;
|
||||
case 'select':
|
||||
/* http://icu-project.org/apiref/icu4c/classicu_1_1SelectFormat.html
|
||||
selectStyle = (selector '{' message '}')+
|
||||
*/
|
||||
if (!isset($token[2])) {
|
||||
return false;
|
||||
}
|
||||
$select = self::tokenizePattern($token[2]);
|
||||
$c = count($select);
|
||||
$message = false;
|
||||
for ($i = 0; $i + 1 < $c; $i++) {
|
||||
if (is_array($select[$i]) || !is_array($select[$i + 1])) {
|
||||
return false;
|
||||
}
|
||||
$selector = trim($select[$i++]);
|
||||
if ($message === false && $selector === 'other' || $selector == $arg) {
|
||||
$message = implode(',', $select[$i]);
|
||||
}
|
||||
}
|
||||
if ($message !== false) {
|
||||
return $this->fallbackFormat($message, $args, $locale);
|
||||
}
|
||||
break;
|
||||
case 'plural':
|
||||
/* http://icu-project.org/apiref/icu4c/classicu_1_1PluralFormat.html
|
||||
pluralStyle = [offsetValue] (selector '{' message '}')+
|
||||
offsetValue = "offset:" number
|
||||
selector = explicitValue | keyword
|
||||
explicitValue = '=' number // adjacent, no white space in between
|
||||
keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
|
||||
message: see MessageFormat
|
||||
*/
|
||||
if (!isset($token[2])) {
|
||||
return false;
|
||||
}
|
||||
$plural = self::tokenizePattern($token[2]);
|
||||
$c = count($plural);
|
||||
$message = false;
|
||||
$offset = 0;
|
||||
for ($i = 0; $i + 1 < $c; $i++) {
|
||||
if (is_array($plural[$i]) || !is_array($plural[$i + 1])) {
|
||||
return false;
|
||||
}
|
||||
$selector = trim($plural[$i++]);
|
||||
|
||||
if ($i == 1 && strncmp($selector, 'offset:', 7) === 0) {
|
||||
$offset = (int) trim(mb_substr($selector, 7, ($pos = mb_strpos(str_replace(["\n", "\r", "\t"], ' ', $selector), ' ', 7, $charset)) - 7, $charset));
|
||||
$selector = trim(mb_substr($selector, $pos + 1, mb_strlen($selector, $charset), $charset));
|
||||
}
|
||||
if ($message === false && $selector === 'other' ||
|
||||
$selector[0] === '=' && (int) mb_substr($selector, 1, mb_strlen($selector, $charset), $charset) === $arg ||
|
||||
$selector === 'one' && $arg - $offset == 1
|
||||
) {
|
||||
$message = implode(',', str_replace('#', $arg - $offset, $plural[$i]));
|
||||
}
|
||||
}
|
||||
if ($message !== false) {
|
||||
return $this->fallbackFormat($message, $args, $locale);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
125
vendor/yiisoft/yii2/i18n/MessageSource.php
vendored
Normal file
125
vendor/yiisoft/yii2/i18n/MessageSource.php
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
|
||||
/**
|
||||
* MessageSource is the base class for message translation repository classes.
|
||||
*
|
||||
* A message source stores message translations in some persistent storage.
|
||||
*
|
||||
* Child classes should override [[loadMessages()]] to provide translated messages.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class MessageSource extends Component
|
||||
{
|
||||
/**
|
||||
* @event MissingTranslationEvent an event that is triggered when a message translation is not found.
|
||||
*/
|
||||
const EVENT_MISSING_TRANSLATION = 'missingTranslation';
|
||||
|
||||
/**
|
||||
* @var bool whether to force message translation when the source and target languages are the same.
|
||||
* Defaults to false, meaning translation is only performed when source and target languages are different.
|
||||
*/
|
||||
public $forceTranslation = false;
|
||||
/**
|
||||
* @var string the language that the original messages are in. If not set, it will use the value of
|
||||
* [[\yii\base\Application::sourceLanguage]].
|
||||
*/
|
||||
public $sourceLanguage;
|
||||
|
||||
private $_messages = [];
|
||||
|
||||
|
||||
/**
|
||||
* Initializes this component.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->sourceLanguage === null) {
|
||||
$this->sourceLanguage = Yii::$app->sourceLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified language and category.
|
||||
* If translation for specific locale code such as `en-US` isn't found it
|
||||
* tries more generic `en`.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return array the loaded messages. The keys are original messages, and the values
|
||||
* are translated messages.
|
||||
*/
|
||||
protected function loadMessages($category, $language)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a message to the specified language.
|
||||
*
|
||||
* Note that unless [[forceTranslation]] is true, if the target language
|
||||
* is the same as the [[sourceLanguage|source language]], the message
|
||||
* will NOT be translated.
|
||||
*
|
||||
* If a translation is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $message the message to be translated
|
||||
* @param string $language the target language
|
||||
* @return string|bool the translated message or false if translation wasn't found or isn't required
|
||||
*/
|
||||
public function translate($category, $message, $language)
|
||||
{
|
||||
if ($this->forceTranslation || $language !== $this->sourceLanguage) {
|
||||
return $this->translateMessage($category, $message, $language);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the specified message.
|
||||
* If the message is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered.
|
||||
* If there is an event handler, it may provide a [[MissingTranslationEvent::$translatedMessage|fallback translation]].
|
||||
* If no fallback translation is provided this method will return `false`.
|
||||
* @param string $category the category that the message belongs to.
|
||||
* @param string $message the message to be translated.
|
||||
* @param string $language the target language.
|
||||
* @return string|bool the translated message or false if translation wasn't found.
|
||||
*/
|
||||
protected function translateMessage($category, $message, $language)
|
||||
{
|
||||
$key = $language . '/' . $category;
|
||||
if (!isset($this->_messages[$key])) {
|
||||
$this->_messages[$key] = $this->loadMessages($category, $language);
|
||||
}
|
||||
if (isset($this->_messages[$key][$message]) && $this->_messages[$key][$message] !== '') {
|
||||
return $this->_messages[$key][$message];
|
||||
} elseif ($this->hasEventHandlers(self::EVENT_MISSING_TRANSLATION)) {
|
||||
$event = new MissingTranslationEvent([
|
||||
'category' => $category,
|
||||
'message' => $message,
|
||||
'language' => $language,
|
||||
]);
|
||||
$this->trigger(self::EVENT_MISSING_TRANSLATION, $event);
|
||||
if ($event->translatedMessage !== null) {
|
||||
return $this->_messages[$key][$message] = $event->translatedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_messages[$key][$message] = false;
|
||||
}
|
||||
}
|
||||
38
vendor/yiisoft/yii2/i18n/MissingTranslationEvent.php
vendored
Normal file
38
vendor/yiisoft/yii2/i18n/MissingTranslationEvent.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use yii\base\Event;
|
||||
|
||||
/**
|
||||
* MissingTranslationEvent represents the parameter for the [[MessageSource::EVENT_MISSING_TRANSLATION]] event.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class MissingTranslationEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var string the message to be translated. An event handler may use this to provide a fallback translation
|
||||
* and set [[translatedMessage]] if possible.
|
||||
*/
|
||||
public $message;
|
||||
/**
|
||||
* @var string the translated message. An event handler may overwrite this property
|
||||
* with a translated version of [[message]] if possible. If not set (null), it means the message is not translated.
|
||||
*/
|
||||
public $translatedMessage;
|
||||
/**
|
||||
* @var string the category that the message belongs to
|
||||
*/
|
||||
public $category;
|
||||
/**
|
||||
* @var string the language ID (e.g. en-US) that the message is to be translated to
|
||||
*/
|
||||
public $language;
|
||||
}
|
||||
166
vendor/yiisoft/yii2/i18n/PhpMessageSource.php
vendored
Normal file
166
vendor/yiisoft/yii2/i18n/PhpMessageSource.php
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\i18n;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* PhpMessageSource represents a message source that stores translated messages in PHP scripts.
|
||||
*
|
||||
* PhpMessageSource uses PHP arrays to keep message translations.
|
||||
*
|
||||
* - Each PHP script contains one array which stores the message translations in one particular
|
||||
* language and for a single message category;
|
||||
* - Each PHP script is saved as a file named as "[[basePath]]/LanguageID/CategoryName.php";
|
||||
* - Within each PHP script, the message translations are returned as an array like the following:
|
||||
*
|
||||
* ```php
|
||||
* return [
|
||||
* 'original message 1' => 'translated message 1',
|
||||
* 'original message 2' => 'translated message 2',
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* You may use [[fileMap]] to customize the association between category names and the file names.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class PhpMessageSource extends MessageSource
|
||||
{
|
||||
/**
|
||||
* @var string the base path for all translated messages. Defaults to '@app/messages'.
|
||||
*/
|
||||
public $basePath = '@app/messages';
|
||||
/**
|
||||
* @var array mapping between message categories and the corresponding message file paths.
|
||||
* The file paths are relative to [[basePath]]. For example,
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'core' => 'core.php',
|
||||
* 'ext' => 'extensions.php',
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
public $fileMap;
|
||||
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified $language and $category.
|
||||
* If translation for specific locale code such as `en-US` isn't found it
|
||||
* tries more generic `en`. When both are present, the `en-US` messages will be merged
|
||||
* over `en`. See [[loadFallbackMessages]] for details.
|
||||
* If the $language is less specific than [[sourceLanguage]], the method will try to
|
||||
* load the messages for [[sourceLanguage]]. For example: [[sourceLanguage]] is `en-GB`,
|
||||
* $language is `en`. The method will load the messages for `en` and merge them over `en-GB`.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return array the loaded messages. The keys are original messages, and the values are the translated messages.
|
||||
* @see loadFallbackMessages
|
||||
* @see sourceLanguage
|
||||
*/
|
||||
protected function loadMessages($category, $language)
|
||||
{
|
||||
$messageFile = $this->getMessageFilePath($category, $language);
|
||||
$messages = $this->loadMessagesFromFile($messageFile);
|
||||
|
||||
$fallbackLanguage = substr($language, 0, 2);
|
||||
$fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
|
||||
|
||||
if ($language !== $fallbackLanguage) {
|
||||
$messages = $this->loadFallbackMessages($category, $fallbackLanguage, $messages, $messageFile);
|
||||
} elseif ($language === $fallbackSourceLanguage) {
|
||||
$messages = $this->loadFallbackMessages($category, $this->sourceLanguage, $messages, $messageFile);
|
||||
} else {
|
||||
if ($messages === null) {
|
||||
Yii::warning("The message file for category '$category' does not exist: $messageFile", __METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
return (array) $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method is normally called by [[loadMessages]] to load the fallback messages for the language.
|
||||
* Method tries to load the $category messages for the $fallbackLanguage and adds them to the $messages array.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $fallbackLanguage the target fallback language
|
||||
* @param array $messages the array of previously loaded translation messages.
|
||||
* The keys are original messages, and the values are the translated messages.
|
||||
* @param string $originalMessageFile the path to the file with messages. Used to log an error message
|
||||
* in case when no translations were found.
|
||||
* @return array the loaded messages. The keys are original messages, and the values are the translated messages.
|
||||
* @since 2.0.7
|
||||
*/
|
||||
protected function loadFallbackMessages($category, $fallbackLanguage, $messages, $originalMessageFile)
|
||||
{
|
||||
$fallbackMessageFile = $this->getMessageFilePath($category, $fallbackLanguage);
|
||||
$fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile);
|
||||
|
||||
if (
|
||||
$messages === null && $fallbackMessages === null
|
||||
&& $fallbackLanguage !== $this->sourceLanguage
|
||||
&& $fallbackLanguage !== substr($this->sourceLanguage, 0, 2)
|
||||
) {
|
||||
Yii::error("The message file for category '$category' does not exist: $originalMessageFile "
|
||||
. "Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
|
||||
} elseif (empty($messages)) {
|
||||
return $fallbackMessages;
|
||||
} elseif (!empty($fallbackMessages)) {
|
||||
foreach ($fallbackMessages as $key => $value) {
|
||||
if (!empty($value) && empty($messages[$key])) {
|
||||
$messages[$key] = $fallbackMessages[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (array) $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message file path for the specified language and category.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return string path to message file
|
||||
*/
|
||||
protected function getMessageFilePath($category, $language)
|
||||
{
|
||||
$messageFile = Yii::getAlias($this->basePath) . "/$language/";
|
||||
if (isset($this->fileMap[$category])) {
|
||||
$messageFile .= $this->fileMap[$category];
|
||||
} else {
|
||||
$messageFile .= str_replace('\\', '/', $category) . '.php';
|
||||
}
|
||||
|
||||
return $messageFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified language and category or returns null if file doesn't exist.
|
||||
*
|
||||
* @param string $messageFile path to message file
|
||||
* @return array|null array of messages or null if file not found
|
||||
*/
|
||||
protected function loadMessagesFromFile($messageFile)
|
||||
{
|
||||
if (is_file($messageFile)) {
|
||||
$messages = include $messageFile;
|
||||
if (!is_array($messages)) {
|
||||
$messages = [];
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
52
vendor/yiisoft/yii2/i18n/migrations/m150207_210500_i18n_init.php
vendored
Normal file
52
vendor/yiisoft/yii2/i18n/migrations/m150207_210500_i18n_init.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
use yii\db\Migration;
|
||||
|
||||
/**
|
||||
* Initializes i18n messages tables.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @since 2.0.7
|
||||
*/
|
||||
class m150207_210500_i18n_init extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$tableOptions = null;
|
||||
if ($this->db->driverName === 'mysql') {
|
||||
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
|
||||
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
|
||||
}
|
||||
|
||||
$this->createTable('{{%source_message}}', [
|
||||
'id' => $this->primaryKey(),
|
||||
'category' => $this->string(),
|
||||
'message' => $this->text(),
|
||||
], $tableOptions);
|
||||
|
||||
$this->createTable('{{%message}}', [
|
||||
'id' => $this->integer()->notNull(),
|
||||
'language' => $this->string(16)->notNull(),
|
||||
'translation' => $this->text(),
|
||||
], $tableOptions);
|
||||
|
||||
$this->addPrimaryKey('pk_message_id_language', '{{%message}}', ['id', 'language']);
|
||||
$this->addForeignKey('fk_message_source_message', '{{%message}}', 'id', '{{%source_message}}', 'id', 'CASCADE', 'RESTRICT');
|
||||
$this->createIndex('idx_source_message_category', '{{%source_message}}', 'category');
|
||||
$this->createIndex('idx_message_language', '{{%message}}', 'language');
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropForeignKey('fk_message_source_message', '{{%message}}');
|
||||
$this->dropTable('{{%message}}');
|
||||
$this->dropTable('{{%source_message}}');
|
||||
}
|
||||
}
|
||||
35
vendor/yiisoft/yii2/i18n/migrations/schema-mssql.sql
vendored
Normal file
35
vendor/yiisoft/yii2/i18n/migrations/schema-mssql.sql
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Database schema required by \yii\i18n\DbMessageSource.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @since 2.0.7
|
||||
*/
|
||||
|
||||
if object_id('[source_message]', 'U') is not null
|
||||
drop table [source_message];
|
||||
|
||||
if object_id('[message]', 'U') is not null
|
||||
drop table [message];
|
||||
|
||||
CREATE TABLE [source_message]
|
||||
(
|
||||
[id] integer IDENTITY PRIMARY KEY,
|
||||
[category] varchar(255),
|
||||
[message] text
|
||||
);
|
||||
|
||||
CREATE TABLE [message]
|
||||
(
|
||||
[id] integer NOT NULL,
|
||||
[language] varchar(16) NOT NULL,
|
||||
[translation] text
|
||||
);
|
||||
|
||||
ALTER TABLE [message] ADD CONSTRAINT [pk_message_id_language] PRIMARY KEY ([id], [language]);
|
||||
ALTER TABLE [message] ADD CONSTRAINT [fk_message_source_message] FOREIGN KEY ([id]) REFERENCES [source_message] ([id]) ON UPDATE CASCADE ON DELETE NO ACTION;
|
||||
|
||||
CREATE INDEX [idx_message_language] on [message] ([language]);
|
||||
CREATE INDEX [idx_source_message_category] on [source_message] ([category]);
|
||||
33
vendor/yiisoft/yii2/i18n/migrations/schema-mysql.sql
vendored
Normal file
33
vendor/yiisoft/yii2/i18n/migrations/schema-mysql.sql
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Database schema required by \yii\i18n\DbMessageSource.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @since 2.0.7
|
||||
*/
|
||||
|
||||
|
||||
drop table if exists `source_message`;
|
||||
drop table if exists `message`;
|
||||
|
||||
CREATE TABLE `source_message`
|
||||
(
|
||||
`id` integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`category` varchar(255),
|
||||
`message` text
|
||||
);
|
||||
|
||||
CREATE TABLE `message`
|
||||
(
|
||||
`id` integer NOT NULL,
|
||||
`language` varchar(16) NOT NULL,
|
||||
`translation` text
|
||||
);
|
||||
|
||||
ALTER TABLE `message` ADD CONSTRAINT `pk_message_id_language` PRIMARY KEY (`id`, `language`);
|
||||
ALTER TABLE `message` ADD CONSTRAINT `fk_message_source_message` FOREIGN KEY (`id`) REFERENCES `source_message` (`id`) ON UPDATE CASCADE ON DELETE RESTRICT;
|
||||
|
||||
CREATE INDEX idx_message_language ON message (language);
|
||||
CREATE INDEX idx_source_message_category ON source_message (category);
|
||||
33
vendor/yiisoft/yii2/i18n/migrations/schema-oci.sql
vendored
Normal file
33
vendor/yiisoft/yii2/i18n/migrations/schema-oci.sql
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Database schema required by \yii\i18n\DbMessageSource.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @since 2.0.7
|
||||
*/
|
||||
|
||||
|
||||
drop table if exists "source_message";
|
||||
drop table if exists "message";
|
||||
|
||||
CREATE TABLE "source_message"
|
||||
(
|
||||
"id" integer NOT NULL PRIMARY KEY,
|
||||
"category" varchar(255),
|
||||
"message" clob
|
||||
);
|
||||
CREATE SEQUENCE "source_message_SEQ";
|
||||
|
||||
CREATE TABLE "message"
|
||||
(
|
||||
"id" integer NOT NULL,
|
||||
"language" varchar(16) NOT NULL,
|
||||
"translation" clob,
|
||||
primary key ("id", "language"),
|
||||
foreign key ("id") references "source_message" ("id") on delete cascade
|
||||
);
|
||||
|
||||
CREATE INDEX idx_message_language ON "message"("language");
|
||||
CREATE INDEX idx_source_message_category ON "source_message"("category");
|
||||
38
vendor/yiisoft/yii2/i18n/migrations/schema-pgsql.sql
vendored
Normal file
38
vendor/yiisoft/yii2/i18n/migrations/schema-pgsql.sql
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Database schema required by \yii\i18n\DbMessageSource.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @since 2.0.7
|
||||
*/
|
||||
|
||||
|
||||
drop table if exists "source_message";
|
||||
drop table if exists "message";
|
||||
|
||||
CREATE SEQUENCE source_message_seq;
|
||||
|
||||
CREATE TABLE "source_message"
|
||||
(
|
||||
"id" integer NOT NULL PRIMARY KEY DEFAULT nextval('source_message_seq'),
|
||||
"category" varchar(255),
|
||||
"message" text
|
||||
);
|
||||
|
||||
CREATE TABLE "message"
|
||||
(
|
||||
"id" integer NOT NULL,
|
||||
"language" varchar(16) NOT NULL,
|
||||
"translation" text
|
||||
);
|
||||
|
||||
ALTER TABLE "message" ADD CONSTRAINT "pk_message_id_language" PRIMARY KEY ("id", "language");
|
||||
ALTER TABLE "message" ADD CONSTRAINT "fk_message_source_message" FOREIGN KEY ("id") REFERENCES "source_message" ("id") ON UPDATE CASCADE ON DELETE RESTRICT;
|
||||
|
||||
CREATE INDEX "idx_message_language" ON "message" USING btree (language);
|
||||
ALTER TABLE "message" CLUSTER ON "idx_message_language";
|
||||
|
||||
CREATE INDEX "idx_source_message_category" ON "source_message" USING btree (category);
|
||||
ALTER TABLE "source_message" CLUSTER ON "idx_source_message_category";
|
||||
30
vendor/yiisoft/yii2/i18n/migrations/schema-sqlite.sql
vendored
Normal file
30
vendor/yiisoft/yii2/i18n/migrations/schema-sqlite.sql
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Database schema required by \yii\i18n\DbMessageSource.
|
||||
*
|
||||
* @author Dmitry Naumenko <d.naumenko.a@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
* @since 2.0.7
|
||||
*/
|
||||
|
||||
drop table if exists `source_message`;
|
||||
drop table if exists `message`;
|
||||
|
||||
CREATE TABLE `source_message`
|
||||
(
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`category` varchar(255),
|
||||
`message` text
|
||||
);
|
||||
|
||||
CREATE TABLE `message`
|
||||
(
|
||||
`id` integer NOT NULL REFERENCES `source_message` (`id`) ON UPDATE CASCADE ON DELETE NO ACTION,
|
||||
`language` varchar(16) NOT NULL,
|
||||
`translation` text,
|
||||
PRIMARY KEY (`id`, `language`)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_message_language ON message (language);
|
||||
CREATE INDEX idx_source_message_category ON source_message (category);
|
||||
Reference in New Issue
Block a user