diff --git a/build/build b/build/build
old mode 100644
new mode 100755
diff --git a/build/controllers/LocaleController.php b/build/controllers/LocaleController.php
index 51de6a0..00de787 100644
--- a/build/controllers/LocaleController.php
+++ b/build/controllers/LocaleController.php
@@ -42,9 +42,10 @@ class LocaleController extends Controller
'/\s+is\s+not\s+/i' => '!=', //is not
'/\s+is\s+/i' => '==', //is
'/n\s+mod\s+(\d+)/i' => 'fmod(n,$1)', //mod (CLDR's "mod" is "fmod()", not "%")
- '/^(.*?)\s+not\s+(?:in|within)\s+(\d+)\.\.(\d+)/i' => '($1<$2||$1>$3)', //not in, not within
+ '/^(.*?)\s+not\s+in\s+(\d+)\.\.(\d+)/i' => '!in_array($1,range($2,$3))', //not in
+ '/^(.*?)\s+in\s+(\d+)\.\.(\d+)/i' => 'in_array($1,range($2,$3))', //in
+ '/^(.*?)\s+not\s+within\s+(\d+)\.\.(\d+)/i' => '($1<$2||$1>$3)', //not within
'/^(.*?)\s+within\s+(\d+)\.\.(\d+)/i' => '($1>=$2&&$1<=$3)', //within
- '/^(.*?)\s+in\s+(\d+)\.\.(\d+)/i' => '($1>=$2&&$1<=$3&&fmod($1,1)==0)', //in
);
foreach ($xml->plurals->pluralRules as $node) {
$attributes = $node->attributes();
@@ -59,7 +60,15 @@ class LocaleController extends Controller
$expr_and = preg_replace(array_keys($patterns), array_values($patterns), $expr_and);
$expr_or[$key_or] = implode('&&', $expr_and);
}
- $rules[] = preg_replace('/\\bn\\b/', '$n', implode('||', $expr_or));
+ $expr = preg_replace('/\\bn\\b/', '$n', implode('||', $expr_or));
+ $rules[] = preg_replace_callback('/range\((\d+),(\d+)\)/', function ($matches) {
+ if ($matches[2] - $matches[1] <= 2) {
+ return 'array(' . implode(',', range($matches[1], $matches[2])) . ')';
+ } else {
+ return $matches[0];
+ }
+ }, $expr);
+
}
foreach ($locales as $locale) {
$allRules[$locale] = $rules;
@@ -70,7 +79,7 @@ class LocaleController extends Controller
$allRules['br'] = array(
0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),array(11,71,91))',
1 => 'fmod($n,10)==2&&!in_array(fmod($n,100),array(12,72,92))',
- 2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99))))',
+ 2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99)))',
3 => 'fmod($n,1000000)==0&&$n!=0',
);
if (preg_match('/\d+/', $xml->version['number'], $matches)) {
diff --git a/framework/YiiBase.php b/framework/YiiBase.php
index e4a01b4..e81a288 100644
--- a/framework/YiiBase.php
+++ b/framework/YiiBase.php
@@ -192,6 +192,7 @@ class YiiBase
* @param boolean $throwException whether to throw an exception if the given alias is invalid.
* If this is false and an invalid alias is given, false will be returned by this method.
* @return string|boolean path corresponding to the alias, false if the root alias is not previously registered.
+ * @throws InvalidParamException if the alias is invalid while $throwException is true.
* @see setAlias
*/
public static function getAlias($alias, $throwException = true)
@@ -231,6 +232,7 @@ class YiiBase
* - a URL (e.g. `http://www.yiiframework.com`)
* - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
* actual path first by calling [[getAlias]].
+ *
* @throws Exception if $path is an invalid alias
* @see getAlias
*/
@@ -504,21 +506,28 @@ class YiiBase
/**
* Translates a message to the specified language.
- * This method supports choice format (see {@link CChoiceFormat}),
- * i.e., the message returned will be chosen from a few candidates according to the given
- * number value. This feature is mainly used to solve plural format issue in case
- * a message has different plural forms in some languages.
- * @param string $message the original message
- * @param array $params parameters to be applied to the message using strtr
.
- * The first parameter can be a number without key.
- * And in this case, the method will call {@link CChoiceFormat::format} to choose
- * an appropriate message translation.
- * You can pass parameter for {@link CChoiceFormat::format}
- * or plural forms format without wrapping it with array.
- * @param string $language the target language. If null (default), the {@link CApplication::getLanguage application language} will be used.
- * @return string the translated message
- * @see CMessageSource
- * @see http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html
+ *
+ * The translation will be conducted according to the message category and the target language.
+ * To specify the category of the message, prefix the message with the category name and separate it
+ * with "|". For example, "app|hello world". If the category is not specified, the default category "app"
+ * will be used. The actual message translation is done by a [[\yii\i18n\MessageSource|message source]].
+ *
+ * In case when a translated message has different plural forms (separated by "|"), this method
+ * will also attempt to choose an appropriate one according to a given numeric value which is
+ * specified as the first parameter (indexed by 0) in `$params`.
+ *
+ * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first
+ * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}"
+ * will be replaced with the given number.
+ *
+ * For more details on how plural rules are applied, please refer to:
+ * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]]
+ *
+ * @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`). If this is null, the current
+ * [[\yii\base\Application::language|application language]] will be used.
+ * @return string the translated message.
*/
public static function t($message, $params = array(), $language = null)
{
diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php
index f71b8c8..a2f372c 100644
--- a/framework/base/ErrorHandler.php
+++ b/framework/base/ErrorHandler.php
@@ -16,8 +16,6 @@ namespace yii\base;
* @author Qiang Xue
* @since 2.0
*/
-use yii\helpers\VarDumper;
-
class ErrorHandler extends Component
{
/**
@@ -63,10 +61,10 @@ class ErrorHandler extends Component
$this->clearOutput();
}
- $this->render($exception);
+ $this->renderException($exception);
}
- protected function render($exception)
+ protected function renderException($exception)
{
if ($this->errorAction !== null) {
\Yii::$app->runAction($this->errorAction);
@@ -84,7 +82,7 @@ class ErrorHandler extends Component
} else {
$viewName = $this->exceptionView;
}
- echo $view->render($viewName, array(
+ echo $view->renderFile($viewName, array(
'exception' => $exception,
), $this);
}
@@ -255,7 +253,7 @@ class ErrorHandler extends Component
{
$view = new View;
$name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView;
- echo $view->render($name, array(
+ echo $view->renderFile($name, array(
'exception' => $exception,
), $this);
}
diff --git a/framework/i18n/I18N.php b/framework/i18n/I18N.php
index 0409da3..8667abc 100644
--- a/framework/i18n/I18N.php
+++ b/framework/i18n/I18N.php
@@ -1,11 +1,23 @@
+ * @since 2.0
+ */
class I18N extends Component
{
/**
@@ -13,11 +25,36 @@ class I18N extends Component
* categories, and the array values are the corresponding [[MessageSource]] objects or the configurations
* for creating the [[MessageSource]] objects. The message categories 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'.
+ *
+ * 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;
+ /**
+ * @var string the path or path alias of the file that contains the plural rules.
+ * By default, this refers to a file shipped with the Yii distribution. The file is obtained
+ * by converting from the data file in the CLDR project.
+ *
+ * If the default rule file does not contain the expected rules, you may copy and modify it
+ * for your application, and then configure this property to point to your modified copy.
+ *
+ * @see http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html
+ */
+ public $pluralRuleFile = '@yii/i18n/data/plurals.php';
+ /**
+ * Initializes the component by configuring the default message categories.
+ */
public function init()
{
+ parent::init();
if (!isset($this->translations['yii'])) {
$this->translations['yii'] = array(
'class' => 'yii\i18n\PhpMessageSource',
@@ -34,6 +71,16 @@ class I18N extends Component
}
}
+ /**
+ * Translates a message to the specified language.
+ * If the first parameter in `$params` is a number and it is indexed by 0, appropriate plural rules
+ * will be applied to the translated message.
+ * @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`). If this is null, the current
+ * [[\yii\base\Application::language|application language]] will be used.
+ * @return string the translated message.
+ */
public function translate($message, $params = array(), $language = null)
{
if ($language === null) {
@@ -55,7 +102,7 @@ class I18N extends Component
}
if (isset($params[0])) {
- $message = $this->getPluralForm($message, $params[0], $language);
+ $message = $this->applyPluralRules($message, $params[0], $language);
if (!isset($params['{n}'])) {
$params['{n}'] = $params[0];
}
@@ -65,6 +112,12 @@ class I18N extends Component
return $params === array() ? $message : strtr($message, $params);
}
+ /**
+ * 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])) {
@@ -85,18 +138,21 @@ class I18N extends Component
}
}
- public function getLocale($language)
- {
-
- }
-
- protected function getPluralForm($message, $number, $language)
+ /**
+ * Applies appropriate plural rules to the given message.
+ * @param string $message the message to be applied with plural rules
+ * @param mixed $number the number by which plural rules will be applied
+ * @param string $language the language code that determines which set of plural rules to be applied.
+ * @return string the message that has applied plural rules
+ */
+ protected function applyPluralRules($message, $number, $language)
{
if (strpos($message, '|') === false) {
return $message;
}
$chunks = explode('|', $message);
- $rules = $this->getLocale($language)->getPluralRules();
+
+ $rules = $this->getPluralRules($language);
foreach ($rules as $i => $rule) {
if (isset($chunks[$i]) && $this->evaluate($rule, $number)) {
return $chunks[$i];
@@ -106,6 +162,29 @@ class I18N extends Component
return isset($chunks[$n]) ? $chunks[$n] : $chunks[0];
}
+ private $_pluralRules = array(); // language => rule set
+
+ /**
+ * Returns the plural rules for the given language code.
+ * @param string $language the language code (e.g. `en_US`, `en`).
+ * @return array the plural rules
+ * @throws InvalidParamException if the language code is invalid.
+ */
+ protected function getPluralRules($language)
+ {
+ if (isset($this->_pluralRules[$language])) {
+ return $this->_pluralRules;
+ }
+ $allRules = require(Yii::getAlias($this->pluralRuleFile));
+ if (isset($allRules[$language])) {
+ return $this->_pluralRules[$language] = $allRules[$language];
+ } elseif (preg_match('/^[a-z]+/', strtolower($language), $matches)) {
+ return $this->_pluralRules[$language] = isset($allRules[$matches[0]]) ? $allRules[$matches[0]] : array();
+ } else {
+ throw new InvalidParamException("Invalid language code: $language");
+ }
+ }
+
/**
* Evaluates a PHP expression with the given number value.
* @param string $expression the PHP expression
@@ -114,6 +193,6 @@ class I18N extends Component
*/
protected function evaluate($expression, $n)
{
- return @eval("return $expression;");
+ return eval("return $expression;");
}
}
diff --git a/framework/i18n/data/plurals.php b/framework/i18n/data/plurals.php
index 3ed5619..b82fec0 100644
--- a/framework/i18n/data/plurals.php
+++ b/framework/i18n/data/plurals.php
@@ -21,8 +21,8 @@ return array (
0 => '$n==0',
1 => '$n==1',
2 => '$n==2',
- 3 => '(fmod($n,100)>=3&&fmod($n,100)<=10&&fmod(fmod($n,100),1)==0)',
- 4 => '(fmod($n,100)>=11&&fmod($n,100)<=99&&fmod(fmod($n,100),1)==0)',
+ 3 => 'in_array(fmod($n,100),range(3,10))',
+ 4 => 'in_array(fmod($n,100),range(11,99))',
),
'asa' =>
array (
@@ -494,93 +494,93 @@ return array (
array (
0 => '$n==1',
1 => '$n==2',
- 2 => '($n>=3&&$n<=6&&fmod($n,1)==0)',
- 3 => '($n>=7&&$n<=10&&fmod($n,1)==0)',
+ 2 => 'in_array($n,range(3,6))',
+ 3 => 'in_array($n,range(7,10))',
),
'ro' =>
array (
0 => '$n==1',
- 1 => '$n==0||$n!=1&&(fmod($n,100)>=1&&fmod($n,100)<=19&&fmod(fmod($n,100),1)==0)',
+ 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))',
),
'mo' =>
array (
0 => '$n==1',
- 1 => '$n==0||$n!=1&&(fmod($n,100)>=1&&fmod($n,100)<=19&&fmod(fmod($n,100),1)==0)',
+ 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))',
),
'lt' =>
array (
- 0 => 'fmod($n,10)==1&&(fmod($n,100)<11||fmod($n,100)>19)',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<11||fmod($n,100)>19)',
+ 0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),range(11,19))',
+ 1 => 'in_array(fmod($n,10),range(2,9))&&!in_array(fmod($n,100),range(11,19))',
),
'be' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'bs' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'hr' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'ru' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'sh' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'sr' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'uk' =>
array (
0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => 'fmod($n,10)==0||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=11&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => 'fmod($n,10)==0||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),range(11,14))',
),
'cs' =>
array (
0 => '$n==1',
- 1 => '($n>=2&&$n<=4&&fmod($n,1)==0)',
+ 1 => 'in_array($n,array(2,3,4))',
),
'sk' =>
array (
0 => '$n==1',
- 1 => '($n>=2&&$n<=4&&fmod($n,1)==0)',
+ 1 => 'in_array($n,array(2,3,4))',
),
'pl' =>
array (
0 => '$n==1',
- 1 => '(fmod($n,10)>=2&&fmod($n,10)<=4&&fmod(fmod($n,10),1)==0)&&(fmod($n,100)<12||fmod($n,100)>14)',
- 2 => '$n!=1&&(fmod($n,10)>=0&&fmod($n,10)<=1&&fmod(fmod($n,10),1)==0)||(fmod($n,10)>=5&&fmod($n,10)<=9&&fmod(fmod($n,10),1)==0)||(fmod($n,100)>=12&&fmod($n,100)<=14&&fmod(fmod($n,100),1)==0)',
+ 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
+ 2 => '$n!=1&&in_array(fmod($n,10),array(0,1))||in_array(fmod($n,10),range(5,9))||in_array(fmod($n,100),array(12,13,14))',
),
'sl' =>
array (
0 => 'fmod($n,100)==1',
1 => 'fmod($n,100)==2',
- 2 => '(fmod($n,100)>=3&&fmod($n,100)<=4&&fmod(fmod($n,100),1)==0)',
+ 2 => 'in_array(fmod($n,100),array(3,4))',
),
'mt' =>
array (
0 => '$n==1',
- 1 => '$n==0||(fmod($n,100)>=2&&fmod($n,100)<=10&&fmod(fmod($n,100),1)==0)',
- 2 => '(fmod($n,100)>=11&&fmod($n,100)<=19&&fmod(fmod($n,100),1)==0)',
+ 1 => '$n==0||in_array(fmod($n,100),range(2,10))',
+ 2 => 'in_array(fmod($n,100),range(11,19))',
),
'mk' =>
array (
@@ -602,13 +602,13 @@ return array (
'shi' =>
array (
0 => '($n>=0&&$n<=1)',
- 1 => '($n>=2&&$n<=10&&fmod($n,1)==0)',
+ 1 => 'in_array($n,range(2,10))',
),
'br' =>
array (
0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),array(11,71,91))',
1 => 'fmod($n,10)==2&&!in_array(fmod($n,100),array(12,72,92))',
- 2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99))))',
+ 2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99)))',
3 => 'fmod($n,1000000)==0&&$n!=0',
),
'ksh' =>
@@ -618,10 +618,10 @@ return array (
),
'tzm' =>
array (
- 0 => '($n==0||$n==1)||($n>=11&&$n<=99&&fmod($n,1)==0)',
+ 0 => '($n==0||$n==1)||in_array($n,range(11,99))',
),
'gv' =>
array (
- 0 => '(fmod($n,10)>=1&&fmod($n,10)<=2&&fmod(fmod($n,10),1)==0)||fmod($n,20)==0',
+ 0 => 'in_array(fmod($n,10),array(1,2))||fmod($n,20)==0',
),
);
\ No newline at end of file