📜 ⬆️ ⬇️

.Net Localized "relative time" messages

Hello,

On the current project, the customer received requirements that information about past events should be shown to the client as “relative to time” (Message like: “The event occurred N minutes \ minutes \ minutes ago”). We received message templates for various options, as well as requirements for appearance, one of which was that messages should be localized.

“What is difficult here?”, We thought, and went to Google, in search of a ready-made solution. Unfortunately, Google portrayed himself as a partisan, offered several options for off-the-shelf solutions for building this type of message for the English language, but none of them supported localization.
')
“There is nothing to do, it is necessary to support the Russian car industry to reinvent your bicycle,” we decided. And that's what came out of it ...


Localization in our application is implemented through resource files. It was decided that all necessary information for localization will also be stored in them. The main problem was the definition of the rules by which it is necessary to take one or another word meaning from the resources. In our case, these were the words “Day”, “Hour”, “Minute”, with the corresponding numeral form.

After talking to Google again, we found a wonderful site that describes and standardizes the rules for constructing single and multiple word forms for most languages. This was the starting point for writing code.

On the website above, various types of education of word forms are divided into categories, the most popular of them are:
- one
- few
- many
- other
For each category of each language, examples are given, as well as the rules for belonging to a particular group.

For easier understanding, I will give an example for the English language:
Language NameCodeCategoryExamplesRules
Englishen
oneoneone → n is 1;

other →
everything else
other0, 2-999;

1.2, 2.07 ...


In a nutshell, I will tell you how we decided to do this:
For each of the languages ​​that we support, a class is created, with the method of rendering the category to which the word belongs, according to the number of objects. Possible categories of words in the form of {NameObject}. {Category} are added to the resource file of this language. A farbrik class is created, which returns the implementation of the above class, depending on the language for which it is necessary to calculate the form of the word. A wrapper class is created to localize the form of the word, one of the methods of which takes as part of the parameter the object's resource key ({ObjectName}) and the number of these objects, and returns a string from the corresponding resource file of the required category.

Well, from words to deeds! It's time to program ...
The first step was to create an enum containing the categories that we need to work:
public enum PluralCategories { One, Few, Many, Other } 

Further, the interface for future classes of calculating the category:
 public interface ITimeFormatter { /// <summary> /// Returns category for plural form /// </summary> /// <param name="count"></param> /// <returns></returns> PluralCategories GetPluralCategoryByCount(int count); } 

Factory class:
  /// <summary> /// Default fabric for TimeFormatters /// </summary> public class TimeFormatterFabric { /// <summary> /// Returns TimeFormatter for corresponding culture /// </summary> /// <param name="cultureInfo"></param> /// <returns></returns> public ITimeFormatter GetTimeFormatter(CultureInfo cultureInfo) { ITimeFormatter result; if (cultureInfo.Name.StartsWith("en", StringComparison.InvariantCultureIgnoreCase)) { result = new EnglishTimeFormatter(); } else if (cultureInfo.Name.StartsWith("ru", StringComparison.InvariantCultureIgnoreCase)) { result = new RussianTimeFormatter(); } else { result = new EnglishTimeFormatter(); } return result; } } 


Wrap class:
 public class TimeFormatter { private readonly ILocalizationService _localizationService; private readonly ITimeFormatter _timeFormatter; /// <summary> /// Defaults constructor. /// </summary> /// <param name="localizationService"></param> public TimeFormatter(ILocalizationService localizationService) { _localizationService = localizationService; _timeFormatter = new TimeFormatterFabric().GetTimeFormatter(localizationService.CurrentUiCulture); } /// <summary> /// Returns localized string by key and count of items. /// </summary> /// <param name="rootResourceKey"></param> /// <param name="count"></param> /// <returns></returns> public string Localize(string rootResourceKey, int count) { var resourceKey = string.Format("{0}.{1}", rootResourceKey, _timeFormatter.GetPluralCategoryByCount(count).ToString()); return _localizationService.Localize(resourceKey); } } 

The grade category for the English language is:
 public class EnglishTimeFormatter : ITimeFormatter { /// <summary> /// Return plural category by count for English language rules /// </summary> /// <param name="count"></param> /// <returns></returns> public PluralCategories GetPluralCategoryByCount(int count) { PluralCategories result = PluralCategories.Other; if (count == 1) { result = PluralCategories.One; } return result; } } 


For English, the following values ​​have been added to the resource file:

DateTime.Days.One day
DateTime.Days.Other days

DateTime.Hours.One hour
DateTime.Hours.Other hours

DateTime.Minutes.One minute
DateTime.Minutes.Other minutes

Well, a small example of use:
 public static string ToTimeLeftString(this DateTime dateTime) { var result = new StringBuilder(); var _localizationService = DependencyResolver.Current.GetService<ILocalizationService>(); var timeFormatter = new TimeFormatter(_localizationService); var currentTime = DateTime.UtcNow; var timeLeft = currentTime - dateTime; result.AppendFormat("{0} {1} {2} {3} {4}", timeLeft.Days, timeFormatter.Localize("DateTime.Days", timeLeft.Days), _localizationService.Localize("Common.Ago"), _localizationService.Localize("Common.At"), _localizationService.ConverUtcDateTimeToUsersTimeZone(dateTime).ToShortTimeString()); return result.ToString(); } 

That's all, thank you for your attention, I hope the article will be useful to someone.

Source: https://habr.com/ru/post/140121/


All Articles