Localisation support for dates throughout the front-end using moment.js.

Closes #734
Closes #732
This commit is contained in:
Adirelle 2015-01-09 08:51:35 +01:00 committed by Dan Cryer
parent 5dced5f990
commit cd4ff6c4ea
8 changed files with 869 additions and 356 deletions

View file

@ -13,6 +13,7 @@ use b8\Config;
/**
* Languages Helper Class - Handles loading strings files and the strings within them.
*
* @package PHPCI\Helper
*/
class Lang
@ -23,6 +24,7 @@ class Lang
/**
* Get a specific string from the language file.
*
* @param $string
* @return mixed|string
*/
@ -48,6 +50,7 @@ class Lang
/**
* Get the currently active language.
*
* @return string|null
*/
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.
*
* @param $language
*
* @return bool
*/
public static function setLanguage($language)
@ -73,6 +78,7 @@ class Lang
/**
* Return a list of available languages and their names.
*
* @return array
*/
public static function getLanguageOptions()
@ -90,6 +96,7 @@ class Lang
/**
* Get the strings for the currently active language.
*
* @return string[]
*/
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.
*
* @param Config $config
*/
public static function init(Config $config)
@ -137,6 +145,7 @@ class Lang
/**
* Load a specific language file.
*
* @return string[]|null
*/
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)
);
}
}

View file

@ -48,7 +48,7 @@
?>
<li class="time-label">
<span class="bg-gray">
<?php print $last->format('M j Y'); ?>
<?php print Lang::formatDateTime($last, 'll'); ?>
</span>
</li>
<?php endif; ?>
@ -58,7 +58,7 @@
<li>
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
<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">
<a href="<?php print PHPCI_URL; ?>project/view/<?php print $build->getProjectId(); ?>">
<?php print $build->getProject()->getTitle(); ?>

View file

@ -27,12 +27,12 @@ foreach($projects as $project):
case 2:
$successes++;
$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;
case 3:
$failures++;
$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;
}
}
@ -59,7 +59,7 @@ foreach($projects as $project):
$message = Lang::get('x_of_x_failed', $failures, $buildCount);
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 {
$message .= Lang::get('never_built_successfully');
}
@ -68,7 +68,7 @@ foreach($projects as $project):
$shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount);
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 {
$message .= Lang::get('never_failed_build');
}

View file

@ -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/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>
</body>

View 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()));
}
}

View file

@ -18,11 +18,16 @@
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
float: right;
margin: 4px;
}
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
float: none;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
@ -41,6 +46,14 @@
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
@ -48,7 +61,8 @@
min-width: 32px;
}
.daterangepicker .ranges label {
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
@ -66,7 +80,6 @@
}
.daterangepicker .ranges .input-mini {
background-color: #eee;
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
@ -153,6 +166,37 @@
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 {
position: absolute;
top: -7px;
@ -196,7 +240,7 @@
color: #999;
}
.daterangepicker td.disabled {
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
}
@ -211,6 +255,24 @@
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 {
background-color: #357ebd;
border-color: #3071a9;
@ -239,7 +301,20 @@
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;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

View file

@ -3,7 +3,16 @@ var PHPCI = {
intervals: {},
init: function () {
// Setup the date locale
moment.locale(PHPCI_LANGUAGE);
$(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:
PHPCI.getBuilds();
PHPCI.intervals.getBuilds = setInterval(PHPCI.getBuilds, 5000);

File diff suppressed because one or more lines are too long