1
0
mirror of https://github.com/rlanvin/php-rrule.git synced 2024-12-01 08:24:10 +01:00

Add custom_path option to humanReadable

If the option is present, it'll first look for a file in this folder
before looking into the default folder.
Fix #56
This commit is contained in:
rlanvin 2019-01-13 10:47:43 +00:00
parent 7c93c0e48a
commit 200b923c9e
5 changed files with 225 additions and 20 deletions

View File

@ -4,6 +4,10 @@
- Rewrite the core algorithm to use a native PHP generator, drop compability with PHP < 5.6 [#43](https://github.com/rlanvin/php-rrule/issues/43)
### Added
- New option `custom_path` to `humanReadable()` to use custom translation files [#56](https://github.com/rlanvin/php-rrule/issues/56)
## [1.6.3] - 2019-01-13
### Fixed

View File

@ -2153,7 +2153,7 @@ class RRule implements RRuleInterface
* Test if intl extension is loaded
* @return bool
*/
static public function intlLoaded()
static protected function intlLoaded()
{
if ( self::$intl_loaded === null ) {
self::$intl_loaded = extension_loaded('intl');
@ -2163,10 +2163,11 @@ class RRule implements RRuleInterface
/**
* Parse a locale and returns a list of files to load.
* For example "fr_FR" will produce "fr" and "fr_FR"
*
* @return array
*/
static public function i18nFilesToLoad($locale, $use_intl = null)
static protected function i18nFilesToLoad($locale, $use_intl = null)
{
if ( $use_intl === null ) {
$use_intl = self::intlLoaded();
@ -2202,26 +2203,38 @@ class RRule implements RRuleInterface
*
* @param string $locale
* @param string|null $fallback
* @param bool $use_intl
* @param string $custom_path
*
* @return array
* @throws \InvalidArgumentException
*/
static protected function i18nLoad($locale, $fallback = null, $use_intl = null)
static protected function i18nLoad($locale, $fallback = null, $use_intl = null, $custom_path = null)
{
$files = self::i18nFilesToLoad($locale, $use_intl);
$base_path = __DIR__.'/i18n';
$result = array();
foreach ( $files as $file ) {
$path = __DIR__."/i18n/$file.php";
if ( isset(self::$i18n[$file]) ) {
$result = array_merge($result, self::$i18n[$file]);
}
elseif ( is_file($path) && is_readable($path) ) {
self::$i18n[$file] = include $path;
$result = array_merge($result, self::$i18n[$file]);
// if the file exists in $custom_path, it overrides the default
if ( $custom_path && is_file("$custom_path/$file.php") ) {
$path = "$custom_path/$file.php";
}
else {
self::$i18n[$file] = array();
$path = "$base_path/$file.php";
}
if ( isset(self::$i18n[$path]) ) {
$result = array_merge($result, self::$i18n[$path]);
}
elseif ( is_file($path) && is_readable($path) ) {
self::$i18n[$path] = include $path;
$result = array_merge($result, self::$i18n[$path]);
}
else {
self::$i18n[$path] = array();
}
}
@ -2237,7 +2250,7 @@ class RRule implements RRuleInterface
/**
* Format a rule in a human readable string
* intl extension is required.
* `intl` extension is required.
*
* Available options
*
@ -2249,6 +2262,9 @@ class RRule implements RRuleInterface
* | `date_formatter` | callable| Function used to format the date (takes date, returns formatted)
* | `explicit_inifite`| bool | Mention "forever" if the rule is infinite (true)
* | `dtstart` | bool | Mention the start date (true)
* | `include_start` | bool |
* | `include_until` | bool |
* | `custom_path` | string |
*
* @param array $opt
*
@ -2267,7 +2283,8 @@ class RRule implements RRuleInterface
'fallback' => 'en',
'explicit_infinite' => true,
'include_start' => true,
'include_until' => true
'include_until' => true,
'custom_path' => null
);
// attempt to detect default locale
@ -2295,7 +2312,7 @@ class RRule implements RRuleInterface
$opt = array_merge($default_opt, $opt);
$i18n = self::i18nLoad($opt['locale'], $opt['fallback'], $opt['use_intl']);
$i18n = self::i18nLoad($opt['locale'], $opt['fallback'], $opt['use_intl'], $opt['custom_path']);
if ( $opt['date_formatter'] && ! is_callable($opt['date_formatter']) ) {
throw new \InvalidArgumentException('The option date_formatter must callable');

View File

@ -2629,15 +2629,19 @@ class RRuleTest extends TestCase
*/
public function testI18nFilesToLoadWithIntl($locale, $files)
{
$reflector = new ReflectionClass('RRule\RRule');
$method = $reflector->getMethod('i18nFilesToLoad');
$method->setAccessible(true);
if ( ! $files ) {
try {
$files = RRule::i18nFilesToLoad($locale, true);
$method->invokeArgs(null, array($locale, true));
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
} catch (\InvalidArgumentException $e) {
}
}
else {
$this->assertEquals($files, RRule::i18nFilesToLoad($locale, true));
$this->assertEquals($files,$method->invokeArgs(null, array($locale, true)));
}
}
@ -2646,15 +2650,19 @@ class RRuleTest extends TestCase
*/
public function testI18nFilesToLoadWithoutIntl($locale, $dummy, $files)
{
$reflector = new ReflectionClass('RRule\RRule');
$method = $reflector->getMethod('i18nFilesToLoad');
$method->setAccessible(true);
if ( ! $files ) {
try {
RRule::i18nFilesToLoad($locale, false);
$method->invokeArgs(null, array($locale, false));
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
} catch (\InvalidArgumentException $e) {
}
}
else {
$this->assertEquals($files, RRule::i18nFilesToLoad($locale, false));
$this->assertEquals($files, $method->invokeArgs(null, array($locale, false)));
}
}
@ -2755,8 +2763,6 @@ class RRuleTest extends TestCase
'dtstart' => '2007-01-01'
));
$reflector = new ReflectionClass('RRule\RRule');
setlocale(LC_MESSAGES, 'C');
$this->assertNotEmpty($rrule->humanReadable(array('fallback' => null)), 'C locale is converted to "en"');
}
@ -2805,6 +2811,22 @@ class RRuleTest extends TestCase
array('locale' => "en_IE", 'include_start' => false, 'explicit_infinite' => false),
"daily"
),
// with custom_path
'custom_path' => array(
"DTSTART:20170202T000000Z\nRRULE:FREQ=YEARLY;UNTIL=20170205T000000Z",
array('locale' => "fr_BE", "custom_path" => __DIR__."/i18n"),
"chaque année, à partir du 2/02/17, jusqu'au 5/02/17"
),
'custom_path cached separately' => array(
"DTSTART:20170202T000000Z\nRRULE:FREQ=YEARLY;UNTIL=20170205T000000Z",
array('locale' => "fr_BE"),
"tous les ans, à partir du 2/02/17, jusqu'au 5/02/17",
),
array(
"RRULE:FREQ=DAILY;UNTIL=20190405T055959Z",
array('locale' => "xx", "custom_path" => __DIR__."/i18n", "date_formatter" => function($date) { return "X"; }),
"daily, starting from X, until X"
),
);
}

9
tests/i18n/fr_BE.php Executable file
View File

@ -0,0 +1,9 @@
<?php
return array(
'yearly' => array(
'1' => 'chaque année',
'2' => 'une années sur deux',
'else' => 'toutes les %{interval} années'
)
);

153
tests/i18n/xx.php Executable file
View File

@ -0,0 +1,153 @@
<?php
return array(
'yearly' => array(
'1' => 'yearly',
'else' => 'every %{interval} years'
),
'monthly' => array(
'1' => 'monthly',
'else' => 'every %{interval} months'
),
'weekly' => array(
'1' => 'weekly',
'2' => 'every other week',
'else' => 'every %{interval} weeks'
),
'daily' => array(
'1' => 'daily',
'2' => 'every other day',
'else' => 'every %{interval} days'
),
'hourly' => array(
'1' => 'hourly',
'else' => 'every %{interval} hours'
),
'minutely' => array(
'1' => 'minutely',
'else' => 'every %{interval} minutes'
),
'secondly' => array(
'1' => 'secondly',
'else' => 'every %{interval} seconds'
),
'dtstart' => ', starting from %{date}',
'infinite' => ', forever',
'until' => ', until %{date}',
'count' => array(
'1' => ', one time',
'else' => ', %{count} times'
),
'and' => 'and',
'x_of_the_y' => array(
'yearly' => '%{x} of the year', // e.g. the first Monday of the year, or the first day of the year
'monthly' => '%{x} of the month',
),
'bymonth' => ' in %{months}',
'months' => array(
1 => 'January',
2 => 'February',
3 => 'March',
4 => 'April',
5 => 'May',
6 => 'June',
7 => 'July',
8 => 'August',
9 => 'September',
10 => 'October',
11 => 'November',
12 => 'December',
),
'byweekday' => ' on %{weekdays}',
'weekdays' => array(
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
7 => 'Sunday',
),
'nth_weekday' => array(
'1' => 'the first %{weekday}', // e.g. the first Monday
'2' => 'the second %{weekday}',
'3' => 'the third %{weekday}',
'else' => 'the %{n}th %{weekday}'
),
'-nth_weekday' => array(
'-1' => 'the last %{weekday}', // e.g. the last Monday
'-2' => 'the penultimate %{weekday}',
'-3' => 'the antepenultimate %{weekday}',
'else' => 'the %{n}th to the last %{weekday}'
),
'byweekno' => array(
'1' => ' on week %{weeks}',
'else' => ' on weeks number %{weeks}'
),
'nth_weekno' => '%{n}',
'bymonthday' => ' on %{monthdays}',
'nth_monthday' => array(
'1' => 'the 1st',
'2' => 'the 2nd',
'3' => 'the 3rd',
'21' => 'the 21st',
'22' => 'the 22nd',
'23' => 'the 23rd',
'31' => 'the 31st',
'else' => 'the %{n}th'
),
'-nth_monthday' => array(
'-1' => 'the last day',
'-2' => 'the penultimate day',
'-3' => 'the antepenultimate day',
'-21' => 'the 21st to the last day',
'-22' => 'the 22nd to the last day',
'-23' => 'the 23rd to the last day',
'-31' => 'the 31st to the last day',
'else' => 'the %{n}th to the last day'
),
'byyearday' => array(
'1' => ' on %{yeardays} day',
'else' => ' on %{yeardays} days'
),
'nth_yearday' => array(
'1' => 'the first',
'2' => 'the second',
'3' => 'the third',
'else' => 'the %{n}th'
),
'-nth_yearday' => array(
'-1' => 'the last',
'-2' => 'the penultimate',
'-3' => 'the antepenultimate',
'else' => 'the %{n}th to the last'
),
'byhour' => array(
'1' => ' at %{hours}',
'else' => ' at %{hours}'
),
'nth_hour' => '%{n}h',
'byminute' => array(
'1' => ' at minute %{minutes}',
'else' => ' at minutes %{minutes}'
),
'nth_minute' => '%{n}',
'bysecond' => array(
'1' => ' at second %{seconds}',
'else' => ' at seconds %{seconds}'
),
'nth_second' => '%{n}',
'bysetpos' => ', but only %{setpos} instance of this set',
'nth_setpos' => array(
'1' => 'the first',
'2' => 'the second',
'3' => 'the third',
'else' => 'the %{n}th'
),
'-nth_setpos' => array(
'-1' => 'the last',
'-2' => 'the penultimate',
'-3' => 'the antepenultimate',
'else' => 'the %{n}th to the last'
)
);