Skip to content

Commit

Permalink
feat: Format date and numbers based on locale (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
DenverCoder1 authored May 29, 2022
1 parent 9b466a7 commit 6a28226
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 90 deletions.
1 change: 1 addition & 0 deletions .github/workflows/phpunit-ci-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
- name: PHPUnit Tests
uses: php-actions/phpunit@v3
with:
php_extensions: imagick intl
bootstrap: vendor/autoload.php
configuration: tests/phpunit/phpunit.xml
args: --testdox
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 43 additions & 25 deletions src/card.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,38 @@
* Convert date from Y-M-D to more human-readable format
*
* @param string $dateString String in Y-M-D format
* @param string $format Date format to use
* @param string|null $format Date format to use, or null to use locale default
* @param string $locale Locale code
* @return string Formatted Date string
*/
function formatDate(string $dateString, string $format): string
function formatDate(string $dateString, string|null $format, string $locale): string
{
$date = new DateTime($dateString);
$formatted = "";
$patternGenerator = new IntlDatePatternGenerator($locale);
// if current year, display only month and day
if (date_format($date, "Y") == date("Y")) {
// remove brackets and all text within them
$formatted = date_format($date, preg_replace("/\[.*?\]/", "", $format));
if ($format) {
// remove brackets and all text within them
$formatted = date_format($date, preg_replace("/\[.*?\]/", "", $format));
} else {
// format without year using locale
$pattern = $patternGenerator->getBestPattern("MMM d");
$dateFormatter = new IntlDateFormatter($locale, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, pattern: $pattern);
$formatted = $dateFormatter->format($date);
}
}
// otherwise, display month, day, and year (just brackets removed)
// otherwise, display month, day, and year
else {
$formatted = date_format($date, str_replace(array("[", "]"), "", $format));
if ($format) {
// remove brackets, but leave text within them
$formatted = date_format($date, str_replace(["[", "]"], "", $format));
} else {
// format with year using locale
$pattern = $patternGenerator->getBestPattern("YYYY MMM d");
$dateFormatter = new IntlDateFormatter($locale, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, pattern: $pattern);
$formatted = $dateFormatter->format($date);
}
}
// sanitize and return formatted date
return htmlspecialchars($formatted);
Expand Down Expand Up @@ -101,35 +118,36 @@ function generateCard(array $stats, array $params = null): string
$theme = getRequestedTheme($params);

// get the labels from the translations file
$labels = include "translations.php";
$translations = include "translations.php";
// get requested locale, default to English
$locale = $params["locale"] ?? "en";
// if the locale does not exist in the first value of the labels array, throw an exception
if (!isset(reset($labels)[$locale])) {
throw new InvalidArgumentException("That locale is not supported. You can help by adding it to the translations file.");
}
$localeCode = $params["locale"] ?? "en";
$localeTranslations = $translations[$localeCode] ?? $translations["en"];

// get date format
$dateFormat = $params["date_format"] ?? "M j[, Y]";
// locale date formatter (used only if date_format is not specified)
$dateFormat = $params["date_format"] ?? $localeTranslations["date_format"] ?? null;

// number formatter
$numFormatter = new NumberFormatter($localeCode, NumberFormatter::DECIMAL);

// total contributions
$totalContributions = $stats["totalContributions"];
$firstContribution = formatDate($stats["firstContribution"], $dateFormat);
$totalContributions = $numFormatter->format($stats["totalContributions"]);
$firstContribution = formatDate($stats["firstContribution"], $dateFormat, $localeCode);
$totalContributionsRange = $firstContribution . " - Present";

// current streak
$currentStreak = $stats["currentStreak"]["length"];
$currentStreakStart = formatDate($stats["currentStreak"]["start"], $dateFormat);
$currentStreakEnd = formatDate($stats["currentStreak"]["end"], $dateFormat);
$currentStreak = $numFormatter->format($stats["currentStreak"]["length"]);
$currentStreakStart = formatDate($stats["currentStreak"]["start"], $dateFormat, $localeCode);
$currentStreakEnd = formatDate($stats["currentStreak"]["end"], $dateFormat, $localeCode);
$currentStreakRange = $currentStreakStart;
if ($currentStreakStart != $currentStreakEnd) {
$currentStreakRange .= " - " . $currentStreakEnd;
}

// longest streak
$longestStreak = $stats["longestStreak"]["length"];
$longestStreakStart = formatDate($stats["longestStreak"]["start"], $dateFormat);
$longestStreakEnd = formatDate($stats["longestStreak"]["end"], $dateFormat);
$longestStreak = $numFormatter->format($stats["longestStreak"]["length"]);
$longestStreakStart = formatDate($stats["longestStreak"]["start"], $dateFormat, $localeCode);
$longestStreakEnd = formatDate($stats["longestStreak"]["end"], $dateFormat, $localeCode);
$longestStreakRange = $longestStreakStart;
if ($longestStreakStart != $longestStreakEnd) {
$longestStreakRange .= " - " . $longestStreakEnd;
Expand Down Expand Up @@ -178,7 +196,7 @@ function generateCard(array $stats, array $params = null): string
<g transform='translate(1,84)'>
<rect width='163' height='50' stroke='none' fill='none'></rect>
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:14px;font-style:normal;fill:{$theme["sideLabels"]};stroke:none; opacity: 0; animation: fadein 0.5s linear forwards 0.7s;'>
{$labels["totalContributions"][$locale]}
{$localeTranslations["Total Contributions"]}
</text>
</g>
Expand All @@ -203,7 +221,7 @@ function generateCard(array $stats, array $params = null): string
<g transform='translate(166,108)'>
<rect width='163' height='50' stroke='none' fill='none'></rect>
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:700;font-size:14px;font-style:normal;fill:{$theme["currStreakLabel"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 0.9s;'>
{$labels["currentStreak"][$locale]}
{$localeTranslations["Current Streak"]}
</text>
</g>
Expand Down Expand Up @@ -239,7 +257,7 @@ function generateCard(array $stats, array $params = null): string
<g transform='translate(331,84)'>
<rect width='163' height='50' stroke='none' fill='none'></rect>
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:14px;font-style:normal;fill:{$theme["sideLabels"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 1.3s;'>
{$labels["longestStreak"][$locale]}
{$localeTranslations["Longest Streak"]}
</text>
</g>
Expand Down Expand Up @@ -371,7 +389,7 @@ function renderOutput(string|array $output, int $responseCode = 200): void
// set content type to JSON
header('Content-Type: application/json');
// generate array from output
$data = gettype($output) === "string" ? array("error" => $output) : $output;
$data = gettype($output) === "string" ? ["error" => $output] : $output;
// output as JSON
echo json_encode($data);
}
Expand Down
4 changes: 2 additions & 2 deletions src/colors.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

// return a list of valid CSS colors
return array(
return [
"aliceblue",
"antiquewhite",
"aqua",
Expand Down Expand Up @@ -150,4 +150,4 @@
"whitesmoke",
"yellow",
"yellowgreen",
);
];
3 changes: 2 additions & 1 deletion src/demo/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
$THEMES = include "../themes.php";
$TRANSLATIONS = include "../translations.php";
// Get the keys of the first value in the translations array
$LOCALES = array_keys(reset($TRANSLATIONS));
$LOCALES = array_keys($TRANSLATIONS);

?>

Expand Down Expand Up @@ -78,6 +78,7 @@ function gtag() {

<label for="date_format">Date Format</label>
<select class="param" id="date_format" name="date_format">
<option value="">default</option>
<option value="M j[, Y]">Aug 10, 2016</option>
<option value="j M[ Y]">10 Aug 2016</option>
<option value="[Y ]M j">2016 Aug 10</option>
Expand Down
3 changes: 2 additions & 1 deletion src/demo/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ let preview = {
// default values
defaults: {
theme: "default",
locale: "en",
hide_border: "false",
date_format: "",
locale: "en",
},
// update the preview
update: function () {
Expand Down
17 changes: 9 additions & 8 deletions src/stats.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function getContributionGraphs(string $user): array
// Get the years the user has contributed
$contributionYears = getContributionYears($user);
// build a list of individual requests
$requests = array();
$requests = [];
foreach ($contributionYears as $year) {
// create query for year
$start = "$year-01-01T00:00:00Z";
Expand Down Expand Up @@ -53,7 +53,7 @@ function getContributionGraphs(string $user): array
}
curl_multi_close($multi);
// collect responses from last to first
$response = array();
$response = [];
foreach ($requests as $request) {
array_unshift($response, json_decode(curl_multi_getcontent($request)));
}
Expand All @@ -65,13 +65,14 @@ function getContributionGraphs(string $user): array
*
* @return array<string> List of tokens
*/
function getGitHubTokens() {
function getGitHubTokens()
{
// result is already calculated
if (isset($GLOBALS["ALL_TOKENS"])) {
return $GLOBALS["ALL_TOKENS"];
}
// find all tokens in environment variables
$tokens = array($_SERVER["TOKEN"] ?? "");
$tokens = isset($_SERVER["TOKEN"]) ? [$_SERVER["TOKEN"]] : [];
for ($i = 2; $i < 4; $i++) {
if (isset($_SERVER["TOKEN$i"])) {
// add token to list
Expand All @@ -93,13 +94,13 @@ function getGraphQLCurlHandle(string $query)
{
$all_tokens = getGitHubTokens();
$token = $all_tokens[array_rand($all_tokens)];
$headers = array(
$headers = [
"Authorization: bearer $token",
"Content-Type: application/json",
"Accept: application/vnd.github.v4.idl",
"User-Agent: GitHub-Readme-Streak-Stats"
);
$body = array("query" => $query);
];
$body = ["query" => $query];
// create curl request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.github.com/graphql");
Expand Down Expand Up @@ -191,7 +192,7 @@ function getContributionYears(string $user): array
function getContributionDates(array $contributionGraphs): array
{
// get contributions from HTML
$contributions = array();
$contributions = [];
$today = date("Y-m-d");
$tomorrow = date("Y-m-d", strtotime("tomorrow"));
foreach ($contributionGraphs as $graph) {
Expand Down
52 changes: 37 additions & 15 deletions src/translations.php
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
<?php

/**
* Locales
* -------
* For a list of supported locale codes, see https://gist.github.com/DenverCoder1/f61147ba26bfcf7c3bf605af7d3382d5
*
* Date Format
* -----------
* Supplying a date format is optional and will be used instead of the default locale date format.
* If the default date format for the locale displays correctly, you should omit the date_format parameter.
*
* Different year Same year Format string
* -------------- --------- -------------
* 10/8/2016 10/8 j/n[/Y]
* 8/10/2016 8/10 n/j[/Y]
* 2016.8.10 8.10 [Y.]n.j
*
* For info on valid date_format strings, see https://github.com/DenverCoder1/github-readme-streak-stats#date-formats
*/

return [
"currentStreak" => [
"en" => "Current Streak",
"de" => "Aktuelle Serie",
"es" => "Racha Actual",
"ja" => "現在のストリーク",
"en" => [
"Total Contributions" => "Total Contributions",
"Current Streak" => "Current Streak",
"Longest Streak" => "Longest Streak",
],
"de" => [
"Total Contributions" => "Gesamte Beiträge",
"Current Streak" => "Aktuelle Serie",
"Longest Streak" => "Längste Serie",
],
"totalContributions" => [
"en" => "Total Contributions",
"de" => "Gesamte Beiträge",
"es" => "Todas Contribuciones",
"ja" => "総コントリビューション数",
"es" => [
"Total Contributions" => "Todas Contribuciones",
"Current Streak" => "Racha Actual",
"Longest Streak" => "Racha Más Larga",
],
"longestStreak" => [
"en" => "Longest Streak",
"de" => "Längste Serie",
"es" => "Racha Más Larga",
"ja" => "最長のストリーク",
"ja" => [
"date_format" => "[Y.]n.j",
"Total Contributions" => "総コントリビューション数",
"Current Streak" => "現在のストリーク",
"Longest Streak" => "最長のストリーク",
],
];
14 changes: 7 additions & 7 deletions tests/OptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

final class OptionsTest extends TestCase
{
private $defaultTheme = array(
private $defaultTheme = [
"background" => "#fffefe",
"border" => "#e4e2e2",
"stroke" => "#e4e2e2",
Expand All @@ -20,7 +20,7 @@ final class OptionsTest extends TestCase
"currStreakLabel" => "#fb8c00",
"sideLabels" => "#151515",
"dates" => "#464646",
);
];

/**
* Test theme request parameters return colors for theme
Expand Down Expand Up @@ -60,7 +60,7 @@ public function testThemesHaveValidParameters(): void
// check that there are no extra keys in the theme
$this->assertEquals(
array_diff_key($colors, $this->defaultTheme),
array(),
[],
"The theme '$theme' contains invalid parameters."
);
# check that no parameters are missing and all values are valid
Expand Down Expand Up @@ -172,7 +172,7 @@ public function testHideBorder(): void
public function testDateFormatSameYear(): void
{
$year = date("Y");
$formatted = formatDate("$year-04-12", "M j[, Y]");
$formatted = formatDate("$year-04-12", "M j[, Y]", "en");
$this->assertEquals("Apr 12", $formatted);
}

Expand All @@ -181,7 +181,7 @@ public function testDateFormatSameYear(): void
*/
public function testDateFormatDifferentYear(): void
{
$formatted = formatDate("2000-04-12", "M j[, Y]");
$formatted = formatDate("2000-04-12", "M j[, Y]", "en");
$this->assertEquals("Apr 12, 2000", $formatted);
}

Expand All @@ -190,7 +190,7 @@ public function testDateFormatDifferentYear(): void
*/
public function testDateFormatNoBracketsDiffYear(): void
{
$formatted = formatDate("2000-04-12", "Y/m/d");
$formatted = formatDate("2000-04-12", "Y/m/d", "en");
$this->assertEquals("2000/04/12", $formatted);
}

Expand All @@ -200,7 +200,7 @@ public function testDateFormatNoBracketsDiffYear(): void
public function testDateFormatNoBracketsSameYear(): void
{
$year = date("Y");
$formatted = formatDate("$year-04-12", "Y/m/d");
$formatted = formatDate("$year-04-12", "Y/m/d", "en");
$this->assertEquals("$year/04/12", $formatted);
}
}
Loading

0 comments on commit 6a28226

Please sign in to comment.