Расширение локализации для режима технического обслуживания¶
В этой статье мы рассмотрим, как создать стратегию локализации, поставляющую строки локализации для режима технического обслуживания. В качестве источника локализации будем использовать CSV
файл.
Файл с данными локализации¶
Вначале создадим файл с данными (localization.csv
).
Name;Language;Value
CustomMessage;ru;"Моё сообщение, в котором есть запятые;
и переносы строк."
CustomMessage;fr;"Mon message, qui contient des virgules;
et les sauts de ligne."
CustomMessage;;"My message, which has commas;
and line breaks."
Tip
В данном случае использован нестандартный разделитель данных ;
.
В данном файле содержатся дополнительные строки локализации для использования в режиме технического обслуживания. Рассмотрим назначение колонок с данными.
Название | Описание |
---|---|
Name |
Название строки локализации. Обязательное. |
Language |
Код языка локализации в формате ISO 639-1 . Может быть не указан, если строка локализации подходит для всех языков (является универсальной). |
Value |
Локализованная на указанном языке строка локализации. |
Стратегия локализации¶
Для большей гибкости условимся, что будем задавать путь к файлу локализации в поле Localization.Csv
. Для этого отредактируем файл app.json
утилиты tadmin
и добавим указанное поле следующим образом (всё остальное содержимое файла оставим без изменений).
{
"Settings": {
"Maintenance": {
"Localization.Csv": "c:/data/localization.csv",
}
}
}
Important
Здесь представлен не файл целиком, а лишь его часть для понимания, куда именно необходимо поместить данные.
Tip
Не забудьте либо поменять путь, либо поместить ваш файл с локализацией по указанному пути.
Important
Согласно принятым в команде tadmin
Maintenance
соглашениям, префикс Localization.
будет отрезан и путь к файлу будет содержаться в дополнительных данных, предоставляемых команде по ключу Csv
.
Заметим, что в нашем файле данных содержатся многострочные локализованные значения, которые также содержат специальные символы (,
и ;
), используемые в файле в качестве разделителей.
Чтобы упростить работу с файлом CSV
и не выполнять разбор вручную, мы будем использовать специализированный пакет CsvHelper
. Данный пакет уже содержится в зависимостях проекта Tessa.Extensions.Console
.
Все стратегии, расширяющие локализацию для режима технического обслуживания, должны быть унаследованы от базового класса MaintenanceLocalizationStrategyBase
. Создайте в проекте Tessa.Extensions.Console
в директории Maintenance
файл CsvMaintenanceLocalizationStrategy.cs
следующего содержания.
#nullable enable
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CsvHelper;
using CsvHelper.Configuration;
using Tessa.Localization;
using Tessa.Platform;
using Tessa.Platform.Maintenance;
using Tessa.Platform.Storage;
namespace Tessa.Extensions.Console.Maintenance
{
[Order(1)]
public sealed class CsvMaintenanceLocalizationStrategy :
MaintenanceLocalizationStrategyBase
{
#region Internal Classes
private class DataRecord
{
public string Name { get; set; } = string.Empty;
public string? Language { get; set; }
public string Value { get; set; } = string.Empty;
}
private class DataMap : ClassMap<DataRecord>
{
public DataMap()
{
this.Map(static m => m.Name).Name("Name").Index(0);
this.Map(static m => m.Language).Name("Language").Index(1);
this.Map(static m => m.Value).Name("Value").Index(2);
}
}
#endregion
#region Private Fields
private const string CsvFileKey = "Csv";
#endregion
#region Base Overrides
/// <inheritdoc />
public override bool ShouldOverride() => true;
/// <inheritdoc />
public override async ValueTask<LocalizationEntryCollection> GetEntriesAsync(
IReadOnlySet<string> names, IDictionary<string, object?> args, CancellationToken cancellationToken = default)
{
var collection = new LocalizationEntryCollection();
var filePath = args?.TryGet<string>(CsvFileKey) ?? string.Empty;
if (string.IsNullOrEmpty(filePath))
{
return collection;
}
var configuration = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";",
};
using var reader = new StreamReader(filePath);
using var csv = new CsvReader(reader, configuration);
csv.Context.RegisterClassMap<DataMap>();
await foreach (var record in csv.GetRecordsAsync<DataRecord>(cancellationToken))
{
if (!collection.TryGetItem(record.Name, out var entry))
{
entry = new LocalizationEntry(record.Name);
collection[entry.Name] = entry;
}
var str = new LocalizationString(GetCultureInfo(record.Language), record.Value);
entry.Strings[str.Culture] = str;
}
return collection;
}
#endregion
#region Private Methods
private static CultureInfo GetCultureInfo(string? lang) =>
string.IsNullOrEmpty(lang) || lang == CultureInfo.InvariantCulture.TwoLetterISOLanguageName
? CultureInfo.InvariantCulture
: CultureInfo.GetCultureInfo(lang);
#endregion
}
}
Important
Следует обратить внимание на атрибут [Order(1)]
, указанный у данного класса. Он определяет условный номер данной стратегии в цепочке стратегий, использующихся для построения библиотеки локализации. Чем меньше номер, тем раньше будет вызвана стратегия и тем раньше она наполнит итоговую библиотеку. По умолчанию строки локализации не переопределяются, а могут лишь дополнить библиотеку, если строк с такими же именами в ней ещё нет. Поэтому порядок следования стратегии существенно влияет на её вклад в формировании итоговой библиотеки локализации. Подробнее о приоритетах (order
) стандартных стратегий.
Important
Второй момент, на который необходимо обратить внимание - это метод ShouldOverride
. Он указывает на то, что строки, возвращаемые библиотекой, должны полностью заменить аналогичные строки в итоговой библиотеке локализации. Если вы не уверены в подобной необходимости, используйте реализацию по умолчанию, возвращающую false
.
Important
Обратите внимание, что стратегия локализации может вызываться при уже недоступном основном web-сервисе TESSA. В этом случае вы не сможете использовать большинство стандартного API
. Определить режим работы можно при помощи ISessionTokenHolder.SessionToken
(если он равен null
, то API
недоступно) так, как это делается в LocalizationServiceMaintenanceLocalizationStrategy.GetEntriesAsync
.
Теперь зарегистрируем нашу стратегию. Создайте класс Registrator.cs
в той же директории, что и только что созданный нами класс, со следующим содержимым:
#nullable enable
using Tessa.Platform.Maintenance;
using Unity;
using Unity.Lifetime;
namespace Tessa.Extensions.Console.Maintenance
{
[Registrator(Tag = RegistratorTag.ConsoleClient)]
public sealed class Registrator :
RegistratorBase
{
public override void RegisterUnity()
{
this.UnityContainer
// Регистрация сервиса получения строк локализации для режима техобслуживания.
.RegisterType<IMaintenanceLocalizationStrategy, CsvMaintenanceLocalizationStrategy>(nameof(CsvMaintenanceLocalizationStrategy), new ContainerControlledLifetimeManager())
;
}
}
}
На этом создание стратегии предоставления строк локализации для режима технического обслуживания завершено.