mirror of
https://github.com/rlanvin/php-rrule.git
synced 2024-12-02 09: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:
parent
7c93c0e48a
commit
200b923c9e
@ -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)
|
- 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
|
## [1.6.3] - 2019-01-13
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -2153,7 +2153,7 @@ class RRule implements RRuleInterface
|
|||||||
* Test if intl extension is loaded
|
* Test if intl extension is loaded
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
static public function intlLoaded()
|
static protected function intlLoaded()
|
||||||
{
|
{
|
||||||
if ( self::$intl_loaded === null ) {
|
if ( self::$intl_loaded === null ) {
|
||||||
self::$intl_loaded = extension_loaded('intl');
|
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.
|
* Parse a locale and returns a list of files to load.
|
||||||
|
* For example "fr_FR" will produce "fr" and "fr_FR"
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
static public function i18nFilesToLoad($locale, $use_intl = null)
|
static protected function i18nFilesToLoad($locale, $use_intl = null)
|
||||||
{
|
{
|
||||||
if ( $use_intl === null ) {
|
if ( $use_intl === null ) {
|
||||||
$use_intl = self::intlLoaded();
|
$use_intl = self::intlLoaded();
|
||||||
@ -2202,26 +2203,38 @@ class RRule implements RRuleInterface
|
|||||||
*
|
*
|
||||||
* @param string $locale
|
* @param string $locale
|
||||||
* @param string|null $fallback
|
* @param string|null $fallback
|
||||||
|
* @param bool $use_intl
|
||||||
|
* @param string $custom_path
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @throws \InvalidArgumentException
|
* @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);
|
$files = self::i18nFilesToLoad($locale, $use_intl);
|
||||||
|
|
||||||
|
$base_path = __DIR__.'/i18n';
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
foreach ( $files as $file ) {
|
foreach ( $files as $file ) {
|
||||||
$path = __DIR__."/i18n/$file.php";
|
|
||||||
if ( isset(self::$i18n[$file]) ) {
|
// if the file exists in $custom_path, it overrides the default
|
||||||
$result = array_merge($result, self::$i18n[$file]);
|
if ( $custom_path && is_file("$custom_path/$file.php") ) {
|
||||||
}
|
$path = "$custom_path/$file.php";
|
||||||
elseif ( is_file($path) && is_readable($path) ) {
|
|
||||||
self::$i18n[$file] = include $path;
|
|
||||||
$result = array_merge($result, self::$i18n[$file]);
|
|
||||||
}
|
}
|
||||||
else {
|
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
|
* Format a rule in a human readable string
|
||||||
* intl extension is required.
|
* `intl` extension is required.
|
||||||
*
|
*
|
||||||
* Available options
|
* Available options
|
||||||
*
|
*
|
||||||
@ -2249,6 +2262,9 @@ class RRule implements RRuleInterface
|
|||||||
* | `date_formatter` | callable| Function used to format the date (takes date, returns formatted)
|
* | `date_formatter` | callable| Function used to format the date (takes date, returns formatted)
|
||||||
* | `explicit_inifite`| bool | Mention "forever" if the rule is infinite (true)
|
* | `explicit_inifite`| bool | Mention "forever" if the rule is infinite (true)
|
||||||
* | `dtstart` | bool | Mention the start date (true)
|
* | `dtstart` | bool | Mention the start date (true)
|
||||||
|
* | `include_start` | bool |
|
||||||
|
* | `include_until` | bool |
|
||||||
|
* | `custom_path` | string |
|
||||||
*
|
*
|
||||||
* @param array $opt
|
* @param array $opt
|
||||||
*
|
*
|
||||||
@ -2267,7 +2283,8 @@ class RRule implements RRuleInterface
|
|||||||
'fallback' => 'en',
|
'fallback' => 'en',
|
||||||
'explicit_infinite' => true,
|
'explicit_infinite' => true,
|
||||||
'include_start' => true,
|
'include_start' => true,
|
||||||
'include_until' => true
|
'include_until' => true,
|
||||||
|
'custom_path' => null
|
||||||
);
|
);
|
||||||
|
|
||||||
// attempt to detect default locale
|
// attempt to detect default locale
|
||||||
@ -2295,7 +2312,7 @@ class RRule implements RRuleInterface
|
|||||||
|
|
||||||
$opt = array_merge($default_opt, $opt);
|
$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']) ) {
|
if ( $opt['date_formatter'] && ! is_callable($opt['date_formatter']) ) {
|
||||||
throw new \InvalidArgumentException('The option date_formatter must callable');
|
throw new \InvalidArgumentException('The option date_formatter must callable');
|
||||||
|
@ -2629,15 +2629,19 @@ class RRuleTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testI18nFilesToLoadWithIntl($locale, $files)
|
public function testI18nFilesToLoadWithIntl($locale, $files)
|
||||||
{
|
{
|
||||||
|
$reflector = new ReflectionClass('RRule\RRule');
|
||||||
|
$method = $reflector->getMethod('i18nFilesToLoad');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
if ( ! $files ) {
|
if ( ! $files ) {
|
||||||
try {
|
try {
|
||||||
$files = RRule::i18nFilesToLoad($locale, true);
|
$method->invokeArgs(null, array($locale, true));
|
||||||
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
|
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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)
|
public function testI18nFilesToLoadWithoutIntl($locale, $dummy, $files)
|
||||||
{
|
{
|
||||||
|
$reflector = new ReflectionClass('RRule\RRule');
|
||||||
|
$method = $reflector->getMethod('i18nFilesToLoad');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
if ( ! $files ) {
|
if ( ! $files ) {
|
||||||
try {
|
try {
|
||||||
RRule::i18nFilesToLoad($locale, false);
|
$method->invokeArgs(null, array($locale, false));
|
||||||
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
|
$this->fail('Expected InvalidArgumentException not thrown (files was '.json_encode($files).')');
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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'
|
'dtstart' => '2007-01-01'
|
||||||
));
|
));
|
||||||
|
|
||||||
$reflector = new ReflectionClass('RRule\RRule');
|
|
||||||
|
|
||||||
setlocale(LC_MESSAGES, 'C');
|
setlocale(LC_MESSAGES, 'C');
|
||||||
$this->assertNotEmpty($rrule->humanReadable(array('fallback' => null)), 'C locale is converted to "en"');
|
$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),
|
array('locale' => "en_IE", 'include_start' => false, 'explicit_infinite' => false),
|
||||||
"daily"
|
"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
9
tests/i18n/fr_BE.php
Executable 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
153
tests/i18n/xx.php
Executable 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'
|
||||||
|
)
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user