diff --git a/src/Roux/Html.hs b/src/Roux/Html.hs
index 7b03721..ffaaa67 100644
--- a/src/Roux/Html.hs
+++ b/src/Roux/Html.hs
@@ -52,16 +52,66 @@ pairWith _ Nothing = Nothing
pairWith a (Just b) = Just (a, b)
-- | Format a quantity for display.
+-- | Format a quantity for natural display (e.g. @4 cups@, @1/2 tsp@).
showQuantity :: Quantity -> Text
showQuantity q =
let amt = quantityAmount q
amount = case denominator amt of
1 -> T.pack (show (numerator amt))
- _ -> T.pack (show (toDouble amt))
+ _ -> formatFraction amt
prefix = if quantityFixed q then "=" else ""
- unit = maybe "" ("%" <>) (quantityUnit q)
+ unit = maybe "" (\u -> " " <> pluralize amt u) (quantityUnit q)
in prefix <> amount <> unit
+-- | Format a rational as a nice fraction string (e.g. @1/2@, @1 1/2@).
+formatFraction :: Rational -> Text
+formatFraction r =
+ let w = numerator r `Prelude.div` denominator r
+ rem = r - toRational w
+ in case w of
+ 0 -> simpleFrac rem
+ _ -> T.pack (show w) <> " " <> simpleFrac rem
+ where
+ simpleFrac f =
+ let n = numerator f
+ d = denominator f
+ in T.pack (show n <> "/" <> show d)
+
+-- | Pluralize a unit name based on the amount.
+pluralize :: Rational -> Text -> Text
+pluralize amt unit
+ | amt == 1 = unit
+ | otherwise = case T.toLower unit of
+ -- Units that stay the same (abbreviations, mass, volume)
+ "g" -> "g"
+ "kg" -> "kg"
+ "ml" -> "ml"
+ "l" -> "l"
+ "oz" -> "oz"
+ "lb" -> "lbs"
+ "tbsp" -> "tbsp"
+ "tsp" -> "tsp"
+ "cup" -> "cups"
+ "tablespoon" -> "tablespoons"
+ "teaspoon" -> "teaspoons"
+ "ounce" -> "ounces"
+ "pound" -> "pounds"
+ "pinch" -> "pinches"
+ "clove" -> "cloves"
+ "item" -> "items"
+ "piece" -> "pieces"
+ "can" -> "cans"
+ "package" -> "packages"
+ "bag" -> "bags"
+ "bunch" -> "bunches"
+ "sprig" -> "sprigs"
+ "leaf" -> "leaves"
+ "slice" -> "slices"
+ "minute" -> "minutes"
+ "hour" -> "hours"
+ "second" -> "seconds"
+ _ -> unit
+
-- ---------------------------------------------------------------------------
-- Page shell
-- ---------------------------------------------------------------------------