Browse Source

Finished plural forms.

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
c629ad776a
  1. 0
      build/build
  2. 17
      build/controllers/LocaleController.php
  3. 39
      framework/YiiBase.php
  4. 10
      framework/base/ErrorHandler.php
  5. 97
      framework/i18n/I18N.php
  6. 66
      framework/i18n/data/plurals.php

0
build/build

17
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)) {

39
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 <code>strtr</code>.
* 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)
{

10
framework/base/ErrorHandler.php

@ -16,8 +16,6 @@ namespace yii\base;
* @author Qiang Xue <qiang.xue@gmail.com>
* @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);
}

97
framework/i18n/I18N.php

@ -1,11 +1,23 @@
<?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;
use yii\base\InvalidParamException;
/**
* I18N provides features related with internationalization (I18N) and localization (L10N).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @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;");
}
}

66
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',
),
);
Loading…
Cancel
Save