Localisation support for dates throughout the front-end using moment.js.
Closes #734 Closes #732
This commit is contained in:
parent
5dced5f990
commit
cd4ff6c4ea
|
@ -13,6 +13,7 @@ use b8\Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Languages Helper Class - Handles loading strings files and the strings within them.
|
* Languages Helper Class - Handles loading strings files and the strings within them.
|
||||||
|
*
|
||||||
* @package PHPCI\Helper
|
* @package PHPCI\Helper
|
||||||
*/
|
*/
|
||||||
class Lang
|
class Lang
|
||||||
|
@ -23,6 +24,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific string from the language file.
|
* Get a specific string from the language file.
|
||||||
|
*
|
||||||
* @param $string
|
* @param $string
|
||||||
* @return mixed|string
|
* @return mixed|string
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +50,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the currently active language.
|
* Get the currently active language.
|
||||||
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public static function getLanguage()
|
public static function getLanguage()
|
||||||
|
@ -57,7 +60,9 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try and load a language, and if successful, set it for use throughout the system.
|
* Try and load a language, and if successful, set it for use throughout the system.
|
||||||
|
*
|
||||||
* @param $language
|
* @param $language
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function setLanguage($language)
|
public static function setLanguage($language)
|
||||||
|
@ -73,6 +78,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of available languages and their names.
|
* Return a list of available languages and their names.
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getLanguageOptions()
|
public static function getLanguageOptions()
|
||||||
|
@ -90,6 +96,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the strings for the currently active language.
|
* Get the strings for the currently active language.
|
||||||
|
*
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public static function getStrings()
|
public static function getStrings()
|
||||||
|
@ -99,6 +106,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the Language helper, try load the language file for the user's browser or the configured default.
|
* Initialise the Language helper, try load the language file for the user's browser or the configured default.
|
||||||
|
*
|
||||||
* @param Config $config
|
* @param Config $config
|
||||||
*/
|
*/
|
||||||
public static function init(Config $config)
|
public static function init(Config $config)
|
||||||
|
@ -137,6 +145,7 @@ class Lang
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a specific language file.
|
* Load a specific language file.
|
||||||
|
*
|
||||||
* @return string[]|null
|
* @return string[]|null
|
||||||
*/
|
*/
|
||||||
protected static function loadLanguage()
|
protected static function loadLanguage()
|
||||||
|
@ -168,4 +177,24 @@ class Lang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a time tag for localization.
|
||||||
|
*
|
||||||
|
* See http://momentjs.com/docs/#/displaying/format/ for a list of supported formats.
|
||||||
|
*
|
||||||
|
* @param \DateTime $dateTime The dateTime to represent.
|
||||||
|
* @param string $format The moment.js format to use.
|
||||||
|
*
|
||||||
|
* @return string The formatted tag.
|
||||||
|
*/
|
||||||
|
public static function formatDateTime(\DateTime $dateTime, $format = 'lll')
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'<time datetime="%s" data-format="%s">%s</time>',
|
||||||
|
$dateTime->format(\DateTime::ISO8601),
|
||||||
|
$format,
|
||||||
|
$dateTime->format(\DateTime::RFC2822)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
?>
|
?>
|
||||||
<li class="time-label">
|
<li class="time-label">
|
||||||
<span class="bg-gray">
|
<span class="bg-gray">
|
||||||
<?php print $last->format('M j Y'); ?>
|
<?php print Lang::formatDateTime($last, 'll'); ?>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
<li>
|
<li>
|
||||||
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
|
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
|
||||||
<div class="timeline-item">
|
<div class="timeline-item">
|
||||||
<span class="time"><i class="fa fa-clock-o"></i> <?php print $updated->format('H:i'); ?></span>
|
<span class="time"><i class="fa fa-clock-o"></i> <?php print Lang::formatDateTime($updated, 'LT'); ?></span>
|
||||||
<h3 class="timeline-header">
|
<h3 class="timeline-header">
|
||||||
<a href="<?php print PHPCI_URL; ?>project/view/<?php print $build->getProjectId(); ?>">
|
<a href="<?php print PHPCI_URL; ?>project/view/<?php print $build->getProjectId(); ?>">
|
||||||
<?php print $build->getProject()->getTitle(); ?>
|
<?php print $build->getProject()->getTitle(); ?>
|
||||||
|
|
|
@ -27,12 +27,12 @@ foreach($projects as $project):
|
||||||
case 2:
|
case 2:
|
||||||
$successes++;
|
$successes++;
|
||||||
$statuses[] = 'ok';
|
$statuses[] = 'ok';
|
||||||
$success = is_null($success) && !is_null($build->getFinished()) ? $build->getFinished()->format('M j Y g:ia') : $success;
|
$success = is_null($success) && !is_null($build->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $success;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
$failures++;
|
$failures++;
|
||||||
$statuses[] = 'failed';
|
$statuses[] = 'failed';
|
||||||
$failure = is_null($failure) && !is_null($build->getFinished()) ? $build->getFinished()->format('M j Y g:ia') : $failure;
|
$failure = is_null($failure) && !is_null($build->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $failure;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ foreach($projects as $project):
|
||||||
$message = Lang::get('x_of_x_failed', $failures, $buildCount);
|
$message = Lang::get('x_of_x_failed', $failures, $buildCount);
|
||||||
|
|
||||||
if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinished())) {
|
if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinished())) {
|
||||||
$message .= Lang::get('last_successful_build', $lastSuccess->getFinished()->format('M j Y'));
|
$message .= Lang::get('last_successful_build', Lang::formatDateTime($lastSuccess->getFinished()));
|
||||||
} else {
|
} else {
|
||||||
$message .= Lang::get('never_built_successfully');
|
$message .= Lang::get('never_built_successfully');
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ foreach($projects as $project):
|
||||||
$shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount);
|
$shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount);
|
||||||
|
|
||||||
if (!is_null($lastFailure) && !is_null($lastFailure->getFinished())) {
|
if (!is_null($lastFailure) && !is_null($lastFailure->getFinished())) {
|
||||||
$message .= Lang::get('last_failed_build', $lastFailure->getFinished()->format('M j Y'));
|
$message .= Lang::get('last_failed_build', Lang::formatDateTime($lastFailure->getFinished()));
|
||||||
} else {
|
} else {
|
||||||
$message .= Lang::get('never_failed_build');
|
$message .= Lang::get('never_failed_build');
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,6 +275,7 @@
|
||||||
|
|
||||||
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/daterangepicker/daterangepicker.js" type="text/javascript"></script>
|
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/daterangepicker/daterangepicker.js" type="text/javascript"></script>
|
||||||
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/datepicker/bootstrap-datepicker.js" type="text/javascript"></script>
|
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/datepicker/bootstrap-datepicker.js" type="text/javascript"></script>
|
||||||
|
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/datepicker/locales/bootstrap-datepicker.<?php print Lang::getLanguage(); ?>.js" type="text/javascript"></script>
|
||||||
<script src="<?php print PHPCI_URL; ?>assets/js/AdminLTE/app.js" type="text/javascript"></script>
|
<script src="<?php print PHPCI_URL; ?>assets/js/AdminLTE/app.js" type="text/javascript"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
28
Tests/PHPCI/Helper/LangTest.php
Normal file
28
Tests/PHPCI/Helper/LangTest.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PHPCI\Tests\Helper;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use PHPCI\Helper\Lang;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTestCase;
|
||||||
|
|
||||||
|
class LangTest extends ProphecyTestCase
|
||||||
|
{
|
||||||
|
public function testLang_UsePassedParameters()
|
||||||
|
{
|
||||||
|
$dateTime = $this->prophesize('DateTime');
|
||||||
|
$dateTime->format(DateTime::ISO8601)->willReturn("ISODATE");
|
||||||
|
$dateTime->format(DateTime::RFC2822)->willReturn("RFCDATE");
|
||||||
|
|
||||||
|
$this->assertEquals('<time datetime="ISODATE" data-format="FORMAT">RFCDATE</time>', Lang::formatDateTime($dateTime->reveal(), 'FORMAT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLang_UseDefaultFormat()
|
||||||
|
{
|
||||||
|
$dateTime = $this->prophesize('DateTime');
|
||||||
|
$dateTime->format(DateTime::ISO8601)->willReturn("ISODATE");
|
||||||
|
$dateTime->format(DateTime::RFC2822)->willReturn("RFCDATE");
|
||||||
|
|
||||||
|
$this->assertEquals('<time datetime="ISODATE" data-format="lll">RFCDATE</time>', Lang::formatDateTime($dateTime->reveal()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,11 +18,16 @@
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
|
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
|
||||||
|
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
|
||||||
float: right;
|
float: right;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
.daterangepicker .ranges {
|
.daterangepicker .ranges {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -41,6 +46,14 @@
|
||||||
max-width: 270px;
|
max-width: 270px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.daterangepicker.show-calendar .calendar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker .calendar.single .calendar-date {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.daterangepicker .calendar th, .daterangepicker .calendar td {
|
.daterangepicker .calendar th, .daterangepicker .calendar td {
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -48,7 +61,8 @@
|
||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.daterangepicker .ranges label {
|
.daterangepicker .daterangepicker_start_input label,
|
||||||
|
.daterangepicker .daterangepicker_end_input label {
|
||||||
color: #333;
|
color: #333;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
@ -66,7 +80,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.daterangepicker .ranges .input-mini {
|
.daterangepicker .ranges .input-mini {
|
||||||
background-color: #eee;
|
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: #555;
|
color: #555;
|
||||||
|
@ -153,6 +166,37 @@
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.daterangepicker.openscenter:before {
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: inline-block;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-bottom: 7px solid #ccc;
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker.openscenter:after {
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: inline-block;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid #fff;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
.daterangepicker.opensright:before {
|
.daterangepicker.opensright:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -7px;
|
top: -7px;
|
||||||
|
@ -196,7 +240,7 @@
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.daterangepicker td.disabled {
|
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +255,24 @@
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.start-date {
|
||||||
|
-webkit-border-radius: 4px 0 0 4px;
|
||||||
|
-moz-border-radius: 4px 0 0 4px;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.end-date {
|
||||||
|
-webkit-border-radius: 0 4px 4px 0;
|
||||||
|
-moz-border-radius: 0 4px 4px 0;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker td.start-date.end-date {
|
||||||
|
-webkit-border-radius: 4px;
|
||||||
|
-moz-border-radius: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.daterangepicker td.active, .daterangepicker td.active:hover {
|
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||||
background-color: #357ebd;
|
background-color: #357ebd;
|
||||||
border-color: #3071a9;
|
border-color: #3071a9;
|
||||||
|
@ -239,7 +301,20 @@
|
||||||
width: 40%;
|
width: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
|
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.daterangepicker_start_input {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker_end_input {
|
||||||
|
float: left;
|
||||||
|
padding-left: 11px
|
||||||
|
}
|
||||||
|
|
||||||
|
.daterangepicker th.month {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,16 @@ var PHPCI = {
|
||||||
intervals: {},
|
intervals: {},
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
|
// Setup the date locale
|
||||||
|
moment.locale(PHPCI_LANGUAGE);
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
// Format datetimes
|
||||||
|
$('time[datetime]').each(function() {
|
||||||
|
var $this = $(this);
|
||||||
|
$this.text(moment(this.dateTime).format($this.data('format') || 'lll'));
|
||||||
|
});
|
||||||
|
|
||||||
// Update latest builds every 5 seconds:
|
// Update latest builds every 5 seconds:
|
||||||
PHPCI.getBuilds();
|
PHPCI.getBuilds();
|
||||||
PHPCI.intervals.getBuilds = setInterval(PHPCI.getBuilds, 5000);
|
PHPCI.intervals.getBuilds = setInterval(PHPCI.getBuilds, 5000);
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue