| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="TimeHumanizer.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System.Collections.Generic; |
| | | 8 | | using System.Globalization; |
| | | 9 | | using System.Linq; |
| | | 10 | | using MyNet.Globalization.Localization.Providers; |
| | | 11 | | using MyNet.Globalization.Localization.Translation; |
| | | 12 | | using MyNet.Humanizer.Formatting.Collections; |
| | | 13 | | using MyNet.Humanizer.Resources; |
| | | 14 | | using MyNet.Primitives; |
| | | 15 | | using MyNet.Temporal.Decomposition; |
| | | 16 | | |
| | | 17 | | namespace MyNet.Humanizer.Temporal; |
| | | 18 | | |
| | | 19 | | /// <summary> |
| | | 20 | | /// Current implementation of <see cref="ITimeHumanizer"/> that provides culture-specific humanization of relative dates |
| | | 21 | | /// Supports specialization through inheritance for cultures with complex grammatical rules. |
| | | 22 | | /// </summary> |
| | | 23 | | /// <param name="translationService">The translation service used to translate resource keys.</param> |
| | | 24 | | /// <param name="listFormatter">The list formatter used to format lists of humanized time units.</param> |
| | | 25 | | /// <param name="culture">The culture used for humanization and formatting.</param> |
| | | 26 | | /// <param name="options">The options used for time localization resource keys.</param> |
| | | 27 | | public sealed class TimeHumanizer(ITranslationService translationService, ICultureScopedServiceSource<IListFormatter> li |
| | 57 | 28 | | : TimeHumanizerBase(translationService, listFormatter, culture, options); |
| | | 29 | | |
| | | 30 | | /// <summary> |
| | | 31 | | /// Current implementation of ITimeHumanizer that uses a translation service and culture information to provide localize |
| | | 32 | | /// </summary> |
| | | 33 | | /// <param name="translationService">The translation service used to translate resource keys.</param> |
| | | 34 | | /// <param name="listFormatter">The list formatter used to format lists of humanized time units.</param> |
| | | 35 | | /// <param name="supportedCulture">The culture used to format the humanized strings.</param> |
| | | 36 | | /// <param name="options">The options used for time localization.</param> |
| | | 37 | | public abstract class TimeHumanizerBase(ITranslationService translationService, ICultureScopedServiceSource<IListFormatt |
| | | 38 | | { |
| | | 39 | | /// <summary> |
| | | 40 | | /// Gets the supported culture for this humanizer. |
| | | 41 | | /// </summary> |
| | | 42 | | public CultureInfo Culture => supportedCulture; |
| | | 43 | | |
| | | 44 | | /// <inheritdoc/> |
| | | 45 | | public virtual string HumanizeRelativeTime(int count, TimeUnit unit, Tense tense) |
| | | 46 | | { |
| | | 47 | | if (count == 0) |
| | | 48 | | { |
| | | 49 | | return translationService.Translate(tense == Tense.Past ? options.NeverKey : options.NowKey, TranslationOpti |
| | | 50 | | } |
| | | 51 | | |
| | | 52 | | var key = GetRelativeDateKey(count, unit, tense); |
| | | 53 | | |
| | | 54 | | return translationService.Translate(key, new() { Quantity = count, UseInflectionFallback = false }, nameof(DateT |
| | | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <inheritdoc/> |
| | | 58 | | public string HumanizeRelativeTime(IReadOnlyList<TimeUnitValue> values, Tense tense) |
| | | 59 | | { |
| | | 60 | | switch (values.Count) |
| | | 61 | | { |
| | | 62 | | case 0: |
| | | 63 | | return string.Empty; |
| | | 64 | | case 1: |
| | | 65 | | return HumanizeRelativeTime(values[0].Value, values[0].Unit, tense); |
| | | 66 | | } |
| | | 67 | | |
| | | 68 | | var key = GetMultipleRelativeDateKey(tense); |
| | | 69 | | |
| | | 70 | | var parts = values.Where(x => x.Value > 0).Select(v => HumanizeDuration(v.Value, v.Unit)).ToArray(); |
| | | 71 | | |
| | | 72 | | var durationStr = listFormatter.Get(Culture).Format(parts); |
| | | 73 | | |
| | | 74 | | return translationService.Translate(key, new TranslationOptionsBuilder().WithArgument("duration", durationStr).W |
| | | 75 | | } |
| | | 76 | | |
| | | 77 | | /// <inheritdoc/> |
| | | 78 | | public virtual string HumanizeDuration(int count, TimeUnit unit) |
| | | 79 | | { |
| | | 80 | | if (count == 0) |
| | | 81 | | { |
| | | 82 | | // "no time" for tiny units, explicit "0 <unit>" for larger constrained units. |
| | | 83 | | return unit is TimeUnit.Minute or TimeUnit.Hour or TimeUnit.Day or TimeUnit.Week or TimeUnit.Month or TimeUn |
| | | 84 | | ? translationService.Translate(GetDurationKey(count, unit), new() { Quantity = count, UseInflectionFallb |
| | | 85 | | : translationService.Translate(options.ZeroKey, TranslationOptionsPresets.Default, nameof(DateTimeResour |
| | | 86 | | } |
| | | 87 | | |
| | | 88 | | var resourceKey = GetDurationKey(count, unit); |
| | | 89 | | |
| | | 90 | | return translationService.Translate(resourceKey, new() { Quantity = count, UseInflectionFallback = false }, name |
| | | 91 | | } |
| | | 92 | | |
| | | 93 | | /// <inheritdoc/> |
| | | 94 | | public string HumanizeDuration(IReadOnlyList<TimeUnitValue> values, ListFormattingOptions? listFormattingOptions = n |
| | | 95 | | { |
| | | 96 | | switch (values.Count) |
| | | 97 | | { |
| | | 98 | | case 0: |
| | | 99 | | return string.Empty; |
| | | 100 | | case 1: |
| | | 101 | | return HumanizeDuration(values[0].Value, values[0].Unit); |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | var key = GetMultipleDurationKey(); |
| | | 105 | | |
| | | 106 | | var parts = values.Where(x => x.Value > 0).Select(v => HumanizeDuration(v.Value, v.Unit)).ToArray(); |
| | | 107 | | |
| | | 108 | | var durationStr = listFormatter.Get(Culture).Format(parts, listFormattingOptions); |
| | | 109 | | |
| | | 110 | | return string.IsNullOrWhiteSpace(key) |
| | | 111 | | ? durationStr |
| | | 112 | | : translationService.Translate(key, new TranslationOptionsBuilder().WithArgument("duration", durationStr).Wi |
| | | 113 | | } |
| | | 114 | | |
| | | 115 | | /// <summary> |
| | | 116 | | /// Gets the resource key used for relative date humanization based on the time unit, count, and tense. |
| | | 117 | | /// Override this method for cultures with complex grammatical rules. |
| | | 118 | | /// </summary> |
| | | 119 | | /// <param name="count">The number of time units.</param> |
| | | 120 | | /// <param name="unit">The time unit.</param> |
| | | 121 | | /// <param name="tense">The tense of the time expression.</param> |
| | | 122 | | /// <returns>The resource key for relative date humanization.</returns> |
| | | 123 | | protected virtual string GetRelativeDateKey(int count, TimeUnit unit, Tense tense) |
| | | 124 | | => count == 1 && unit == TimeUnit.Day |
| | | 125 | | ? tense == Tense.Future ? options.TomorrowKey : options.YesterdayKey |
| | | 126 | | : string.Format(CultureInfo.InvariantCulture, options.RelativeDateKeyFormat, tense, unit); |
| | | 127 | | |
| | | 128 | | protected virtual string GetMultipleRelativeDateKey(Tense tense) => string.Format(CultureInfo.InvariantCulture, opti |
| | | 129 | | |
| | | 130 | | /// <summary> |
| | | 131 | | /// Gets the resource key used for duration humanization. |
| | | 132 | | /// Override this method for cultures with complex grammatical rules. |
| | | 133 | | /// </summary> |
| | | 134 | | /// <param name="count">The number of time units.</param> |
| | | 135 | | /// <param name="unit">The time unit.</param> |
| | | 136 | | /// <returns>The resource key for duration humanization.</returns> |
| | | 137 | | protected virtual string GetDurationKey(int count, TimeUnit unit) => string.Format(CultureInfo.InvariantCulture, opt |
| | | 138 | | |
| | | 139 | | /// <summary> |
| | | 140 | | /// Gets the resource key used for multiple duration humanization. |
| | | 141 | | /// Override this method for cultures with complex grammatical rules. |
| | | 142 | | /// </summary> |
| | | 143 | | /// <returns>The resource key for multiple duration humanization.</returns> |
| | | 144 | | protected virtual string GetMultipleDurationKey() => string.Empty; |
| | | 145 | | } |
| | | 146 | | |