Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tricky-streets-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"enhanced-ms": minor
---

Added language support for Dutch, French, Italian and Spanish
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ The currently supported languages include:
| German | de |
| Russian | ru |
| Māori | mi |
| Spanish | es |
| Dutch | nl |
| Italian | it |
| French | fr |

You can help by adding support for more languages.

Expand Down
72 changes: 72 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export default {
decimal: ',',
and: 'y',

units: {
nanosecond: {
name: (c) => (c === 1 ? 'nanosegundo' : 'nanosegundos'),
abbreviation: 'ns',
matches: ['nanosegundo', 'nanosegundos', 'ns'],
},
microsecond: {
name: (c) => (c === 1 ? 'microsegundo' : 'microsegundos'),
abbreviation: 'μs',
matches: ['microsegundo', 'microsegundos', 'μs'],
},
millisecond: {
name: (c) => (c === 1 ? 'milisegundo' : 'milisegundos'),
abbreviation: 'ms',
matches: ['milisegundo', 'milisegundos', 'ms'],
},
second: {
name: (c) => (c === 1 ? 'segundo' : 'segundos'),
abbreviation: 's',
matches: ['segundo', 'segundos', 's'],
},
minute: {
name: (c) => (c === 1 ? 'minuto' : 'minutos'),
abbreviation: 'min',
matches: ['minuto', 'minutos', 'min'],
},
hour: {
name: (c) => (c === 1 ? 'hora' : 'horas'),
abbreviation: 'h',
matches: ['hora', 'horas', 'h'],
},
day: {
name: (c) => (c === 1 ? 'día' : 'días'),
abbreviation: 'd',
matches: ['día', 'días', 'd'],
},
week: {
name: (c) => (c === 1 ? 'semana' : 'semanas'),
abbreviation: 'sem',
matches: ['semana', 'semanas', 'sem'],
},
month: {
name: (c) => (c === 1 ? 'mes' : 'meses'),
abbreviation: 'mes',
matches: ['mes', 'meses', 'mes'],
},
year: {
name: (c) => (c === 1 ? 'año' : 'años'),
abbreviation: 'a',
matches: ['año', 'años', 'a'],
},
decade: {
name: (c) => (c === 1 ? 'década' : 'décadas'),
abbreviation: 'déc',
matches: ['década', 'décadas', 'déc'],
},
century: {
name: (c) => (c === 1 ? 'siglo' : 'siglos'),
abbreviation: 'sig',
matches: ['siglo', 'siglos', 'sig'],
},
millennium: {
name: (c) => (c === 1 ? 'milenio' : 'milenios'),
abbreviation: 'mil',
matches: ['milenio', 'milenios', 'mil'],
},
},
} satisfies import('./helpers/definition-types').LanguageDefinition;
72 changes: 72 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export default {
decimal: ',',
and: 'et',

units: {
nanosecond: {
name: (c) => (c === 1 ? 'nanoseconde' : 'nanosecondes'),
abbreviation: 'ns',
matches: ['nanoseconde', 'nanosecondes', 'ns'],
},
microsecond: {
name: (c) => (c === 1 ? 'microseconde' : 'microsecondes'),
abbreviation: 'μs',
matches: ['microseconde', 'microsecondes', 'μs'],
},
millisecond: {
name: (c) => (c === 1 ? 'milliseconde' : 'millisecondes'),
abbreviation: 'ms',
matches: ['milliseconde', 'millisecondes', 'ms'],
},
second: {
name: (c) => (c === 1 ? 'seconde' : 'secondes'),
abbreviation: 's',
matches: ['seconde', 'secondes', 's'],
},
minute: {
name: (c) => (c === 1 ? 'minute' : 'minutes'),
abbreviation: 'min',
matches: ['minute', 'minutes', 'min'],
},
hour: {
name: (c) => (c === 1 ? 'heure' : 'heures'),
abbreviation: 'h',
matches: ['heure', 'heures', 'h'],
},
day: {
name: (c) => (c === 1 ? 'jour' : 'jours'),
abbreviation: 'j',
matches: ['jour', 'jours', 'j'],
},
week: {
name: (c) => (c === 1 ? 'semaine' : 'semaines'),
abbreviation: 'sem',
matches: ['semaine', 'semaines', 'sem'],
},
month: {
name: (_c) => 'mois', // In French, the singular and plural form is identical.
abbreviation: 'mo',
matches: ['mois', 'mo'],
},
year: {
name: (c) => (c === 1 ? 'an' : 'ans'),
abbreviation: 'an',
matches: ['an', 'ans'],
},
decade: {
name: (c) => (c === 1 ? 'décennie' : 'décennies'),
abbreviation: 'dec',
matches: ['décennie', 'décennies', 'dec'],
},
century: {
name: (c) => (c === 1 ? 'siècle' : 'siècles'),
abbreviation: 'siè',
matches: ['siècle', 'siècles', 'siè'],
},
millennium: {
name: (c) => (c === 1 ? 'millénaire' : 'millénaires'),
abbreviation: 'mil',
matches: ['millénaire', 'millénaires', 'mil'],
},
},
} satisfies import('./helpers/definition-types').LanguageDefinition;
8 changes: 6 additions & 2 deletions src/languages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import type { LanguageDefinition } from './helpers/definition-types';

import de from './de';
import en from './en';
import es from './es';
import fr from './fr';
import it from './it';
import mi from './mi';
import nl from './nl';
import ru from './ru';

// This prevents the whole language definition being included in the dts output
type Locale = 'de' | 'en' | 'mi' | 'ru';
type Locale = 'de' | 'en' | 'es' | 'fr' | 'it' | 'mi' | 'nl' | 'ru';
type Languages = Record<Locale, LanguageDefinition>;
export const languages: Languages = { de, en, mi, ru };
export const languages: Languages = { de, en, es, fr, it, mi, nl, ru };
72 changes: 72 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export default {
decimal: ',',
and: 'e',

units: {
nanosecond: {
name: (c) => (c === 1 ? 'nanosecondo' : 'nanosecondi'),
abbreviation: 'ns',
matches: ['nanosecondo', 'nanosecondi', 'ns'],
},
microsecond: {
name: (c) => (c === 1 ? 'microsecondo' : 'microsecondi'),
abbreviation: 'μs',
matches: ['microsecondo', 'microsecondi', 'μs'],
},
millisecond: {
name: (c) => (c === 1 ? 'millisecondo' : 'millisecondi'),
abbreviation: 'ms',
matches: ['millisecondo', 'millisecondi', 'ms'],
},
second: {
name: (c) => (c === 1 ? 'secondo' : 'secondi'),
abbreviation: 's',
matches: ['secondo', 'secondi', 's'],
},
minute: {
name: (c) => (c === 1 ? 'minuto' : 'minuti'),
abbreviation: 'min',
matches: ['minuto', 'minuti', 'min'],
},
hour: {
name: (c) => (c === 1 ? 'ora' : 'ore'),
abbreviation: 'h',
matches: ['ora', 'ore', 'h'],
},
day: {
name: (c) => (c === 1 ? 'giorno' : 'giorni'),
abbreviation: 'g',
matches: ['giorno', 'giorni', 'g'],
},
week: {
name: (c) => (c === 1 ? 'settimana' : 'settimane'),
abbreviation: 'sett',
matches: ['settimana', 'settimane', 'sett'],
},
month: {
name: (c) => (c === 1 ? 'mese' : 'mesi'),
abbreviation: 'mes',
matches: ['mese', 'mesi', 'mes'],
},
year: {
name: (c) => (c === 1 ? 'anno' : 'anni'),
abbreviation: 'a',
matches: ['anno', 'anni', 'a'],
},
decade: {
name: (c) => (c === 1 ? 'decennio' : 'decenni'),
abbreviation: 'dec',
matches: ['decennio', 'decenni', 'dec'],
},
century: {
name: (c) => (c === 1 ? 'secolo' : 'secoli'),
abbreviation: 'sc',
matches: ['secolo', 'secoli', 'sc'],
},
millennium: {
name: (c) => (c === 1 ? 'millennio' : 'millenni'),
abbreviation: 'mil',
matches: ['millennio', 'millenni', 'mil'],
},
},
} satisfies import('./helpers/definition-types').LanguageDefinition;
72 changes: 72 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export default {
decimal: ',',
and: 'en',

units: {
nanosecond: {
name: (c) => (c === 1 ? 'nanoseconde' : 'nanoseconden'),
abbreviation: 'ns',
matches: ['nanoseconde', 'nanoseconden', 'ns'],
},
microsecond: {
name: (c) => (c === 1 ? 'microseconde' : 'microseconden'),
abbreviation: 'μs',
matches: ['microseconde', 'microseconden', 'μs'],
},
millisecond: {
name: (c) => (c === 1 ? 'milliseconde' : 'milliseconden'),
abbreviation: 'ms',
matches: ['milliseconde', 'milliseconden', 'ms'],
},
second: {
name: (c) => (c === 1 ? 'seconde' : 'seconden'),
abbreviation: 's',
matches: ['seconde', 'seconden', 's'],
},
minute: {
name: (c) => (c === 1 ? 'minuut' : 'minuten'),
abbreviation: 'min',
matches: ['minuut', 'minuten', 'min'],
},
hour: {
name: (c) => (c === 1 ? 'uur' : 'uren'),
abbreviation: 'h',
matches: ['uur', 'uren', 'h'],
},
day: {
name: (c) => (c === 1 ? 'dag' : 'dagen'),
abbreviation: 'd',
matches: ['dag', 'dagen', 'd'],
},
week: {
name: (c) => (c === 1 ? 'week' : 'weken'),
abbreviation: 'w',
matches: ['week', 'weken', 'w'],
},
month: {
name: (c) => (c === 1 ? 'maand' : 'maanden'),
abbreviation: 'mnd',
matches: ['maand', 'maanden', 'mnd'],
},
year: {
name: (c) => (c === 1 ? 'jaar' : 'jaren'),
abbreviation: 'jr',
matches: ['jaar', 'jaren', 'jr'],
},
decade: {
name: (c) => (c === 1 ? 'decennium' : 'decennia'),
abbreviation: 'dec',
matches: ['decennium', 'decennia', 'dec'],
},
century: {
name: (c) => (c === 1 ? 'eeuw' : 'eeuwen'),
abbreviation: 'eeuw',
matches: ['eeuw', 'eeuwen'],
},
millennium: {
name: (c) => (c === 1 ? 'millennium' : 'millennia'),
abbreviation: 'mil',
matches: ['millennium', 'millennia', 'mil'],
},
},
} satisfies import('./helpers/definition-types').LanguageDefinition;
58 changes: 58 additions & 0 deletions tests/de.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { createMs } from 'enhanced-ms';
import { describe, expect, it } from 'vitest';

// Using German locale (de)
const ms = createMs({ language: 'de' });

describe('German (Deutsch)', () => {
describe('format milliseconds', () => {
it('formats with default options', () => {
// 90061 ms should format as "1 Minute 30 Sekunden"
expect(ms(90061)).toBe('1 Minute 30 Sekunden');
// 90061000 ms should format as "1 Tag 1 Stunde 1 Minute 1 Sekunde"
expect(ms(90061000)).toBe('1 Tag 1 Stunde 1 Minute 1 Sekunde');
});

it('returns null for durations below one second with default options', () => {
expect(ms(0)).toBeNull();
});

it('formats with preset short', () => {
// Expected abbreviations based on the German config:
// Minute -> "Min.", Sekunde -> "Sek.", Tag -> "T.", Stunde -> "Std."
expect(ms(90061, 'short')).toBe('1Min. 30Sek.');
expect(ms(90061000, 'short')).toBe('1T. 1Std.');
});

it('formats with colon notation preset', () => {
expect(ms(90061, 'colonNotation')).toBe('01:30');
expect(ms(90061000, 'colonNotation')).toBe('25:01:01');
});

it('formats with use abbreviations', () => {
const options = { useAbbreviations: true };
expect(ms(90061, options)).toBe('1Min. 30Sek.');
expect(ms(90061000, options)).toBe('1T. 1Std. 1Min. 1Sek.');
});

it('formats with a unit limit', () => {
const options = { unitLimit: 1 };
expect(ms(90061, options)).toBe('1 Minute');
expect(ms(90061000, options)).toBe('1 Tag');
});
});

describe('parse duration', () => {
it('parses durations correctly', () => {
expect(ms('1 Minute 30 Sekunden')).toBe(90000);
expect(ms('1Min. 30Sek.')).toBe(90000);
expect(ms('1 Minute 30 Sekunden 61 Millisekunden')).toBe(90061);
expect(ms('1Min. 30Sek. 61ms')).toBe(90061);
});

it('returns zero for invalid duration strings', () => {
expect(ms('nyr9341')).toBe(0);
expect(ms('o4utrc89nyt')).toBe(0);
});
});
});
Loading
Loading