protected static function joinInternal($list, $keys, $locale) { $result = ''; if (is_array($list)) { $list = array_values($list); $n = count($list); switch ($n) { case 0: break; case 1: $result = strval($list[0]); break; default: $allData = \RedCat\Localize\Punic\Data::get('listPatterns', $locale); $data = null; if (!empty($keys)) { foreach ($keys as $key) { if (array_key_exists($key, $allData)) { $data = $allData[$key]; break; } } } if (is_null($data)) { $data = $allData['standard']; } if (array_key_exists($n, $data)) { $result = vsprintf($data[$n], $list); } else { $result = sprintf($data['end'], $list[$n - 2], $list[$n - 1]); if ($n > 2) { for ($index = $n - 3; $index > 0; $index--) { $result = sprintf($data['middle'], $list[$index], $result); } $result = sprintf($data['start'], $list[0], $result); } } break; } } return $result; }
/** * Convert a localized representation of a number to a number (for instance, converts the string '1,234' to 1234 in case of English and to 1.234 in case of Italian) * @param string $value The string value to convert * @param string $locale = '' The locale to use. If empty we'll use the default locale set in \RedCat\Localize\Punic\Data * @return int|float|null Returns null if $value is not valid, the numeric value otherwise */ public static function unformat($value, $locale = '') { $result = null; if (is_int($value) || is_float($value)) { $result = $value; } elseif (is_string($value) && strlen($value)) { $data = \RedCat\Localize\Punic\Data::get('numbers', $locale); $plus = $data['symbols']['plusSign']; $plusQ = preg_quote($plus); $minus = $data['symbols']['minusSign']; $minusQ = preg_quote($minus); $decimal = $data['symbols']['decimal']; $decimalQ = preg_quote($decimal); $group = $data['symbols']['group']; $groupQ = preg_quote($group); $ok = true; if (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*)" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = null; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*){$decimalQ}" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = ''; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*){$decimalQ}(\\d+)" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = $m[3]; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?{$decimalQ}(\\d+)" . '$/', $value, $m)) { $sign = $m[1]; $int = '0'; $float = $m[2]; } else { $ok = false; } if ($ok) { if ($sign === $minus) { $sign = '-'; } else { $sign = ''; } $int = str_replace($group, '', $int); if (is_null($float)) { $result = intval("{$sign}{$int}"); } else { $result = floatval("{$sign}{$int}.{$float}"); } } } return $result; }
protected static function decodeTimezoneDelta(\DateTime $value, $count, $locale) { $offset = $value->getOffset(); $sign = $offset < 0 ? '-' : '+'; $seconds = abs($offset); $hours = intval(floor($seconds / 3600)); $seconds -= $hours * 3600; $minutes = intval(floor($seconds / 60)); $seconds -= $minutes * 60; $partsWithoutSeconds = array(); $partsWithoutSeconds[] = $sign . substr('0' . strval($hours), -2); $partsWithoutSeconds[] = substr('0' . strval($minutes), -2); $partsMaybeWithSeconds = $partsWithoutSeconds; /* @TZWS if ($seconds > 0) { $partsMaybeWithSeconds[] = substr('0' . strval($seconds), -2); } */ switch ($count) { case 1: case 2: case 3: return implode('', $partsMaybeWithSeconds); case 4: $data = \RedCat\Localize\Punic\Data::get('timeZoneNames', $locale); $format = array_key_exists('gmtFormat', $data) ? $data['gmtFormat'] : 'GMT%1$s'; return sprintf($format, implode(':', $partsWithoutSeconds)); case 5: return implode(':', $partsMaybeWithSeconds); default: throw new Exception\ValueNotInList($count, array(1, 2, 3, 4, 5)); } }
/** * Format a unit string * @param int|float|string $number The unit amount * @param string $unit The unit identifier (eg 'duration/millisecond' or 'millisecond') * @param string $width = 'short' The format name; it can be 'long' (eg '3 milliseconds'), 'short' (eg '3 ms') or 'narrow' (eg '3ms'). You can also add a precision specifier ('long,2' or just '2') * @param string $locale = '' The locale to use. If empty we'll use the default locale set in \RedCat\Localize\Punic\Data */ public static function format($number, $unit, $width = 'short', $locale = '') { $data = \RedCat\Localize\Punic\Data::get('units', $locale); $precision = null; if (is_int($width)) { $precision = $width; $width = 'short'; } elseif (is_string($width) && preg_match('/^(?:(.*),)?([+\\-]?\\d+)$/', $width, $m)) { $precision = intval($m[2]); $width = $m[1]; if (!strlen($width)) { $width = 'short'; } } if (strpos($width, '_') === 0 || !array_key_exists($width, $data)) { $widths = array(); foreach (array_keys($data) as $w) { if (strpos($w, '_') !== 0) { $widths[] = $w; } } throw new Exception\ValueNotInList($width, $widths); } $data = $data[$width]; if (strpos($unit, '/') === false) { $unitCategory = null; $unitID = null; foreach (array_keys($data) as $c) { if (strpos($c, '_') === false) { if (array_key_exists($unit, $data[$c])) { if (is_null($unitCategory)) { $unitCategory = $c; $unitID = $unit; } else { $unitCategory = null; break; } } } } } else { list($unitCategory, $unitID) = explode('/', $unit, 2); } $rules = null; if (strpos($unit, '_') === false && !is_null($unitCategory) && !is_null($unitID) && array_key_exists($unitCategory, $data) && array_key_exists($unitID, $data[$unitCategory])) { $rules = $data[$unitCategory][$unitID]; } if (is_null($rules)) { $units = array(); foreach ($data as $c => $us) { if (strpos($c, '_') === false) { foreach (array_keys($us) as $u) { if (strpos($c, '_') === false) { $units[] = "{$c}/{$u}"; } } } } throw new \RedCat\Localize\Punic\Exception\ValueNotInList($unit, $units); } $pluralRule = \RedCat\Localize\Punic\Plural::getRule($number, $locale); //@codeCoverageIgnoreStart // These checks aren't necessary since $pluralRule should always be in $rules, but they don't hurt ;) if (!array_key_exists($pluralRule, $rules)) { if (array_key_exists('other', $rules)) { $pluralRule = 'other'; } else { $availableRules = array_keys($rules); $pluralRule = $availableRules[0]; } } //@codeCoverageIgnoreEnd return sprintf($rules[$pluralRule], \RedCat\Localize\Punic\Number::format($number, $precision, $locale)); }
/** * Return the plural rule ('zero', 'one', 'two', 'few', 'many' or 'other') for a number and a locale * @param string|int|float $number The number to check the plural rule for for * @param string $locale = '' The locale to use. If empty we'll use the default locale set in \RedCat\Localize\Punic\Data * @return string Returns one of the following values: 'zero', 'one', 'two', 'few', 'many', 'other' * @throws \RedCat\Localize\Punic\Exception\BadArgumentType Throws a \RedCat\Localize\Punic\Exception\BadArgumentType if $number is not a valid number * @throws \Exception Throws a \Exception if there were problems calculating the plural rule */ public static function getRule($number, $locale = '') { if (is_int($number)) { $intPartAbs = strval(abs($number)); $floatPart = ''; } elseif (is_float($number)) { $s = strval($number); if (strpos($s, '.') === false) { $intPart = $s; $floatPart = ''; } else { list($intPart, $floatPart) = explode('.', $s); } $intPartAbs = strval(abs(intval($intPart))); } elseif (is_string($number) && strlen($number)) { if (preg_match('/^[+|\\-]?\\d+\\.?$/', $number)) { $v = intval($number); $intPartAbs = strval(abs($v)); $floatPart = ''; } elseif (preg_match('/^(\\d*)\\.(\\d+)$/', $number, $m)) { list($intPart, $floatPart) = explode('.', $number); $v = @intval($intPart); $intPartAbs = strval(abs($v)); } else { throw new Exception\BadArgumentType($number, 'number'); } } else { throw new Exception\BadArgumentType($number, 'number'); } // 'n' => '%1$s', // absolute value of the source number (integer and decimals). $v1 = $intPartAbs . (strlen($floatPart) ? ".{$floatPart}" : ''); // 'i' => '%2$s', // integer digits of n $v2 = $intPartAbs; // 'v' => '%3$s', // number of visible fraction digits in n, with trailing zeros. $v3 = strlen($floatPart); // 'w' => '%4$s', // number of visible fraction digits in n, without trailing zeros. $v4 = strlen(rtrim($floatPart, '0')); // 'f' => '%5$s', // visible fractional digits in n, with trailing zeros. $v5 = strlen($floatPart) ? strval(intval($floatPart)) : '0'; // 't' => '%6$s', // visible fractional digits in n, without trailing zeros. $v6 = trim($floatPart, '0'); if (!strlen($v6)) { $v6 = '0'; } $result = 'other'; $node = \RedCat\Localize\Punic\Data::getLanguageNode(\RedCat\Localize\Punic\Data::getGeneric('plurals'), $locale); foreach ($node as $rule => $formulaPattern) { $formula = sprintf($formulaPattern, $v1, $v2, $v3, $v4, $v5, $v6); $check = str_replace(array('static::inRange(', ' and ', ' or ', ', false, ', ', true, ', ', array('), ' , ', $formula); if (preg_match('/[a-z]/', $check)) { throw new \Exception('Bad formula!'); } // fix for difference in modulo (%) in the definition and the one implemented in PHP for decimal numbers while (preg_match('/(\\d+\\.\\d+) % (\\d+(\\.\\d+)?)/', $formula, $m)) { list(, $decimalPart) = explode('.', $m[1], 2); $decimals = strlen(rtrim($decimalPart, '0')); if ($decimals > 0) { $pow = intval(pow(10, $decimals)); $repl = '(' . strval(intval(floatval($m[1]) * $pow)) . ' % ' . strval(intval(floatval($m[2] * $pow))) . ') / ' . $pow; } else { $repl = strval(intval($m[1])) . ' % ' . $m[2]; } $formula = str_replace($m[0], $repl, $formula); } $formulaResult = @eval("return ({$formula}) ? 'yes' : 'no';"); if ($formulaResult === 'yes') { $result = $rule; break; } elseif ($formulaResult !== 'no') { throw new \Exception('There was a problem in the formula ' . $formulaPattern); } } return $result; }