.Netローカライズされた相対時間メッセージ

みなさん、こんにちは



現在のプロジェクトでは、過去のイベントに関する情報を「時間に関連する」形式(「イベントはN分\分\分前に発生しました」の形式)でクライアントに表示する必要があるという要件がありました。 さまざまなオプションのメッセージテンプレートと外観の要件を受け取りましたが、その1つはメッセージをローカライズすることです。



「ここで何が複雑なのでしょうか?」、私たちは、既成のソリューションを探してGoogleに行き、考えました。 残念ながら、Googleは党派のふりをして、このタイプの英語のメッセージを作成するための既製のソリューションにいくつかのオプションを提供しましたが、ローカリゼーションをサポートするものはありませんでした。



「することは何もありません。 ロシアの自動車産業をサポートして 、独自の自転車発明する必要があります」と私たちは決めました。 そして、それが何をもたらしたのか...





アプリケーションのローカライズは、リソースファイルを通じて実装されます。 ローカライズに必要なすべての情報もそれらに保存されることになりました。 主な問題は、単語の特定の意味をリソースから取得する必要があるルールの定義でした。 私たちの場合、これらは「Day」、「Hour」、「Minute」という単語で、対応する数字の形式があります。



Googleと再度話し合ったところ、ほとんどの言語で単一および複数の単語形式を構築するためのルールが記述され、標準化されている素晴らしいサイトを見つけました。 これがコードを書くための出発点でした。



上記のサイトでは、さまざまな種類の単語形式がカテゴリに分類されており、最も人気があります。

-1

-少ない

-多くの

-その他

各言語のカテゴリごとに、特定のグループのメンバーシップ、ルール、例が示されています。



わかりやすくするために、英語の例を示します。

言語名 コード カテゴリー ルール
英語 en

1 one→nは1です。



その他→

他のすべて

その他 0、2-999;



1.2、2.07 ...





一言で言えば、これをどのように決定したかを説明します。

サポートする言語ごとに、オブジェクトの数ごとに、単語が属するカテゴリを計算するメソッドを持つクラスが作成されます。 {ObjectName}。{Category}という形式の単語の可能なリソースカテゴリは、この言語のリソースファイルに追加されます。 単語の形式を計算する必要がある言語に応じて、前述のクラスの実装を返すfarbricクラスが作成されます。 ラッパークラスが作成され、単語形式をローカライズします。メソッドの1つは、キーの一部としてオブジェクトのリソースキー({ObjectName})とこれらのオブジェクトの数を受け取り、必要なカテゴリの対応するリソースファイルから文字列を返します。



まあ、言葉から行動まで! プログラムする時間です...

最初のステップは、作業する必要があるカテゴリを含む列挙を作成することでした:

public enum PluralCategories { One, Few, Many, Other }
      
      





以下は、カテゴリを計算する将来のクラスのインターフェイスです。

 public interface ITimeFormatter { /// <summary> /// Returns category for plural form /// </summary> /// <param name="count"></param> /// <returns></returns> PluralCategories GetPluralCategoryByCount(int count); }
      
      





工場クラス:

  /// <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; } }
      
      







ラッパークラス:

 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); } }
      
      





英語のカテゴリレンダリングクラス:

 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; } }
      
      







英語の場合、次の値がリソースファイルに追加されています。



DateTime.Days.One day

DateTime.Days.Other days



DateTime.Hours.One hour

DateTime.Hours.Other時間



DateTime.Minutes.1分

DateTime.Minutes。その他の分



さて、小さな使用例:

 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(); }
      
      





以上で、ご清聴ありがとうございました。この記事が誰かに役立つことを願っています。



All Articles