diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ffba4a..f3f8d41 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,13 @@ ### Added +- Support for fallback locale when using `RRule::humanReadable()` [#11](https://github.com/rlanvin/php-rrule/pull/11) - Dutch translation (NL) [#9](https://github.com/rlanvin/php-rrule/pull/9) +### Fixed + +- Fixed fatal error Locale class not found when intl extension is not loaded [#11](https://github.com/rlanvin/php-rrule/pull/11) + ## [1.1.0] - 2016-03-30 ### Added @@ -40,4 +45,4 @@ [Unreleased]: https://github.com/rlanvin/php-rrule/compare/v1.1.0...HEAD [1.1.0]: https://github.com/rlanvin/php-rrule/compare/v1.0.1...v1.1.0 -[1.0.1]: https://github.com/rlanvin/php-rrule/compare/v1.0.0...v1.0.1 \ No newline at end of file +[1.0.1]: https://github.com/rlanvin/php-rrule/compare/v1.0.0...v1.0.1 diff --git a/src/RRule.php b/src/RRule.php index 67a879c..f18af6e 100755 --- a/src/RRule.php +++ b/src/RRule.php @@ -2016,10 +2016,16 @@ class RRule implements RRuleInterface * Will load the basic first (e.g. "en") and then the region-specific if any * (e.g. "en_GB"), merging as necessary. * So region-specific translation files don't need to redefine every strings. + * + * @param string $locale + * @param string|null $fallback + * + * @return array + * @throws \InvalidArgumentException */ - static protected function i18nLoad($locale) + static protected function i18nLoad($locale, $fallback = null) { - if ( ! preg_match('/^([a-z]{2})(_[A-Z]{2})?$/', $locale, $matches) ) { + if ( ! preg_match('/^([a-z]{2})(_[A-Z]{2})?(?:_[A-Z]*)?(?:\.[a-zA-Z\-0-8]*)?$/', $locale, $matches) ) { throw new \InvalidArgumentException('The locale option does not look like a valid locale: '.$locale); } @@ -2045,6 +2051,9 @@ class RRule implements RRuleInterface } if ( empty($result) ) { + if (!is_null($fallback)) { + return self::i18nLoad($fallback); + } throw new \InvalidArgumentException("Failed to load translations for '$locale'"); } @@ -2054,6 +2063,10 @@ class RRule implements RRuleInterface /** * Format a rule in a human readable string * intl extension is required. + * + * @param array $opt + * + * @return string */ public function humanReadable(array $opt = array()) { @@ -2061,9 +2074,17 @@ class RRule implements RRuleInterface self::$intl_loaded = extension_loaded('intl'); } + $locale = setlocale(LC_MESSAGES, 0); + if ( self::$intl_loaded ) { + $locale = \Locale::getDefault(); + } else if ($locale == 'C') { + $locale = 'en'; + } + $default_opt = array( - 'locale' => \Locale::getDefault(), - 'date_formatter' => null + 'locale' => $locale, + 'date_formatter' => null, + 'fallback' => 'en', ); if ( self::$intl_loaded ) { @@ -2104,7 +2125,7 @@ class RRule implements RRuleInterface } } - $i18n = self::i18nLoad($opt['locale']); + $i18n = self::i18nLoad($opt['locale'], $opt['fallback']); $parts = array( 'freq' => '', @@ -2323,5 +2344,4 @@ class RRule implements RRuleInterface $str = implode('',$parts); return $str; } - } diff --git a/tests/RRuleTest.php b/tests/RRuleTest.php index 910c053..060293e 100755 --- a/tests/RRuleTest.php +++ b/tests/RRuleTest.php @@ -1943,4 +1943,88 @@ class RRuleTest extends PHPUnit_Framework_TestCase $this->assertEquals(date_create('2007-01-01'), $rrule[0], 'No modification possible with getOccurences (cached version)'); } + + /** + * Providing a set of valid locales to call RRule::i18nLoad() with + * + * @return array + */ + public function validFallbackLocales() + { + return array( + array('en'), + array('fr'), + array('en_US'), + array('en_US_POSIX'), + array('en_US.utf-8'), + ); + } + + /** + * Test that the RRule::i18nLoad() does not fail when provided with valid locales + * + * @dataProvider validFallbackLocales + */ + public function testHumanReadable($fallback) + { + $date = date_create('2007-01-01'); + $rrule = new RRule(array( + 'freq' => 'daily', + 'count' => 10, + 'dtstart' => $date + )); + + $reflector = new ReflectionClass('RRule\RRule'); + + // Force RRule::$intl_loaded to false, to test fallback locales + $property = $reflector->getProperty('intl_loaded'); + $property->setAccessible(true); + $property->setValue($rrule, false); + + $method = $reflector->getMethod('humanReadable'); + $result = $method->invokeArgs($rrule, array(array('locale' => 'xx', 'fallback' => $fallback))); + $this->assertNotEmpty($result); + } + + /** + * Providing a set of invalid locales to call RRule::i18nLoad() with + * + * @return array + */ + public function invalidFallbackLocales() + { + return array( + array('xx', 'eng'), + array('xx', 'invalid'), + array('xx', null), + array(null, null), + array(null, 'en_US._wr!ng'), + ); + } + + /** + * Tests that the RRule::i18nLoad() fails as expected on invalid $locale settings + * + * @dataProvider invalidFallbackLocales + * @expectedException \InvalidArgumentException + */ + public function testHumanReadableFails($locale, $fallback) + { + $date = date_create('2007-01-01'); + $rrule = new RRule(array( + 'freq' => 'daily', + 'count' => 10, + 'dtstart' => $date + )); + + $reflector = new ReflectionClass('RRule\RRule'); + + // Force RRule::$intl_loaded to false, to test fallback locales + $property = $reflector->getProperty('intl_loaded'); + $property->setAccessible(true); + $property->setValue($rrule, false); + + $method = $reflector->getMethod('humanReadable'); + $method->invokeArgs($rrule, array(array('locale' => $locale, 'fallback' => $fallback))); + } }