Skip to content

feat: add i18n/locale support to format() #278

@awbx

Description

@awbx

Summary

ms currently formats millisecond values using English-only unit strings. This proposal adds a locale option to format() so callers can receive output in other
languages without any change to parsing behavior.

Motivation

Libraries that surface human-readable durations (e.g. error messages, UI labels, CLI output) are often used in multilingual products. Today every consumer has to
post-process format() output to localize it. A first-class locale option removes that burden.

Proposed API

import { format, fr, de, ar, es, zh } from 'ms';

// short
format(60000, { locale: fr });             // '1min'
format(3600000, { locale: de });           // '1Std'

// long
format(1000, { locale: fr, long: true }); // '1 seconde'
format(2000, { locale: fr, long: true }); // '2 secondes'
format(3600000, { locale: zh, long: true }); // '1 小时'

Design

- New exported LocaleDefinition interface with shortUnits, longUnits (singular/plural tuples), and an optional isPlural callback.
- Options gains an optional locale?: LocaleDefinition field. Omitting it keeps the exact existing English behavior  fully backward compatible.
- Built-in locales: fr, ar, de, es, zh.
- parse() / parseStrict() remain English-only (locale-aware parsing would require per-language grammar rules that go beyond the scope of this package).
- Custom locales are trivially supported  callers just pass their own LocaleDefinition object.

Custom locale example

const pt: LocaleDefinition = {
  shortUnits: { ms: 'ms', s: 's', m: 'min', h: 'h', d: 'd', w: 'sem', mo: 'mês', y: 'ano' },
  longUnits: {
    millisecond: ['milissegundo', 'milissegundos'],
    second:      ['segundo',      'segundos'],
    minute:      ['minuto',       'minutos'],
    hour:        ['hora',         'horas'],
    day:         ['dia',          'dias'],
    week:        ['semana',       'semanas'],
    month:       ['mês',          'meses'],
    year:        ['ano',          'anos'],
  },
  isPlural: (v) => v !== 1,
};

format(2000, { locale: pt, long: true }); // '2 segundos'

Checklist

- Backward compatible  existing tests unchanged
- New LocaleDefinition interface exported
- Built-in locales: fr, ar, de, es, zh
- Full test coverage (format, parse, parseStrict) per locale
- README updated with usage and custom locale walkthrough
- 100% statement/branch/function/line coverage maintained

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions