How to make localization on months / days for D3js? - d3.js

I am looking for a way to do localization on D3
I have found the values
d3_time_days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
d3_time_dayAbbreviations = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
d3_time_monthAbbreviations = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
inside D3 but as they are local/privat to D3 i (obviously) cant access them.
Any hints would be great, thanks :)
Update 1:
Seems only a recompile can do the magic - but can't be boughtered - so ended up hard coding the edits in the minified version - shame on me...
Update 2:
Will try to look into how to make D3 accept an "on the fly" localization setting like fx moment.js does it:
moment.lang('da', {
months : "Januar_Februar_Marts_April_Maj_Juni_Juli_August_September_Oktober_November_December".split("_"),
monthsShort : "Jan_Feb_Mar_Apr_Maj_Jun_Jul_Aug_Sep_Okt_Nov_Dec".split("_"),
weekdays : "Søndag_Mandag_Tirsdag_Onsdag_Torsdag_Fredag_Lørdag".split("_"),
weekdaysShort : "Søn_Man_Tir_Ons_Tor_Fre_Lør".split("_"),
weekdaysMin : "Sø_Ma_Ti_On_To_Fr_Lø".split("_"),
ordinal : '%d.',
week : {
dow : 1, // Monday is the first day of the week.
doy : 4 // The week that contains Jan 4th is the first week of the year.
}
});

I just had the same issue, and I found the way to fix it without recompiling d3.
https://github.com/mbostock/d3/wiki/Localization
The documentation mentions the function d3.locale, this one builds the functions used for formatting numbers and dates. So if you call it with your own localization rules, you're half way there.
var myFormatters = d3.locale({
"decimal": ".",
"thousands": ",",
"grouping": [3],
"currency": ["$", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%m/%d/%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
"shortDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
"months": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
"shortMonths": ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"]
});
The next step is to actually tell d3 to use this new formatting functions. So for using the new time formatter do this:
d3.time.format = myFormatters.timeFormat;

Just thought I'd add to #Adrian Salazar's answer as it took me a while to get it right.
If you're doing an axis with 'out-of-the-box' d3 time scale. You can set it to the locale with timeFormat.multi
var localeFormatter = d3.locale({
"decimal": ",",
"thousands": ".",
"grouping": [3],
"currency": ["€", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%d-%m-%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag"],
"shortDays": ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za"],
"months": ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
"shortMonths": ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"]
})
var tickFormat = localeFormatter.timeFormat.multi([
["%H:%M", function(d) { return d.getMinutes(); }],
["%H:%M", function(d) { return d.getHours(); }],
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
["%b %d", function(d) { return d.getDate() != 1; }],
["%B", function(d) { return d.getMonth(); }],
["%Y", function() { return true; }]
]);
axis.tickFormat(tickFormat);

You can create custom d3 time formatters and use moment.js for localization. For example, to create an axis with localized dates:
var x = d3.time.scale();
var myTimeFormatter = function(date) {
return moment(date).format("LL");
};
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(myTimeFormatter);
Then use moment.lang to add support for additional languages and to toggle current language. Refer to http://momentjs.com/docs/#/i18n for specifics.

From the documentation:
D3's implementation is fixed to a locale at compile time based on the $LOCALE environment variable.
In order to localise it to your environment, you need to recompile d3.js (and d3.min.js) on a machine with the locale settings you want. This will replace the strings you're seeing in the source.

With D3 >= 4 I use this to set de_DE as the default locale:
import { formatDefaultLocale } from 'd3-format';
import { timeFormat, timeFormatDefaultLocale } from 'd3-time-format';
import {
timeSecond,
timeMinute,
timeHour,
timeDay,
timeMonth,
timeWeek,
timeYear,
} from 'd3-time';
// set default locale
formatDefaultLocale({
"decimal": ",",
"thousands": ".",
"grouping": [3],
"currency": ["", "\u00a0€"]
});
// set default time locale
timeFormatDefaultLocale({
"dateTime": "%A, der %e. %B %Y, %X",
"date": "%d.%m.%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
"shortDays": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
"months": ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
"shortMonths": ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
});
let formatMillisecond = timeFormat(".%L");
let formatSecond = timeFormat(":%S");
let formatMinute = timeFormat("%I:%M");
let formatHour = timeFormat("%I %p");
let formatDay = timeFormat("%a %d");
let formatWeek = timeFormat("%b %d");
let formatMonth = timeFormat("%B");
let formatYear = timeFormat("%Y");
function tickFormat (date) {
function multiFormat (date) {
return (timeSecond(date) < date ? formatMillisecond
: timeMinute(date) < date ? formatSecond
: timeHour(date) < date ? formatMinute
: timeDay(date) < date ? formatHour
: timeMonth(date) < date ? (timeWeek(date) < date ? formatDay : formatWeek)
: timeYear(date) < date ? formatMonth
: formatYear)(date);
}
return multiFormat(date);
}
axis.tickFormat(tickFormat);

Multiple Files:
Recompiling the Lib and providing multiple files of 10.000 lines of codes, differing only at 5 lines... is this best practice?
Patch for on the fly changes:
As locale-string are private you can (or rather need to) patch D3.js.
Unfortunately any patched library complicates update processes.
An example of on-the-fly localization is shown at internationalization and localization at runtime
Official Commit:
Mike Bostock reacted quickly an there is a commit on locale-branch by him. It seems to be a first draft, but maybe it is helpful for you.
dsuess

I did this and it worked
var monthss = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];
var x = d3.time.scale()
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months)
.tickFormat(function(d,i) {
if(i<monthss.length){
return monthss[i];
}
return monthss[i-monthss.length]
});
The if's are because I draw two years. Hope that this helps.

Related

How to get country specific formatting rules?

I am currently building a workflow for vega lite API and want to parametrize the choice of a locale.
So I want the user to specify her country code by providing a IETF language tag like "us-En" or "fr-FR" and from that I want to obtain the country specific formatting rules (see below for the format I need).
E.g., I am looking for a function, which produces the following output for different country codes:
function getLocaleFormats(countryCode) {
if (countryCode === "de-DE") {
return {
"decimal": ",",
"thousands": ".",
"grouping": [3],
"currency": ["€", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%d.%m.%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
"shortDays": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
"months": ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
"shortMonths": ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
};
}
}
Any help would be greatly appreciated.
You are looking for the built-in Intl object:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/NumberFormat
On instances of these objects you can call resolvedOptions() to get detailed information about the formatting being used on that locale.
Example:
const nf = new Intl.NumberFormat('en-US');
const df = new Intl.DateTimeFormat('en-US');
const nfOptions = nf.resolvedOptions();
const dfOptions = df.resolvedOptions();
console.dir(nfOptions);
console.dir(dfOptions);
I found the references I need, they are in:
https://unpkg.com/d3-format#1/locale/${countryCode}.json
https://unpkg.com/d3-time-format#1/locale/${countryCode}.json
with country code being the IETF language tag like "us-En".
p.s. I noticed these formatting rules are specified in d3.js as vega lite is build upon d3.js, so I added the d3.js and vega lite tags to the question.

Daterangepicker dates are wrong

I am using this daterangepicker https://www.daterangepicker.com/ but somehow it displays wrong dates.
For example: Today it is the Sunday, 22th November 2020, but the daterangepicker says the 22th Novemeber 2020 is a Monday.
Example
and here is my configuration:
$('#singledaterange').daterangepicker({
"locale": {
"format": "D. MMM YYYY",
"separator": " - ",
"applyLabel": "OK",
"cancelLabel": "Abbrechen",
"fromLabel": "Von",
"toLabel": "Bis",
"weekLabel": "W",
"daysOfWeek": [
"Mo",
"Di",
"Mi",
"Do",
"Fr",
"Sa",
"So"
],
"monthNames": [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
],
"firstDay": 0
},
"minDate": moment(),
});
I solved it. It was obviously my fault.
By default, Sunday is the first day of the week, so I had to put Sunday first in the daysOfWeek array, not Monday.

How to update nested child nodes using D3 v4's update pattern

I'm having trouble getting the certain descendants to update their data when using the D3 v4 update pattern.
What is the proper way to get changes in a data-join to propagate to children?
Here is an example
https://bl.ocks.org/yanofsky/5b30b12582b6b66bc262b165806a6dc5
When you click the buttons, only the text changes rather than both the text and the colors of the rects.
You should follow the pattern of adding data for every element you add. In this way you always have a update, enter and exit selections. After removing the exit and adding new elements for enter, you can merge update and enter and update the attributes:
Here is the updated block
//define some data
var data = [
{"location": 1, "month": "Jan", "year": 2017, "value": "#ccc"},
{"location": 2, "month": "Jan", "year": 2017, "value": "#999"},
{"location": 3, "month": "Jan", "year": 2017, "value": "#666"},
{"location": 4, "month": "Jan", "year": 2017, "value": "#333"},
{"location": 1, "month": "Jan", "year": 2018, "value": "#fcc"},
{"location": 2, "month": "Jan", "year": 2018, "value": "#f99"},
{"location": 3, "month": "Jan", "year": 2018, "value": "#f66"},
{"location": 4, "month": "Jan", "year": 2018, "value": "#f33"},
{"location": 1, "month": "Feb", "year": 2017, "value": "#cfc"},
{"location": 2, "month": "Feb", "year": 2017, "value": "#9f9"},
{"location": 3, "month": "Feb", "year": 2017, "value": "#6f6"},
{"location": 4, "month": "Feb", "year": 2017, "value": "#3f3"},
{"location": 1, "month": "Feb", "year": 2018, "value": "#ccf"},
{"location": 2, "month": "Feb", "year": 2018, "value": "#99f"},
{"location": 3, "month": "Feb", "year": 2018, "value": "#66f"},
{"location": 4, "month": "Feb", "year": 2018, "value": "#33f"},
{"location": 5, "month": "Feb", "year": 2018, "value": "#0505ff"},
]
// nest the data by month then year
var by_month = d3.nest()
.key(function(d){return d.month})
.key(function(d){return d.year})
.entries(data)
function render(ident, key_month) {
var w = 500
var h = 100
var container = d3.select(ident)
// select and add a container div for each year in the data
// using only the data for the target month
var year = container
.selectAll("div.year")
var data = by_month.filter(function(d){return d.key == key_month})[0].values;
var yearAll= year.data(data);
yearAll.exit().remove();
var yearEnter= yearAll
.enter()
.append("div")
.classed("year",true);
//Add h2 and svg
yearEnter.append("h2");
yearEnter.append("svg")
.append("g")
.classed("gwrapper",true);
yearEnter = yearEnter.merge(yearAll);
yearEnter.select("h2")
.text(function(d) {return d.values[0].month + " " + d.values[0].year ;});
// update the svg dimensions
yearEnter.select("svg")
.attr("width", w)
.attr("height", h);
// select and add element g location
var gloc = yearEnter.select(".gwrapper").selectAll("g")
.data(function(d){return d.values;});
gloc.exit().remove();
var glocEnter = gloc.enter()
.append("g")
.classed("loc",true);
glocEnter.append("rect");
;
glocEnter = glocEnter.merge(gloc);
// merge and position element wrappers
glocEnter
.attr("transform", function(d,i){return "translate("+[w/5*i]+")"});
var t = d3.transition().duration(1000);
glocEnter.select("rect")
.attr("x", (w/5 - 5)/2)
.attr("y", (w/5 - 5)/2)
.attr("width", 0)
.attr("height", 0)
.attr("fill", "black")
.transition(t)
.attr("x", 5)
.attr("y", 0)
.attr("width", w/5 - 5)
.attr("height", w/5 - 5)
.attr("fill", function(d){return d.value});
}
// on button click change the subset of data being used
d3.selectAll(".month").on("click", function(){
render("#toggle",this.getAttribute("data-month"))
})
d3.selectAll("#clearb").on("click", function(){
d3.selectAll("g.loc").remove();
})
render("#toggle","Jan")

Formatting metrics in Superset / Caravel pivot table

My Superset / Caravel pivot table slice is displaying the metrics (sum of revenue in this case) in scientific notation.
All 4.136578e+08 355190412.0 5.375492e+07 8.226031e+08
How do I display it nicely with thousands separator?
I cant find any options to specify Number Format for Pivot Table visualization type. In contrast, when I create slice using Big Number type, there is option to specify Number Format where it accepts D3 number format. I cant find this for Pivot Table.
you can change it by :-
select the table .
edit the record .
list the sql metrics.
then choose the metric
you can fill the D3 format with +, | +12,345.4321
You may changing number in D3 format, here's some guideline :
{
"decimal": ".",
"thousands": ",",
"grouping": [3],
"currency": ["$", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%m/%d/%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
"shortDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
"months": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
"shortMonths": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
}
It can be defined in your dataset edit panel,
just like this:
then it can be displayed as you designed.

tickformat for d3.js time scale

I am using a d3.js time.scale like this:
var xScale = d3.time.scale()
.domain(d3.extent(data, function(d) { return d.date; }))
.range([0, chartWidth]);
The data is like this:
var testData = [
{
"date": "2015-04-01T00:00:00.000Z",
"value": 200.00
},
{
"date": "2015-05-01T00:00:00.000Z",
"value": 2000.00
},
{
"date": "2015-06-01T00:00:00.000Z",
"value": 4000.01
},
{
"date": "2015-07-01T00:00:00.000Z",
"value": 1000.00
},
{
"date": "2015-08-01T00:00:00.000Z",
"value": 750.00
},
{
"date": "2015-09-01T00:00:00.000Z",
"value": 1568.00
},
{
"date": "2015-10-01T00:00:00.000Z",
"value": 3789.00
},
{
"date": "2015-11-01T00:00:00.000Z",
"value": 5678.00
},
{
"date": "2015-12-01T00:00:00.000Z",
"value": 4898.00
},
{
"date": "2016-01-01T00:00:00.000Z",
"value": 9002.00
},
{
"date": "2016-02-01T00:00:00.000Z",
"value": 3320.00
},
{
"date": "2016-03-01T00:00:00.000Z",
"value": 12000.00
}
];
But the ticks and the ordering are not as I would expect:
I would expect the ticks to be ordered from april 2015 to march 2016 and I do not understand where the 2016 tick is coming from.
How can I order the ticks as I would expect and also where is the 2016 tick coming from?
Here is a jsbin that shows the problem
If you want the ticks to show the month name and the year, add this to the axis:
.tickFormat(d3.time.format("%B %Y"));
Alternatively, you can display the abbreviated month name:
.tickFormat(d3.time.format("%b %Y"));
Here is the updated jsbin: http://jsbin.com/wikafuluqu/1/edit?js,output
PS: To show the first tick, add nice() to the scale. This is the jsbin with nice():
http://jsbin.com/loyupelazu/1/edit?js,output

Resources