Repository: denoland/std (TypeScript)

Issue:
@std/fmt/duration singularize words when the value === 1
Currently the full unit words (seconds, minutes, hours, etc) are plural in all cases, even when the number is 1. It seems fairly simple to only pluralize when the number != 1, which would produce more idiomatic, grammatically correct English phrases ( "1 minute" vs "1 minutes"). Since I believe that's the point of the `style: "full"` setting, this seems like a worthwhile update.

Perhaps there's some complexity there I haven't considered, but I've created a quick PR to show how it might be done. Happy to discuss.


Discussion:
This would also be aligned with https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat



Patch:
diff --git a/fmt/duration.ts b/fmt/duration.ts
index d116340aed06..3cf878a4ca30 100644
--- a/fmt/duration.ts
+++ b/fmt/duration.ts
@@ -14,7 +14,7 @@
  *
  * assertEquals(format(99674, { ignoreZero: true }), "1m 39s 674ms");
  *
- * assertEquals(format(99674, { style: "full", ignoreZero: true }), "1 minutes, 39 seconds, 674 milliseconds");
+ * assertEquals(format(99674, { style: "full", ignoreZero: true }), "1 minute, 39 seconds, 674 milliseconds");
  * ```
  * @module
  */
@@ -34,15 +34,20 @@ interface DurationObject {
 }
 
 const keyList: Record<keyof DurationObject, string> = {
-  d: "days",
-  h: "hours",
-  m: "minutes",
-  s: "seconds",
-  ms: "milliseconds",
-  us: "microseconds",
-  ns: "nanoseconds",
+  d: "day",
+  h: "hour",
+  m: "minute",
+  s: "second",
+  ms: "millisecond",
+  us: "microsecond",
+  ns: "nanosecond",
 };
 
+/** Get key with pluralization */
+function getPluralizedKey(type: keyof DurationObject, value: number) {
+  return value === 1 ? keyList[type] : `${keyList[type]}s`;
+}
+
 /** Parse milliseconds into a duration. */
 function millisecondsToDurationObject(ms: number): DurationObject {
   // Duration cannot be negative
@@ -109,7 +114,7 @@ export interface FormatOptions {
  *
  * assertEquals(format(99674, { ignoreZero: true }), "1m 39s 674ms");
  *
- * assertEquals(format(99674, { style: "full", ignoreZero: true }), "1 minutes, 39 seconds, 674 milliseconds");
+ * assertEquals(format(99674, { style: "full", ignoreZero: true }), "1 minute, 39 seconds, 674 milliseconds");
  * ```
  *
  * @param ms The milliseconds value to format
@@ -146,12 +151,14 @@ export function format(
       if (ignoreZero) {
         return `${
           durationArr.filter((x) => x.value).map((x) =>
-            `${x.value} ${keyList[x.type]}`
+            `${x.value} ${getPluralizedKey(x.type, x.value)}`
           ).join(", ")
         }`;
       }
       return `${
-        durationArr.map((x) => `${x.value} ${keyList[x.type]}`).join(", ")
+        durationArr.map((x) =>
+          `${x.value} ${getPluralizedKey(x.type, x.value)}`
+        ).join(", ")
       }`;
     }
     case "digital": {


Test patch:
diff --git a/fmt/duration_test.ts b/fmt/duration_test.ts
index 27d0edaff142..988c9c8ea2fe 100644
--- a/fmt/duration_test.ts
+++ b/fmt/duration_test.ts
@@ -32,7 +32,7 @@ Deno.test({
   fn() {
     assertEquals(
       format(99674, { style: "full" }),
-      "0 days, 0 hours, 1 minutes, 39 seconds, 674 milliseconds, 0 microseconds, 0 nanoseconds",
+      "0 days, 0 hours, 1 minute, 39 seconds, 674 milliseconds, 0 microseconds, 0 nanoseconds",
     );
   },
 });
@@ -62,7 +62,7 @@ Deno.test({
   fn() {
     assertEquals(
       format(99674, { style: "full", ignoreZero: true }),
-      "1 minutes, 39 seconds, 674 milliseconds",
+      "1 minute, 39 seconds, 674 milliseconds",
     );
   },
 });