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
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:
English
German
French
Spanish
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