summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-12-18 11:19:14 +0000
committerFilipa Lacerda <filipa@gitlab.com>2017-12-18 11:19:14 +0000
commitc20720cf84132d7bf14763aba0570bd8882f9c6d (patch)
tree69d943464250790018b156b2d75f6ca8ba1536fd
parentdcc0455c4864b4251559cfc922bf7c53ae317497 (diff)
parente720e8b8fbc9c7e04e528a54cce37d18c776d2d2 (diff)
downloadgitlab-ce-c20720cf84132d7bf14763aba0570bd8882f9c6d.tar.gz
Merge branch 'winh-translate-contributors-page-dates' into 'master'
Translate contributors page dates Closes #39283 and #38592 See merge request gitlab-org/gitlab-ce!15846
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js21
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js17
-rw-r--r--app/assets/javascripts/lib/utils/tick_formats.js39
-rw-r--r--app/assets/javascripts/locale/index.js15
-rw-r--r--changelogs/unreleased/winh-translate-contributors-page-dates.yml5
-rw-r--r--doc/development/i18n/externalization.md15
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_spec.js26
-rw-r--r--spec/javascripts/helpers/locale_helper.js11
-rw-r--r--spec/javascripts/lib/utils/tick_formats_spec.js40
-rw-r--r--spec/javascripts/locale/index_spec.js35
10 files changed, 210 insertions, 14 deletions
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index e7232ca3712..743c049e9fb 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -1,13 +1,14 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
import _ from 'underscore';
-import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
-import { n__ } from '../locale';
+import { n__, s__, createDateTimeFormat, sprintf } from '../locale';
export default (function() {
- function ContributorsStatGraph() {}
+ function ContributorsStatGraph() {
+ this.dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' });
+ }
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
@@ -95,11 +96,15 @@ export default (function() {
};
ContributorsStatGraph.prototype.change_date_header = function() {
- var print, print_date_format, x_domain;
- x_domain = ContributorsGraph.prototype.x_domain;
- print_date_format = d3.time.format("%B %e %Y");
- print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]);
- return $("#date_header").text(print);
+ const x_domain = ContributorsGraph.prototype.x_domain;
+ const formattedDateRange = sprintf(
+ s__('ContributorsPage|%{startDate} – %{endDate}'),
+ {
+ startDate: this.dateFormat.format(new Date(x_domain[0])),
+ endDate: this.dateFormat.format(new Date(x_domain[1])),
+ },
+ );
+ return $('#date_header').text(formattedDateRange);
};
ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) {
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index f64b4638485..187f3c008e8 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
import _ from 'underscore';
import d3 from 'd3';
+import { dateTickFormat } from '../lib/utils/tick_formats';
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
const hasProp = {}.hasOwnProperty;
@@ -93,9 +94,12 @@ export const ContributorsMasterGraph = (function(superClass) {
extend(ContributorsMasterGraph, superClass);
function ContributorsMasterGraph(data1) {
+ const $parentElement = $('#contributors-master');
+ const parentPadding = parseFloat($parentElement.css('padding-left')) + parseFloat($parentElement.css('padding-right'));
+
this.data = data1;
this.update_content = this.update_content.bind(this);
- this.width = $('.content').width() - 70;
+ this.width = $('.content').width() - parentPadding - (this.MARGIN.left + this.MARGIN.right);
this.height = 200;
this.x = null;
this.y = null;
@@ -131,7 +135,10 @@ export const ContributorsMasterGraph = (function(superClass) {
};
ContributorsMasterGraph.prototype.create_axes = function() {
- this.x_axis = d3.svg.axis().scale(this.x).orient("bottom");
+ this.x_axis = d3.svg.axis()
+ .scale(this.x)
+ .orient('bottom')
+ .tickFormat(dateTickFormat);
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
@@ -219,7 +226,11 @@ export const ContributorsAuthorGraph = (function(superClass) {
};
ContributorsAuthorGraph.prototype.create_axes = function() {
- this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8);
+ this.x_axis = d3.svg.axis()
+ .scale(this.x)
+ .orient('bottom')
+ .ticks(8)
+ .tickFormat(dateTickFormat);
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
diff --git a/app/assets/javascripts/lib/utils/tick_formats.js b/app/assets/javascripts/lib/utils/tick_formats.js
new file mode 100644
index 00000000000..0c10a85e336
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/tick_formats.js
@@ -0,0 +1,39 @@
+import { createDateTimeFormat } from '../../locale';
+
+let dateTimeFormats;
+
+export const initDateFormats = () => {
+ const dayFormat = createDateTimeFormat({ month: 'short', day: 'numeric' });
+ const monthFormat = createDateTimeFormat({ month: 'long' });
+ const yearFormat = createDateTimeFormat({ year: 'numeric' });
+
+ dateTimeFormats = {
+ dayFormat,
+ monthFormat,
+ yearFormat,
+ };
+};
+
+initDateFormats();
+
+/**
+ Formats a localized date in way that it can be used for d3.js axis.tickFormat().
+
+ That is, it displays
+ - 4-digit for first of January
+ - full month name for first of every month
+ - day and abbreviated month otherwise
+
+ see also https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickFormat
+ */
+export const dateTickFormat = (date) => {
+ if (date.getDate() !== 1) {
+ return dateTimeFormats.dayFormat.format(date);
+ }
+
+ if (date.getMonth() > 0) {
+ return dateTimeFormats.monthFormat.format(date);
+ }
+
+ return dateTimeFormats.yearFormat.format(date);
+};
diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js
index 1003b9ba0af..2f4328b56e1 100644
--- a/app/assets/javascripts/locale/index.js
+++ b/app/assets/javascripts/locale/index.js
@@ -1,8 +1,7 @@
import Jed from 'jed';
import sprintf from './sprintf';
-const langAttribute = document.querySelector('html').getAttribute('lang');
-const lang = (langAttribute || 'en').replace(/-/g, '_');
+const languageCode = () => document.querySelector('html').getAttribute('lang') || 'en';
const locale = new Jed(window.translations || {});
delete window.translations;
@@ -47,9 +46,19 @@ const pgettext = (keyOrContext, key) => {
return translated[translated.length - 1];
};
-export { lang };
+/**
+ Creates an instance of Intl.DateTimeFormat for the current locale.
+
+ @param formatOptions for available options, please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+ @returns {Intl.DateTimeFormat}
+*/
+const createDateTimeFormat =
+ formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions);
+
+export { languageCode };
export { gettext as __ };
export { ngettext as n__ };
export { pgettext as s__ };
export { sprintf };
+export { createDateTimeFormat };
export default locale;
diff --git a/changelogs/unreleased/winh-translate-contributors-page-dates.yml b/changelogs/unreleased/winh-translate-contributors-page-dates.yml
new file mode 100644
index 00000000000..74801bbd86e
--- /dev/null
+++ b/changelogs/unreleased/winh-translate-contributors-page-dates.yml
@@ -0,0 +1,5 @@
+---
+title: Translate date ranges on contributors page
+merge_request: 15846
+author:
+type: changed
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 43b996d9395..f493ad4ae66 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -262,6 +262,21 @@ Sometimes you need to add some context to the text that you want to translate
s__('OpenedNDaysAgo|Opened')
```
+### Dates / times
+
+- In JavaScript:
+
+```js
+import { createDateTimeFormat } from '.../locale';
+
+const dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' });
+console.log(dateFormat.format(new Date('2063-04-05'))) // April 5, 2063
+```
+
+This makes use of [`Intl.DateTimeFormat`].
+
+[`Intl.DateTimeFormat`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+
## Adding a new language
Let's suppose you want to add translations for a new language, let's say French.
diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js
new file mode 100644
index 00000000000..962423462e7
--- /dev/null
+++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js
@@ -0,0 +1,26 @@
+import ContributorsStatGraph from '~/graphs/stat_graph_contributors';
+import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph';
+
+import { setLanguage } from '../helpers/locale_helper';
+
+describe('ContributorsStatGraph', () => {
+ describe('change_date_header', () => {
+ beforeAll(() => {
+ setLanguage('de');
+ });
+
+ afterAll(() => {
+ setLanguage(null);
+ });
+
+ it('uses the locale to display date ranges', () => {
+ ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]);
+ setFixtures('<div id="date_header"></div>');
+ const graph = new ContributorsStatGraph();
+
+ graph.change_date_header();
+
+ expect(document.getElementById('date_header').innerText).toBe('31. Januar 2012 – 31. Januar 2013');
+ });
+ });
+});
diff --git a/spec/javascripts/helpers/locale_helper.js b/spec/javascripts/helpers/locale_helper.js
new file mode 100644
index 00000000000..99e6ce61234
--- /dev/null
+++ b/spec/javascripts/helpers/locale_helper.js
@@ -0,0 +1,11 @@
+/* eslint-disable import/prefer-default-export */
+
+export const setLanguage = (languageCode) => {
+ const htmlElement = document.querySelector('html');
+
+ if (languageCode) {
+ htmlElement.setAttribute('lang', languageCode);
+ } else {
+ htmlElement.removeAttribute('lang');
+ }
+};
diff --git a/spec/javascripts/lib/utils/tick_formats_spec.js b/spec/javascripts/lib/utils/tick_formats_spec.js
new file mode 100644
index 00000000000..283989b4fc8
--- /dev/null
+++ b/spec/javascripts/lib/utils/tick_formats_spec.js
@@ -0,0 +1,40 @@
+import { dateTickFormat, initDateFormats } from '~/lib/utils/tick_formats';
+
+import { setLanguage } from '../../helpers/locale_helper';
+
+describe('tick formats', () => {
+ describe('dateTickFormat', () => {
+ beforeAll(() => {
+ setLanguage('de');
+ initDateFormats();
+ });
+
+ afterAll(() => {
+ setLanguage(null);
+ });
+
+ it('returns year for first of January', () => {
+ const tick = dateTickFormat(new Date('2001-01-01'));
+
+ expect(tick).toBe('2001');
+ });
+
+ it('returns month for first of February', () => {
+ const tick = dateTickFormat(new Date('2001-02-01'));
+
+ expect(tick).toBe('Februar');
+ });
+
+ it('returns day and month for second of February', () => {
+ const tick = dateTickFormat(new Date('2001-02-02'));
+
+ expect(tick).toBe('2. Feb.');
+ });
+
+ it('ignores time', () => {
+ const tick = dateTickFormat(new Date('2001-02-02 12:34:56'));
+
+ expect(tick).toBe('2. Feb.');
+ });
+ });
+});
diff --git a/spec/javascripts/locale/index_spec.js b/spec/javascripts/locale/index_spec.js
new file mode 100644
index 00000000000..29b0b21eed7
--- /dev/null
+++ b/spec/javascripts/locale/index_spec.js
@@ -0,0 +1,35 @@
+import { createDateTimeFormat, languageCode } from '~/locale';
+
+import { setLanguage } from '../helpers/locale_helper';
+
+describe('locale', () => {
+ afterEach(() => {
+ setLanguage(null);
+ });
+
+ describe('languageCode', () => {
+ it('parses the lang attribute', () => {
+ setLanguage('ja');
+
+ expect(languageCode()).toBe('ja');
+ });
+
+ it('falls back to English', () => {
+ setLanguage(null);
+
+ expect(languageCode()).toBe('en');
+ });
+ });
+
+ describe('createDateTimeFormat', () => {
+ beforeEach(() => {
+ setLanguage('de');
+ });
+
+ it('creates an instance of Intl.DateTimeFormat', () => {
+ const dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' });
+
+ expect(dateFormat.format(new Date(2015, 6, 3))).toBe('3. Juli 2015');
+ });
+ });
+});