<div dir="ltr"><div>Hi all,</div><div><br></div><div>I'm happy to announce that one of the more commonly requested features of the Intl/L10n API in Firefox has just landed.</div><div><br></div><div>Intl.RelativeTimeFormat allows us to format relative time as a string. It can produce locale aware phrases like "3 seconds ago", "in 2 hours", "last month", "today" and so on.</div><div><br></div><div>It was one of the three most highly requested features out of my team, and it took a long time to bake it, due to complexity described at the bottom of this post.<br></div><div><br></div><div>In line with our approach to all Intl APIs [0] this has been designed in cooperation with the ECMA402 Working Group and is currently at Stage 3 [1].</div><div><br></div><div>It also recently landed behind the flag in Firefox, and now got exposed via `Services.intl.RelativeTimeFormat` to our chrome code.</div><div><br></div><div>I'm in the process of documenting it, but in case you are working on any code that might benefit from it, here's a summary of how to use it:</div><div><br></div><div>The raw API is operating on numerical deltas and units. It doesn't perform any calculations for you, just formats a number and unit into a string:</div><div><br></div><div>```</div><div>let rtf = new Intl.RelativeTimeFormat(locales, {</div><div>  style: "long", // "narrow", "short", "long" (default)</div><div>  numeric: "always", // "auto", "always" (default)<br></div><div>});</div><div><br></div><div>rtf.format(5, "second"); // "in 5 seconds"<br></div><div><div>rtf.format(-5, "second"); // "5 seconds ago"<br></div></div><div><br></div><div><div>rtf.format(5, "month"); // "in 5 months"<br></div><div><div>rtf.format(-5, "month"); // "5 months ago"<br></div><div></div></div><br></div><div>```<br></div><div><br></div><div>There's one tricky part about this API that is counter intuitive and requires a bit of understanding of how to handle.</div><div><br></div><div>The `numeric` option distinguishes between always treating the value as number, and with trying to use language-specific overlays where available.</div><div>That's a difference between "1 day ago" and "yesterday", "in 0 seconds" and "now", or "1 year ago" and "last year".<br></div><div><br></div><div>It may sound counter-intuitive to use "1 day ago" when "yesterday" is available, but unfortunately, the latter may bite you back since it does require pretty non-trivial calculations.<br></div><div><br></div><div>While "1 day ago" is roughly 24 hours ago, with some approximations, "yesterday" means something very different at 1am and at 11pm, while "last year" means something very different on January 2nd and on December 29th.<br></div><div><br></div><div>But fear not, we do introduce also our custom, sophisticated algorithm for calculating that for you!</div><div><br></div><div>If you use Mozilla's extended Intl - mozIntl, which is exposed to chrome code as `Services.intl`, you are getting a wrapper over the standard `Intl.RTF` which uses `numeric: auto` by default and offers a new method `formatBestUnit`:</div><div><br></div><div>```</div><div>let rtf = new Services.intl.RelativeTimeFormat(locales);</div><div><br></div><div>rtf.formatBestUnit(new Date()); // "now"<br></div><div><div>rtf.formatBestUnit(new Date(Date.now() - 1000 * 60 * 60)); // "an hour ago"<br></div></div><div><br></div><div>```<br></div><div><br></div><div>This API takes a date and calculates a distance from a reference point to it. You can provide your own `now` reference point (useful at least for tests!):</div><div><br></div><div>```</div><div><br></div><div><div>let rtf = new Services.intl.RelativeTimeFormat(locales);</div><div>let now = new Date("2017-03-19 14:45");<br></div><div><br></div><div>rtf.formatBestUnit(new Date("2017-02-19 12:19"), { now }); // "last month"<br></div><div><div></div></div><br></div><div>```<br></div><div><br></div><div>You can read more about the concept of discrete relative time and why it is not trivial in the description of `relative-time` library by Rafael Xavier de Souza [2].<br></div><div><br></div><div>And you can find more examples of how our calculations work in our tests [3].<br></div><div><br></div><div>We're looking for feedback on the API so let me know if you'd like to use it!</div><div><br></div><div>zb.</div><div>p.s. The API ties nicely into Fluent and can work together with it using Fluent's Partial Arguments feature [4]<br></div><div><br></div><div>[0] <a href="https://diary.braniecki.net/2018/01/08/multilingual-gecko-in-2017/">https://diary.braniecki.net/2018/01/08/multilingual-gecko-in-2017/</a><br></div><div>[1] <a href="https://github.com/tc39/proposal-intl-relative-time">https://github.com/tc39/proposal-intl-relative-time</a></div><div>[2] <a href="https://github.com/rxaviers/relative-time#relative-time-1">https://github.com/rxaviers/relative-time#relative-time-1</a><br></div><div>[3] <a href="https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/mozintl/test/test_mozintl.js#l61">https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/mozintl/test/test_mozintl.js#l61</a></div><div>[4] <a href="https://firefox-source-docs.mozilla.org/intl/l10n/l10n/fluent_tutorial.html#partial-arguments">https://firefox-source-docs.mozilla.org/intl/l10n/l10n/fluent_tutorial.html#partial-arguments</a><br></div></div>