How to make localization on months / days for D3js

2019-01-11 17:37发布

问题:

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.
    }
});

回答1:

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;


回答2:

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);


回答3:

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.



回答4:

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.



回答5:

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);


回答6:

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



回答7:

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.



标签: d3.js