From 94106df828b374b8e0dae826595f0c96205615ef Mon Sep 17 00:00:00 2001 From: Arsentii Zakharchenko Date: Mon, 21 Oct 2024 06:15:31 +1100 Subject: [PATCH] handle pluralization for vingle values --- .../common/constants/duration_formats.ts | 58 ++++--- .../common/converters/duration.test.ts | 144 +++++++++++++++++- .../common/converters/duration.ts | 13 +- 3 files changed, 189 insertions(+), 26 deletions(-) diff --git a/src/plugins/field_formats/common/constants/duration_formats.ts b/src/plugins/field_formats/common/constants/duration_formats.ts index 2cff55e539d63..e1de9aa714a62 100644 --- a/src/plugins/field_formats/common/constants/duration_formats.ts +++ b/src/plugins/field_formats/common/constants/duration_formats.ts @@ -79,22 +79,26 @@ const inputFormats = [ }, ]; const DEFAULT_OUTPUT_FORMAT = { - text: i18n.translate('fieldFormats.duration.outputFormats.humanize.approximate', { - defaultMessage: 'Human-readable (approximate)', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.humanize.approximate', { + defaultMessage: 'Human-readable (approximate)', }), method: 'humanize', }; const outputFormats = [ { ...DEFAULT_OUTPUT_FORMAT }, { - text: i18n.translate('fieldFormats.duration.outputFormats.humanize.precise', { - defaultMessage: 'Human-readable (precise)', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.humanize.precise', { + defaultMessage: 'Human-readable (precise)', }), method: 'humanizePrecise', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asMilliseconds', { - defaultMessage: 'Milliseconds', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asMilliseconds', { + defaultMessage: '{value, plural, one {Millisecond} other {Milliseconds}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asMilliseconds.short', { defaultMessage: 'ms', @@ -102,8 +106,10 @@ const outputFormats = [ method: 'asMilliseconds', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asSeconds', { - defaultMessage: 'Seconds', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asSeconds', { + defaultMessage: '{value, plural, one {Second} other {Seconds}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asSeconds.short', { defaultMessage: 's', @@ -111,8 +117,10 @@ const outputFormats = [ method: 'asSeconds', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asMinutes', { - defaultMessage: 'Minutes', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asMinutes', { + defaultMessage: '{value, plural, one {minute} other {minutes}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asMinutes.short', { defaultMessage: 'min', @@ -120,8 +128,10 @@ const outputFormats = [ method: 'asMinutes', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asHours', { - defaultMessage: 'Hours', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asHours', { + defaultMessage: '{value, plural, one {Hour} other {Hours}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asHours.short', { defaultMessage: 'h', @@ -129,8 +139,10 @@ const outputFormats = [ method: 'asHours', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asDays', { - defaultMessage: 'Days', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asDays', { + defaultMessage: '{value, plural, one {Day} other {Days}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asDays.short', { defaultMessage: 'd', @@ -138,8 +150,10 @@ const outputFormats = [ method: 'asDays', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asWeeks', { - defaultMessage: 'Weeks', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asWeeks', { + defaultMessage: '{value, plural, one {Week} other {Weeks}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asWeeks.short', { defaultMessage: 'w', @@ -147,8 +161,10 @@ const outputFormats = [ method: 'asWeeks', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asMonths', { - defaultMessage: 'Months', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asMonths', { + defaultMessage: '{value, plural, one {Month} other {Months}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asMonths.short', { defaultMessage: 'mon', @@ -156,8 +172,10 @@ const outputFormats = [ method: 'asMonths', }, { - text: i18n.translate('fieldFormats.duration.outputFormats.asYears', { - defaultMessage: 'Years', + text: (value: string) => + i18n.translate('fieldFormats.duration.outputFormats.asYears', { + defaultMessage: '{value, plural, one {Year} other {Years}}', + values: { value }, }), shortText: i18n.translate('fieldFormats.duration.outputFormats.asYears.short', { defaultMessage: 'y', diff --git a/src/plugins/field_formats/common/converters/duration.test.ts b/src/plugins/field_formats/common/converters/duration.test.ts index bdef0c2b23f23..a7d07670dcc1a 100644 --- a/src/plugins/field_formats/common/converters/duration.test.ts +++ b/src/plugins/field_formats/common/converters/duration.test.ts @@ -81,6 +81,99 @@ describe('Duration Format', () => { ], }); + testCase({ + inputFormat: 'minutes', + outputFormat: 'asHours', + outputPrecision: undefined, + showSuffix: true, + fixtures: [ + { + input: -60, + output: '-1.00 hour', + }, + { + input: 60, + output: '1.00 hour', + }, + { + input: 125, + output: '2.08 hours', + }, + ], + }); + + testCase({ + inputFormat: 'minutes', + outputFormat: 'asDays', + outputPrecision: undefined, + showSuffix: true, + fixtures: [ + { + input: -1440, + output: '-1.00 day', + }, + { + input: 1440, + output: '1.00 day', + }, + { + input: 2880, + output: '2.00 days', + }, + ], + }); + + testCase({ + inputFormat: 'hours', + outputFormat: 'asWeeks', + outputPrecision: undefined, + showSuffix: true, + fixtures: [ + { + input: 168, + output: '1.00 week', + }, + { + input: 336, + output: '2.00 weeks', + }, + ], + }); + + testCase({ + inputFormat: 'hours', + outputFormat: 'asMonths', + outputPrecision: undefined, + showSuffix: true, + fixtures: [ + { + input: 730.56, + output: '1.00 month', + }, + { + input: 1461.11, + output: '2.00 months', + }, + ], + }); + + testCase({ + inputFormat: 'hours', + outputFormat: 'asYears', + outputPrecision: undefined, + showSuffix: true, + fixtures: [ + { + input: 8760, + output: '1.00 year', + }, + { + input: 17520, + output: '2.00 years', + }, + ], + }); + testCase({ inputFormat: 'seconds', outputFormat: 'asSeconds', @@ -137,6 +230,10 @@ describe('Duration Format', () => { input: -60, output: '-60 seconds', }, + { + input: 1, + output: '1 second', + }, { input: -32.333, output: '-32 seconds', @@ -144,6 +241,41 @@ describe('Duration Format', () => { ], }); + testCase({ + inputFormat: 'nanoseconds', + outputFormat: 'asMilliseconds', + outputPrecision: 0, + showSuffix: true, + fixtures: [ + { + input: 1000000, + output: '1 millisecond', + }, + { + input: 2000000, + output: '2 milliseconds', + }, + ], + }); + + testCase({ + inputFormat: 'seconds', + outputFormat: 'asMinutes', + outputPrecision: 2, + showSuffix: true, + useShortSuffix: false, + fixtures: [ + { + input: 60, + output: '1.00 minute', + }, + { + input: 120, + output: '2.00 minutes', + }, + ], + }); + testCase({ inputFormat: 'nanoseconds', outputFormat: 'humanizePrecise', @@ -253,7 +385,7 @@ describe('Duration Format', () => { }, { input: 1, - output: '1.00 seconds', + output: '1.00 second', }, { input: 12, @@ -275,6 +407,14 @@ describe('Duration Format', () => { input: 3857, output: '1.07 hours', }, + { + input: 3600, + output: '1.00 hour', + }, + { + input: 86400, + output: '1.00 day', + }, { input: 123292, output: '1.43 days', @@ -422,7 +562,7 @@ describe('Duration Format', () => { }, { input: 604800, - output: '1.00 weeks', + output: '1.00 week', }, // 1 week and 3 and a half days { diff --git a/src/plugins/field_formats/common/converters/duration.ts b/src/plugins/field_formats/common/converters/duration.ts index fcead11fabb45..31492828ad3dc 100644 --- a/src/plugins/field_formats/common/converters/duration.ts +++ b/src/plugins/field_formats/common/converters/duration.ts @@ -99,7 +99,7 @@ export class DurationFormat extends FieldFormat { const precise = human || humanPrecise ? formatted : Number(formatted).toFixed(outputPrecision); const type = DURATION_OUTPUT_FORMATS.find(({ method }) => method === outputFormat); - const unitText = useShortSuffix ? type?.shortText : type?.text.toLowerCase(); + const unitText = useShortSuffix ? type?.shortText : type?.text(precise).toLowerCase(); const suffix = showSuffix && unitText && !human ? `${includeSpace}${unitText}` : ''; @@ -131,9 +131,9 @@ function formatDurationHumanPrecise( if (!duration || !duration.isValid()) return; const valueInSeconds = duration.as('seconds'); - const getUnitText = (method: string) => { + const getUnitText = (method: string, unitValue: string) => { const type = DURATION_OUTPUT_FORMATS.find(({ method: methodT }) => method === methodT); - return useShortSuffix ? type?.shortText : type?.text.toLowerCase(); + return useShortSuffix ? type?.shortText : type?.text(unitValue).toLowerCase(); }; for (const unit of units) { @@ -141,7 +141,12 @@ function formatDurationHumanPrecise( if (unitValue >= 1 || unit === units[units.length - 1]) { // return a value if it's the first iteration where the value > 1, or the last iteration const prefix = negativeValue ? '-' : ''; - return prefix + unitValue.toFixed(outputPrecision) + includeSpace + getUnitText(unit.method); + return ( + prefix + + unitValue.toFixed(outputPrecision) + + includeSpace + + getUnitText(unit.method, unitValue.toFixed(outputPrecision)) + ); } } }