@jsenv/humanize 1.7.7 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/dist/browser/jsenv_humanize_browser.js +1960 -42
- package/dist/browser/jsenv_humanize_browser.js.map +19 -4
- package/dist/node/jsenv_humanize_node.js +1959 -42
- package/dist/node/jsenv_humanize_node.js.map +21 -7
- package/dist/node/jsenv_humanize_node_modules.js.map +13 -6
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -48,3 +48,116 @@ import { humanizeDuration } from "@jsenv/humanize";
|
|
|
48
48
|
|
|
49
49
|
humanizeDuration(61_421); // "1 minute and 1 second"
|
|
50
50
|
```
|
|
51
|
+
|
|
52
|
+
## format\* — text for the reader of an app
|
|
53
|
+
|
|
54
|
+
Everything above writes English for a developer reading a terminal. The
|
|
55
|
+
`format*` family writes for the person using the app, in their language,
|
|
56
|
+
through `Intl`.
|
|
57
|
+
|
|
58
|
+
| what you are writing | reach for |
|
|
59
|
+
| --------------------------------------------- | -------------------------------------------------- |
|
|
60
|
+
| a message in a terminal, a log, a test report | `humanize`, `humanizeDuration`, `humanizeFileSize` |
|
|
61
|
+
| a date, a delay or a number someone will read | `format*` |
|
|
62
|
+
|
|
63
|
+
It lives in this package, which has no frontend of its own, so that a server
|
|
64
|
+
and a browser word the same instant identically: these are the functions
|
|
65
|
+
behind `@jsenv/navi`'s `<Time>` components, so a notification row written by a
|
|
66
|
+
backend and the card it points at read the same date the same way — and a REST
|
|
67
|
+
service does not install a frontend framework to print a month name. Nothing
|
|
68
|
+
here touches the DOM.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import {
|
|
72
|
+
formatDatetime,
|
|
73
|
+
formatDay,
|
|
74
|
+
formatDuration,
|
|
75
|
+
formatHourDuration,
|
|
76
|
+
formatMinuteDuration,
|
|
77
|
+
formatMonth,
|
|
78
|
+
formatNumber,
|
|
79
|
+
formatSecondDuration,
|
|
80
|
+
formatTimeOfDay,
|
|
81
|
+
formatTimeRange,
|
|
82
|
+
formatTimeRelative,
|
|
83
|
+
} from "@jsenv/humanize";
|
|
84
|
+
|
|
85
|
+
const start = new Date("2026-05-11T14:05:00");
|
|
86
|
+
const end = new Date("2026-05-11T16:00:00");
|
|
87
|
+
const lang = "fr";
|
|
88
|
+
|
|
89
|
+
// a date
|
|
90
|
+
formatDay(start, { lang }); // "lundi 11 mai"
|
|
91
|
+
formatDay(start, { lang, format: "numeric" }); // "11/05/2026"
|
|
92
|
+
formatMonth(start, { lang }); // "mai 2026"
|
|
93
|
+
formatDatetime(start, { lang }); // "lun. 11 mai à 14:05"
|
|
94
|
+
|
|
95
|
+
// a time of day, a span
|
|
96
|
+
formatTimeOfDay(start, { lang, format: "compact" }); // "14h05"
|
|
97
|
+
formatTimeRange(start, end, { lang, format: "compact" }); // "14h05–16h00"
|
|
98
|
+
|
|
99
|
+
// a duration, from the unit you happen to hold
|
|
100
|
+
formatMinuteDuration(90, { lang, format: "compact" }); // "1h30"
|
|
101
|
+
formatHourDuration(2.5, { lang }); // "2 heures et 30 minutes"
|
|
102
|
+
formatSecondDuration(90, { lang }); // "1 minute et 30 secondes"
|
|
103
|
+
formatDuration("PT1H30M", { lang }); // "1 heure et 30 minutes"
|
|
104
|
+
|
|
105
|
+
// a moment, relative to now (second argument is how long it lasts)
|
|
106
|
+
formatTimeRelative(start, 30 * 60_000, { lang });
|
|
107
|
+
// "dans 1 heure et 30 minutes", "En cours" or "il y a 2 heures"
|
|
108
|
+
|
|
109
|
+
// a number
|
|
110
|
+
formatNumber(1_234_567.5, { lang }); // "1 234 567,5"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Each takes more than `lang` — how a part is spelled, precision, padding, time
|
|
114
|
+
zone, whether the year is written. Those options live in the function's own
|
|
115
|
+
JSDoc, which the published bundle keeps: hover the import in an editor rather
|
|
116
|
+
than looking for a page listing them.
|
|
117
|
+
|
|
118
|
+
Two more, for a date field with nothing in it yet: `formatDatePlaceholder()`
|
|
119
|
+
writes `"jj/mm/aaaa"`, and `formatMonthPlaceholder()`, `formatWeekPlaceholder()`
|
|
120
|
+
and `formatDatetimePlaceholder()` do the same for their own shape.
|
|
121
|
+
|
|
122
|
+
### Which language
|
|
123
|
+
|
|
124
|
+
With no `lang`, a call uses the runtime language source: by default the
|
|
125
|
+
runtime's own locale, exactly what `Intl` would pick on its own. A frontend
|
|
126
|
+
replaces that source once, with a live one:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
import { setRuntimeLangSource } from "@jsenv/humanize";
|
|
130
|
+
|
|
131
|
+
setRuntimeLangSource(() => languagesSignal.value);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`@jsenv/navi` already does this, pointing it at the user's language
|
|
135
|
+
preference; because the source is read on every call, a component formatting a
|
|
136
|
+
date re-renders when that preference changes.
|
|
137
|
+
|
|
138
|
+
On a server the default is the process locale, which is nobody's in
|
|
139
|
+
particular — **pass `lang` explicitly whenever the text is for someone else**,
|
|
140
|
+
and `timeZone` too when the instant must be worded in the reader's zone rather
|
|
141
|
+
than the machine's.
|
|
142
|
+
|
|
143
|
+
### Changing a word
|
|
144
|
+
|
|
145
|
+
The words around the numbers — `"time.ongoing"`, the compact unit symbols, the
|
|
146
|
+
placeholder tokens — live in `humanizeI18n`, and every one of them can be
|
|
147
|
+
replaced or translated into a language that is not shipped:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import { humanizeI18n } from "@jsenv/humanize";
|
|
151
|
+
|
|
152
|
+
humanizeI18n.add("time.ongoing", { fr: "En cours…" });
|
|
153
|
+
humanizeI18n.addLangKeys("ja", { "time.midnight": "真夜中" });
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`src/i18n/humanize_i18n.js` is the exhaustive list of keys and defaults, and is
|
|
157
|
+
meant to be read. It is the same registry `@jsenv/navi` exposes as `naviI18n`,
|
|
158
|
+
so one call reaches both.
|
|
159
|
+
|
|
160
|
+
`createI18n()` (an app's own text registry) and `interpolateText()` (one
|
|
161
|
+
sentence with values in it) are exported here too; `@jsenv/navi`'s
|
|
162
|
+
[docs/i18n.md](../../frontend/navi/docs/i18n.md) explains when to use which,
|
|
163
|
+
and why a library's keys are opaque while an app's are not.
|