From bf722c0423c69a0606abd539bfce4f3988325ca8 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 28 Sep 2013 02:50:25 +0400 Subject: [PATCH] Used intl ICU for message translation --- build/controllers/LocaleController.php | 114 ---- docs/guide/i18n.md | 48 ++ framework/yii/i18n/I18N.php | 95 +--- framework/yii/i18n/MessageFormatter.php | 74 +++ framework/yii/i18n/data/plurals.php | 627 --------------------- framework/yii/i18n/data/plurals.xml | 109 ---- framework/yii/requirements/requirements.php | 4 +- tests/unit/framework/i18n/MessageFormatterTest.php | 47 ++ 8 files changed, 187 insertions(+), 931 deletions(-) delete mode 100644 build/controllers/LocaleController.php create mode 100644 framework/yii/i18n/MessageFormatter.php delete mode 100644 framework/yii/i18n/data/plurals.php delete mode 100644 framework/yii/i18n/data/plurals.xml create mode 100644 tests/unit/framework/i18n/MessageFormatterTest.php diff --git a/build/controllers/LocaleController.php b/build/controllers/LocaleController.php deleted file mode 100644 index 7b2a5df..0000000 --- a/build/controllers/LocaleController.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @since 2.0 - */ -class LocaleController extends Controller -{ - public $defaultAction = 'plural'; - - /** - * Generates the plural rules data. - * - * This command will parse the plural rule XML file from CLDR and convert them - * into appropriate PHP representation to support Yii message translation feature. - * @param string $xmlFile the original plural rule XML file (from CLDR). This file may be found in - * http://www.unicode.org/Public/cldr/latest/core.zip - * Extract the zip file and locate the file "common/supplemental/plurals.xml". - * @throws Exception - */ - public function actionPlural($xmlFile) - { - if (!is_file($xmlFile)) { - throw new Exception("The source plural rule file does not exist: $xmlFile"); - } - - $xml = simplexml_load_file($xmlFile); - - $allRules = array(); - - $patterns = array( - '/n in 0..1/' => '(n==0||n==1)', - '/\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\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 - ); - foreach ($xml->plurals->pluralRules as $node) { - $attributes = $node->attributes(); - $locales = explode(' ', $attributes['locales']); - $rules = array(); - - if (!empty($node->pluralRule)) { - foreach ($node->pluralRule as $rule) { - $expr_or = preg_split('/\s+or\s+/i', $rule); - foreach ($expr_or as $key_or => $val_or) { - $expr_and = preg_split('/\s+and\s+/i', $val_or); - $expr_and = preg_replace(array_keys($patterns), array_values($patterns), $expr_and); - $expr_or[$key_or] = implode('&&', $expr_and); - } - $expr = preg_replace('/\\bn\\b/', '$n', implode('||', $expr_or)); - $rules[] = preg_replace_callback('/range\((\d+),(\d+)\)/', function ($matches) { - if ($matches[2] - $matches[1] <= 5) { - return 'array(' . implode(',', range($matches[1], $matches[2])) . ')'; - } else { - return $matches[0]; - } - }, $expr); - - } - foreach ($locales as $locale) { - $allRules[$locale] = $rules; - } - } - } - // hard fix for "br": the rule is too complex - $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)))', - 3 => 'fmod($n,1000000)==0&&$n!=0', - ); - if (preg_match('/\d+/', $xml->version['number'], $matches)) { - $revision = $matches[0]; - } else { - $revision = -1; - } - - echo "getMessageSource($category)->translate($category, $message, $language); + $params = (array)$params; - if (!is_array($params)) { - $params = array($params); - } - - if (isset($params[0])) { - $message = $this->applyPluralRules($message, $params[0], $language); - if (!isset($params['{n}'])) { - $params['{n}'] = $params[0]; + if (class_exists('MessageFormatter', false) && preg_match('~{\s*[\d\w]+\s*,~u', $message)) { + $formatter = new MessageFormatter($language, $message); + if ($formatter === null) { + \Yii::$app->getLog()->log("$language message from category $category failed. Message is: $message.", Logger::LEVEL_WARNING, 'application'); + } + $result = $formatter->format($params); + if ($result === false) { + $errorMessage = $formatter->getErrorMessage(); + \Yii::$app->getLog()->log("$language message from category $category failed with error: $errorMessage. Message is: $message.", Logger::LEVEL_WARNING, 'application'); + } + else { + return $result; } - unset($params[0]); } return empty($params) ? $message : strtr($message, $params); @@ -125,62 +118,4 @@ class I18N extends Component throw new InvalidConfigException("Unable to locate message source for category '$category'."); } } - - /** - * 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->getPluralRules($language); - foreach ($rules as $i => $rule) { - if (isset($chunks[$i]) && $this->evaluate($rule, $number)) { - return $chunks[$i]; - } - } - $n = count($rules); - 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[$language]; - } - $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 - * @param mixed $n the number value - * @return boolean the expression result - */ - protected function evaluate($expression, $n) - { - return eval("return $expression;"); - } } diff --git a/framework/yii/i18n/MessageFormatter.php b/framework/yii/i18n/MessageFormatter.php new file mode 100644 index 0000000..e7163c0 --- /dev/null +++ b/framework/yii/i18n/MessageFormatter.php @@ -0,0 +1,74 @@ + + * @since 2.0 + */ +class MessageFormatter extends \MessageFormatter +{ + /** + * Format the message. + * + * @link http://php.net/manual/en/messageformatter.format.php + * @param array $args Arguments to insert into the format string. + * @return string|boolean The formatted string, or false if an error occurred. + */ + public function format($args) + { + $pattern = self::replaceNamedArguments($this->getPattern(), $args); + $this->setPattern($pattern); + return parent::format(array_values($args)); + } + + /** + * Quick format message. + * + * @link http://php.net/manual/en/messageformatter.formatmessage.php + * @param string $locale The locale to use for formatting locale-dependent parts. + * @param string $pattern The pattern string to insert things into. + * @param array $args The array of values to insert into the format string. + * @return string|boolean The formatted pattern string or false if an error occurred. + */ + public static function formatMessage($locale, $pattern, $args) + { + $pattern = self::replaceNamedArguments($pattern, $args); + return parent::formatMessage($locale, $pattern, array_values($args)); + } + + /** + * Replace named placeholders with numeric placeholders. + * + * @param string $pattern The pattern string to relace things into. + * @param array $args The array of values to insert into the format string. + * @return string The pattern string with placeholders replaced. + */ + private static function replaceNamedArguments($pattern, $args) + { + $map = array_flip(array_keys($args)); + return preg_replace_callback('~({\s*)([\d\w]+)(\s*[,}])~u', function ($input) use ($map) { + $name = $input[2]; + if (isset($map[$name])) { + return $input[1] . $map[$name] . $input[3]; + } + else { + return "'" . $input[1] . $name . $input[3] . "'"; + } + }, $pattern); + } +} + \ No newline at end of file diff --git a/framework/yii/i18n/data/plurals.php b/framework/yii/i18n/data/plurals.php deleted file mode 100644 index cb51307..0000000 --- a/framework/yii/i18n/data/plurals.php +++ /dev/null @@ -1,627 +0,0 @@ - - array ( - 0 => '$n==0', - 1 => '$n==1', - 2 => '$n==2', - 3 => 'in_array(fmod($n,100),range(3,10))', - 4 => 'in_array(fmod($n,100),range(11,99))', - ), - 'asa' => - array ( - 0 => '$n==1', - ), - 'af' => - array ( - 0 => '$n==1', - ), - 'bem' => - array ( - 0 => '$n==1', - ), - 'bez' => - array ( - 0 => '$n==1', - ), - 'bg' => - array ( - 0 => '$n==1', - ), - 'bn' => - array ( - 0 => '$n==1', - ), - 'brx' => - array ( - 0 => '$n==1', - ), - 'ca' => - array ( - 0 => '$n==1', - ), - 'cgg' => - array ( - 0 => '$n==1', - ), - 'chr' => - array ( - 0 => '$n==1', - ), - 'da' => - array ( - 0 => '$n==1', - ), - 'de' => - array ( - 0 => '$n==1', - ), - 'dv' => - array ( - 0 => '$n==1', - ), - 'ee' => - array ( - 0 => '$n==1', - ), - 'el' => - array ( - 0 => '$n==1', - ), - 'en' => - array ( - 0 => '$n==1', - ), - 'eo' => - array ( - 0 => '$n==1', - ), - 'es' => - array ( - 0 => '$n==1', - ), - 'et' => - array ( - 0 => '$n==1', - ), - 'eu' => - array ( - 0 => '$n==1', - ), - 'fi' => - array ( - 0 => '$n==1', - ), - 'fo' => - array ( - 0 => '$n==1', - ), - 'fur' => - array ( - 0 => '$n==1', - ), - 'fy' => - array ( - 0 => '$n==1', - ), - 'gl' => - array ( - 0 => '$n==1', - ), - 'gsw' => - array ( - 0 => '$n==1', - ), - 'gu' => - array ( - 0 => '$n==1', - ), - 'ha' => - array ( - 0 => '$n==1', - ), - 'haw' => - array ( - 0 => '$n==1', - ), - 'he' => - array ( - 0 => '$n==1', - ), - 'is' => - array ( - 0 => '$n==1', - ), - 'it' => - array ( - 0 => '$n==1', - ), - 'jmc' => - array ( - 0 => '$n==1', - ), - 'kaj' => - array ( - 0 => '$n==1', - ), - 'kcg' => - array ( - 0 => '$n==1', - ), - 'kk' => - array ( - 0 => '$n==1', - ), - 'kl' => - array ( - 0 => '$n==1', - ), - 'ksb' => - array ( - 0 => '$n==1', - ), - 'ku' => - array ( - 0 => '$n==1', - ), - 'lb' => - array ( - 0 => '$n==1', - ), - 'lg' => - array ( - 0 => '$n==1', - ), - 'mas' => - array ( - 0 => '$n==1', - ), - 'ml' => - array ( - 0 => '$n==1', - ), - 'mn' => - array ( - 0 => '$n==1', - ), - 'mr' => - array ( - 0 => '$n==1', - ), - 'nah' => - array ( - 0 => '$n==1', - ), - 'nb' => - array ( - 0 => '$n==1', - ), - 'nd' => - array ( - 0 => '$n==1', - ), - 'ne' => - array ( - 0 => '$n==1', - ), - 'nl' => - array ( - 0 => '$n==1', - ), - 'nn' => - array ( - 0 => '$n==1', - ), - 'no' => - array ( - 0 => '$n==1', - ), - 'nr' => - array ( - 0 => '$n==1', - ), - 'ny' => - array ( - 0 => '$n==1', - ), - 'nyn' => - array ( - 0 => '$n==1', - ), - 'om' => - array ( - 0 => '$n==1', - ), - 'or' => - array ( - 0 => '$n==1', - ), - 'pa' => - array ( - 0 => '$n==1', - ), - 'pap' => - array ( - 0 => '$n==1', - ), - 'ps' => - array ( - 0 => '$n==1', - ), - 'pt' => - array ( - 0 => '$n==1', - ), - 'rof' => - array ( - 0 => '$n==1', - ), - 'rm' => - array ( - 0 => '$n==1', - ), - 'rwk' => - array ( - 0 => '$n==1', - ), - 'saq' => - array ( - 0 => '$n==1', - ), - 'seh' => - array ( - 0 => '$n==1', - ), - 'sn' => - array ( - 0 => '$n==1', - ), - 'so' => - array ( - 0 => '$n==1', - ), - 'sq' => - array ( - 0 => '$n==1', - ), - 'ss' => - array ( - 0 => '$n==1', - ), - 'ssy' => - array ( - 0 => '$n==1', - ), - 'st' => - array ( - 0 => '$n==1', - ), - 'sv' => - array ( - 0 => '$n==1', - ), - 'sw' => - array ( - 0 => '$n==1', - ), - 'syr' => - array ( - 0 => '$n==1', - ), - 'ta' => - array ( - 0 => '$n==1', - ), - 'te' => - array ( - 0 => '$n==1', - ), - 'teo' => - array ( - 0 => '$n==1', - ), - 'tig' => - array ( - 0 => '$n==1', - ), - 'tk' => - array ( - 0 => '$n==1', - ), - 'tn' => - array ( - 0 => '$n==1', - ), - 'ts' => - array ( - 0 => '$n==1', - ), - 'ur' => - array ( - 0 => '$n==1', - ), - 'wae' => - array ( - 0 => '$n==1', - ), - 've' => - array ( - 0 => '$n==1', - ), - 'vun' => - array ( - 0 => '$n==1', - ), - 'xh' => - array ( - 0 => '$n==1', - ), - 'xog' => - array ( - 0 => '$n==1', - ), - 'zu' => - array ( - 0 => '$n==1', - ), - 'ak' => - array ( - 0 => '($n==0||$n==1)', - ), - 'am' => - array ( - 0 => '($n==0||$n==1)', - ), - 'bh' => - array ( - 0 => '($n==0||$n==1)', - ), - 'fil' => - array ( - 0 => '($n==0||$n==1)', - ), - 'tl' => - array ( - 0 => '($n==0||$n==1)', - ), - 'guw' => - array ( - 0 => '($n==0||$n==1)', - ), - 'hi' => - array ( - 0 => '($n==0||$n==1)', - ), - 'ln' => - array ( - 0 => '($n==0||$n==1)', - ), - 'mg' => - array ( - 0 => '($n==0||$n==1)', - ), - 'nso' => - array ( - 0 => '($n==0||$n==1)', - ), - 'ti' => - array ( - 0 => '($n==0||$n==1)', - ), - 'wa' => - array ( - 0 => '($n==0||$n==1)', - ), - 'ff' => - array ( - 0 => '($n>=0&&$n<=2)&&$n!=2', - ), - 'fr' => - array ( - 0 => '($n>=0&&$n<=2)&&$n!=2', - ), - 'kab' => - array ( - 0 => '($n>=0&&$n<=2)&&$n!=2', - ), - 'lv' => - array ( - 0 => '$n==0', - 1 => 'fmod($n,10)==1&&fmod($n,100)!=11', - ), - 'iu' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'kw' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'naq' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'se' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'sma' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'smi' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'smj' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'smn' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'sms' => - array ( - 0 => '$n==1', - 1 => '$n==2', - ), - 'ga' => - array ( - 0 => '$n==1', - 1 => '$n==2', - 2 => 'in_array($n,array(3,4,5,6))', - 3 => 'in_array($n,array(7,8,9,10))', - ), - 'ro' => - array ( - 0 => '$n==1', - 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))', - ), - 'mo' => - array ( - 0 => '$n==1', - 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))', - ), - 'lt' => - array ( - 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 => '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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'bs' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'hr' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'ru' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'sh' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'sr' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'uk' => - array ( - 0 => 'fmod($n,10)==1&&fmod($n,100)!=11', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', - ), - 'cs' => - array ( - 0 => '$n==1', - 1 => 'in_array($n,array(2,3,4))', - ), - 'sk' => - array ( - 0 => '$n==1', - 1 => 'in_array($n,array(2,3,4))', - ), - 'pl' => - array ( - 0 => '$n==1', - 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),array(5,6,7,8,9))||in_array(fmod($n,100),array(12,13,14))', - ), - 'sl' => - array ( - 0 => 'fmod($n,100)==1', - 1 => 'fmod($n,100)==2', - 2 => 'in_array(fmod($n,100),array(3,4))', - ), - 'mt' => - array ( - 0 => '$n==1', - 1 => '$n==0||in_array(fmod($n,100),range(2,10))', - 2 => 'in_array(fmod($n,100),range(11,19))', - ), - 'mk' => - array ( - 0 => 'fmod($n,10)==1&&$n!=11', - ), - 'cy' => - array ( - 0 => '$n==0', - 1 => '$n==1', - 2 => '$n==2', - 3 => '$n==3', - 4 => '$n==6', - ), - 'lag' => - array ( - 0 => '$n==0', - 1 => '($n>=0&&$n<=2)&&$n!=0&&$n!=2', - ), - 'shi' => - array ( - 0 => '($n>=0&&$n<=1)', - 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)))', - 3 => 'fmod($n,1000000)==0&&$n!=0', - ), - 'ksh' => - array ( - 0 => '$n==0', - 1 => '$n==1', - ), - 'tzm' => - array ( - 0 => '($n==0||$n==1)||in_array($n,range(11,99))', - ), - 'gv' => - array ( - 0 => 'in_array(fmod($n,10),array(1,2))||fmod($n,20)==0', - ), -); diff --git a/framework/yii/i18n/data/plurals.xml b/framework/yii/i18n/data/plurals.xml deleted file mode 100644 index 9227dc6..0000000 --- a/framework/yii/i18n/data/plurals.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - n is 0 - n is 1 - n is 2 - n mod 100 in 3..10 - n mod 100 in 11..99 - - - n is 1 - - - n in 0..1 - - - n within 0..2 and n is not 2 - - - n is 0 - n mod 10 is 1 and n mod 100 is not 11 - - - n is 1 - n is 2 - - - n is 1 - n is 2 - n in 3..6 - n in 7..10 - - - n is 1 - n is 0 OR n is not 1 AND n mod 100 in 1..19 - - - n mod 10 is 1 and n mod 100 not in 11..19 - n mod 10 in 2..9 and n mod 100 not in 11..19 - - - n mod 10 is 1 and n mod 100 is not 11 - n mod 10 in 2..4 and n mod 100 not in 12..14 - n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14 - - - - n is 1 - n in 2..4 - - - n is 1 - n mod 10 in 2..4 and n mod 100 not in 12..14 - n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14 - - - - - n mod 100 is 1 - n mod 100 is 2 - n mod 100 in 3..4 - - - n is 1 - n is 0 or n mod 100 in 2..10 - n mod 100 in 11..19 - - - n mod 10 is 1 and n is not 11 - - - n is 0 - n is 1 - n is 2 - n is 3 - n is 6 - - - n is 0 - n within 0..2 and n is not 0 and n is not 2 - - - n within 0..1 - n in 2..10 - - - n mod 10 is 1 and n mod 100 not in 11,71,91 - n mod 10 is 2 and n mod 100 not in 12,72,92 - n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99 - n mod 1000000 is 0 and n is not 0 - - - n is 0 - n is 1 - - - n in 0..1 or n in 11..99 - - - n mod 10 in 1..2 or n mod 20 is 0 - - - diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php index 005a205..571aa57 100644 --- a/framework/yii/requirements/requirements.php +++ b/framework/yii/requirements/requirements.php @@ -43,6 +43,8 @@ return array( 'mandatory' => false, 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='), 'by' => 'Internationalization support', - 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN-feature of EmailValidator or UrlValidator or the yii\i18n\Formatter class.' + 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use advanced parameters formatting + in \Yii::t(), IDN-feature of + EmailValidator or UrlValidator or the yii\i18n\Formatter class.' ), ); diff --git a/tests/unit/framework/i18n/MessageFormatterTest.php b/tests/unit/framework/i18n/MessageFormatterTest.php new file mode 100644 index 0000000..7595fe0 --- /dev/null +++ b/tests/unit/framework/i18n/MessageFormatterTest.php @@ -0,0 +1,47 @@ + + * @since 2.0 + * @group i18n + */ +class MessageFormatterTest extends TestCase +{ + const N = 'n'; + const N_VALUE = 42; + const SUBJECT = 'сабж'; + const SUBJECT_VALUE = 'Answer to the Ultimate Question of Life, the Universe, and Everything'; + + public function testNamedArguments() + { + $expected = self::SUBJECT_VALUE.' is '.self::N_VALUE; + + $result = MessageFormatter::formatMessage('en_US', '{'.self::SUBJECT.'} is {'.self::N.', number}', array( + self::N => self::N_VALUE, + self::SUBJECT => self::SUBJECT_VALUE, + )); + + $this->assertEquals($expected, $result); + } + + public function testInsufficientArguments() + { + $expected = '{'.self::SUBJECT.'} is '.self::N_VALUE; + + $result = MessageFormatter::formatMessage('en_US', '{'.self::SUBJECT.'} is {'.self::N.', number}', array( + self::N => self::N_VALUE, + )); + + $this->assertEquals($expected, $result); + } +} \ No newline at end of file