diff --git a/.travis.yml b/.travis.yml
index 03071a4..59b86c0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,11 @@
sudo: false
language: php
+addons:
+ apt:
+ packages:
+ - locales
+ - language-pack-de
+ - language-pack-fr
cache:
directories:
- $HOME/.composer/cache
@@ -14,4 +20,4 @@ install:
script:
- make clean
- make check_permissions
- - make test
+ - make all_tests
diff --git a/Makefile b/Makefile
index f3065b7..1d8a73a 100644
--- a/Makefile
+++ b/Makefile
@@ -124,8 +124,20 @@ test:
@echo "-------"
@echo "PHPUNIT"
@echo "-------"
- @mkdir -p sandbox
- @$(BIN)/phpunit tests
+ @mkdir -p sandbox coverage
+ @$(BIN)/phpunit --coverage-php coverage/main.cov --testsuite unit-tests
+
+locale_test_%:
+ @UT_LOCALE=$*.utf8 \
+ $(BIN)/phpunit \
+ --coverage-php coverage/$(firstword $(subst _, ,$*)).cov \
+ --bootstrap tests/languages/bootstrap.php \
+ --testsuite language-$(firstword $(subst _, ,$*))
+
+all_tests: test locale_test_de_DE locale_test_en_US locale_test_fr_FR
+ @$(BIN)/phpcov merge --html coverage coverage
+ @# --text doesn't work with phpunit 4.* (v5 requires PHP 5.6)
+ @#$(BIN)/phpcov merge --text coverage/txt coverage
##
# Custom release archive generation
diff --git a/application/Utils.php b/application/Utils.php
index 35d6522..a936b09 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -216,20 +216,55 @@ function is_session_id_valid($sessionId)
function autoLocale($headerLocale)
{
// Default if browser does not send HTTP_ACCEPT_LANGUAGE
- $attempts = array('en_US');
+ $attempts = array('en_US', 'en_US.utf8', 'en_US.UTF-8');
if (isset($headerLocale)) {
// (It's a bit crude, but it works very well. Preferred language is always presented first.)
- if (preg_match('/([a-z]{2})-?([a-z]{2})?/i', $headerLocale, $matches)) {
- $loc = $matches[1] . (!empty($matches[2]) ? '_' . strtoupper($matches[2]) : '');
- $attempts = array(
- $loc.'.UTF-8', $loc, str_replace('_', '-', $loc).'.UTF-8', str_replace('_', '-', $loc),
- $loc . '_' . strtoupper($loc).'.UTF-8', $loc . '_' . strtoupper($loc),
- $loc . '_' . $loc.'.UTF-8', $loc . '_' . $loc, $loc . '-' . strtoupper($loc).'.UTF-8',
- $loc . '-' . strtoupper($loc), $loc . '-' . $loc.'.UTF-8', $loc . '-' . $loc
- );
+ if (preg_match('/([a-z]{2,3})[-_]?([a-z]{2})?/i', $headerLocale, $matches)) {
+ $first = [strtolower($matches[1]), strtoupper($matches[1])];
+ $separators = ['_', '-'];
+ $encodings = ['utf8', 'UTF-8'];
+ if (!empty($matches[2])) {
+ $second = [strtoupper($matches[2]), strtolower($matches[2])];
+ $attempts = cartesian_product_generator([$first, $separators, $second, ['.'], $encodings]);
+ } else {
+ $attempts = cartesian_product_generator([$first, $separators, $first, ['.'], $encodings]);
+ }
+ }
+ }
+ setlocale(LC_ALL, implode('implode', iterator_to_array($attempts)));
+}
+
+/**
+ * Build a Generator object representing the cartesian product from given $items.
+ *
+ * Example:
+ * [['a'], ['b', 'c']]
+ * will generate:
+ * [
+ * ['a', 'b'],
+ * ['a', 'c'],
+ * ]
+ *
+ * @param array $items array of array of string
+ *
+ * @return Generator representing the cartesian product of given array.
+ *
+ * @see https://en.wikipedia.org/wiki/Cartesian_product
+ */
+function cartesian_product_generator($items)
+{
+ if (empty($items)) {
+ yield [];
+ }
+ $subArray = array_pop($items);
+ if (empty($subArray)) {
+ return;
+ }
+ foreach (cartesian_product_generator($items) as $item) {
+ foreach ($subArray as $value) {
+ yield $item + [count($item) => $value];
}
}
- setlocale(LC_ALL, $attempts);
}
/**
@@ -270,3 +305,33 @@ function normalize_spaces($string)
{
return preg_replace('/\s{2,}/', ' ', trim($string));
}
+
+/**
+ * Format the date according to the locale.
+ *
+ * Requires php-intl to display international datetimes,
+ * otherwise default format '%c' will be returned.
+ *
+ * @param DateTime $date to format.
+ * @param bool $intl Use international format if true.
+ *
+ * @return bool|string Formatted date, or false if the input is invalid.
+ */
+function format_date($date, $intl = true)
+{
+ if (! $date instanceof DateTime) {
+ return false;
+ }
+
+ if (! $intl || ! class_exists('IntlDateFormatter')) {
+ return strftime('%c', $date->getTimestamp());
+ }
+
+ $formatter = new IntlDateFormatter(
+ setlocale(LC_TIME, 0),
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::LONG
+ );
+
+ return $formatter->format($date);
+}
diff --git a/composer.json b/composer.json
index b82acee..70b87bb 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,8 @@
"phpmd/phpmd" : "@stable",
"phpunit/phpunit": "4.8.*",
"sebastian/phpcpd": "*",
- "squizlabs/php_codesniffer": "2.*"
+ "squizlabs/php_codesniffer": "2.*",
+ "phpunit/phpcov": "*"
},
"autoload": {
"psr-4": {
diff --git a/phpunit.xml b/phpunit.xml
index d6e01c3..8b66e6c 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -3,13 +3,25 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
colors="true">
+