Skip to main content

Overview

Enhanced MS includes a comprehensive localization system that allows you to format and parse durations in multiple languages. Each language has its own unit names, abbreviations, pluralization rules, and parsing patterns.

Supported languages

Enhanced MS currently supports 11 languages out of the box (~/workspace/source/src/languages/index.ts:29):
  • cs - Czech
  • de - German
  • en - English (default)
  • es - Spanish
  • fr - French
  • it - Italian
  • mi - Māori
  • nl - Dutch
  • pl - Polish
  • ru - Russian
  • zh-CN - Chinese (Simplified)

Using a language

You can specify a language when creating an ms instance:
import { createMs } from 'enhanced-ms';

// German
const msDE = createMs({ language: 'de' });
msDE(3661000);
// "1 Stunde 1 Minute 1 Sekunde"

// French
const msFR = createMs({ language: 'fr' });
msFR(3661000);
// "1 heure 1 minute 1 seconde"

// Russian
const msRU = createMs({ language: 'ru' });
msRU(3661000);
// "1 час 1 минута 1 секунда"
Or use getLanguage() with the lower-level functions:
import { formatMilliseconds, parseDuration, getLanguage } from 'enhanced-ms';

const german = getLanguage('de');

formatMilliseconds(3661000, german);
// "1 Stunde 1 Minute 1 Sekunde"

parseDuration("2 stunden 30 minuten", german);
// 9000000

Language structure

Each language is defined using a LanguageDefinition object (~/workspace/source/src/languages/helpers/definition-types.ts:3):
export interface LanguageDefinition {
  decimal: '.' | ',';
  and?: string | ((words: string[]) => string[]);
  units: Record<keyof typeof units, LanguageUnitDefinition>;
}

Example: English

Here’s a snippet from the English language definition (~/workspace/source/src/languages/en.ts:1):
export default {
  decimal: '.',
  and: 'and',

  units: {
    second: {
      name: (c) => (c === 1 ? 'second' : 'seconds'),
      abbreviation: 's',
      matches: ['second', 'seconds', 's'],
    },
    minute: {
      name: (c) => (c === 1 ? 'minute' : 'minutes'),
      abbreviation: 'm',
      matches: ['minute', 'minutes', 'm'],
    },
    hour: {
      name: (c) => (c === 1 ? 'hour' : 'hours'),
      abbreviation: 'h',
      matches: ['hour', 'hours', 'h'],
    },
    // ... more units
  },
};

Example: German

German uses different pluralization and a comma as decimal separator (~/workspace/source/src/languages/de.ts:3):
export default {
  decimal: ',',
  and: 'und',

  units: {
    second: {
      name: (c) => (c === 1 ? 'Sekunde' : 'Sekunden'),
      abbreviation: 'Sek.',
      matches: ['s', 'sekunde', 'sekunden', 'sek', 'seks'],
    },
    minute: {
      name: (c) => (c === 1 ? 'Minute' : 'Minuten'),
      abbreviation: 'Min.',
      matches: ['m', 'minute', 'minuten', 'min', 'mins'],
    },
    hour: {
      name: (c) => (c === 1 ? 'Stunde' : 'Stunden'),
      abbreviation: 'Std.',
      matches: ['h', 'stunde', 'stunden', 'st'],
    },
    // ... more units
  },
};

Language components

Decimal separator

Defines how decimal numbers are written:
// English (uses '.')
ms("1.5 hours"); // 5400000

// German (uses ',')
const msDE = createMs({ language: 'de' });
msDE("1,5 stunden"); // 5400000

Unit definitions

Each unit has three components:

1. Name

The full unit name, with pluralization support:
// Function form (for languages with pluralization)
name: (c) => (c === 1 ? 'hour' : 'hours')

// String form (for invariant languages)
name: 'hour'

2. Abbreviation

Short form used when useAbbreviations: true:
abbreviation: 'h'  // English
abbreviation: 'Std.'  // German
Some languages might not provide abbreviations for all units. If you try to use useAbbreviations: true with a language that doesn’t support it, you’ll get an error.

3. Matches

All variations accepted when parsing:
matches: ['hour', 'hours', 'h']  // English
matches: ['h', 'stunde', 'stunden', 'st']  // German
This allows flexible parsing:
// All of these work:
ms("1 hour");
ms("2 hours");
ms("1h");
// All return appropriate millisecond values

Pluralization

Languages handle pluralization differently:

Simple pluralization (English)

name: (c) => (c === 1 ? 'second' : 'seconds')
ms(1000); // "1 second"
ms(2000); // "2 seconds"

Complex pluralization (Russian)

Some languages like Russian have more complex rules:
// Simplified example
name: (c) => {
  if (c === 1) return 'секунда';
  if (c < 5) return 'секунды';
  return 'секунд';
}

No pluralization

Some languages don’t change unit names:
// Invariant form
name: 'second'

Creating custom languages

You can define your own language by implementing the LanguageDefinition interface:
import { createMs } from 'enhanced-ms';
import type { LanguageDefinition } from 'enhanced-ms';

const myLanguage: LanguageDefinition = {
  decimal: '.',
  and: 'and',
  
  units: {
    nanosecond: {
      name: (c) => c === 1 ? 'nanosecond' : 'nanoseconds',
      abbreviation: 'ns',
      matches: ['ns', 'nanosecond', 'nanoseconds'],
    },
    microsecond: {
      name: (c) => c === 1 ? 'microsecond' : 'microseconds',
      abbreviation: 'μs',
      matches: ['μs', 'microsecond', 'microseconds'],
    },
    // ... define all required units
    millisecond: { /* ... */ },
    second: { /* ... */ },
    minute: { /* ... */ },
    hour: { /* ... */ },
    day: { /* ... */ },
    week: { /* ... */ },
    month: { /* ... */ },
    year: { /* ... */ },
    decade: { /* ... */ },
    century: { /* ... */ },
    millennium: { /* ... */ },
  },
};

const ms = createMs({ language: myLanguage });
You must define all 13 time units in your custom language: nanosecond, microsecond, millisecond, second, minute, hour, day, week, month, year, decade, century, and millennium.

How languages are processed

When you use getLanguage(), the library processes the definition (~/workspace/source/src/languages/helpers/make-language.ts:20):

1. Build the matcher regex

A regular expression is constructed from all unit matches:
const matcherRegex = new RegExp(
  // Don't match separators alone
  `(?![${decimalSeparator}${thousandSeparator}])` +
  // Match numbers
  `[\\d${decimalSeparator}${thousandSeparator}]+|` +
  // Match units (sorted by length, longest first)
  '(?<=\\s|\\d)((?:-)?(' +
  Object.values(languageDefinition.units)
    .flatMap(({ matches }) => matches)
    .sort((a, b) => b.length - a.length)
    .join('|') +
  '))',
  'gi',
);

2. Create time units map

All unit variations are mapped to their definitions:
const timeUnits = {
  'hour': { name: ..., abbreviation: ..., ms: 3600000 },
  'hours': { name: ..., abbreviation: ..., ms: 3600000 },
  'h': { name: ..., abbreviation: ..., ms: 3600000 },
  // ... all variations point to the same definition
};

3. Cache the result

Processed languages are cached for performance:
const languageCache = new Map<string, Language>();
This means you can call getLanguage('en') multiple times without performance penalties.

Language caching

The library automatically caches processed languages (~/workspace/source/src/languages/helpers/make-language.ts:60):
export function getLanguage(
  localeOrLanguageDefinition: keyof typeof languages | LanguageDefinition,
) {
  const languageDefinition =
    typeof localeOrLanguageDefinition === 'string'
      ? languages[localeOrLanguageDefinition]
      : localeOrLanguageDefinition;
  
  const key = JSON.stringify(languageDefinition);
  if (languageCache.has(key)) return languageCache.get(key)!;
  
  const language = makeLanguage(languageDefinition);
  languageCache.set(key, language);
  return language;
}
This provides:
  • Fast lookups for repeated language requests
  • Efficient regex and map construction
  • Support for custom language objects

Formatting across languages

Compare how the same duration appears in different languages:
const duration = 90061000; // 1 day, 1 hour, 1 minute, 1 second

createMs({ language: 'en' })(duration);
// "1 day 1 hour 1 minute 1 second"

createMs({ language: 'de' })(duration);
// "1 Tag 1 Stunde 1 Minute 1 Sekunde"

createMs({ language: 'fr' })(duration);
// "1 jour 1 heure 1 minute 1 seconde"

createMs({ language: 'es' })(duration);
// "1 día 1 hora 1 minuto 1 segundo"

createMs({ language: 'ru' })(duration);
// "1 день 1 час 1 минута 1 секунда"

Parsing across languages

The same string can be parsed in different languages:
const ms = createMs({ language: 'en' });
ms("2 hours 30 minutes");
// 9000000

Best practices

Choose the right language for your users

Set the language based on user preferences:
const userLocale = navigator.language; // e.g., 'de-DE'
const language = userLocale.startsWith('de') ? 'de' : 'en';
const ms = createMs({ language });

Reuse language instances

Create the ms instance once and reuse it:
// ✓ Good - create once
const ms = createMs({ language: 'de' });
ms(1000);
ms(2000);

// ✗ Avoid - creating repeatedly
createms({ language: 'de' })(1000);
createMs({ language: 'de' })(2000);

Test with multiple languages

If your app is international, test with various languages:
const languages = ['en', 'de', 'fr', 'es'] as const;
for (const lang of languages) {
  const ms = createMs({ language: lang });
  console.log(lang, ms(3661000));
}
  • Formatting - Learn about format options that work with all languages
  • Parsing - Understand how language affects parsing behavior

Build docs developers (and LLMs) love