Комментарии 14
Хороший вопрос.
Microsoft вводит требование локализуемости, т.е. отделение кода и ресурсов. Утверждается, что в локализуемом приложении не понадобится писать код для новых языков приложения. Наивные.
В вашем примере это явно не так. Возможно, именно для таких случаев, необходимо уметь размещать в сателлитных сборках (один из видов хранения ресурсов в .NET) стратегии по обработке конкретных ресурсов для разных языков.
Но почему бы вам не реализовать ICU Messages? (если еще нет соответвующей либы)
Сейчас никак — стандартных средств нет.
Но и случаев таких пока не было.
Порт ICU Messages найден на гитхабе: https://github.com/jeffijoe/messageformat.net
А по-поводу статьи (далее обращаюсь к ТС), извините, но вводить новый тип для строк и использовать что-то вроде
var fu = new MultiCulturalString(ru, "Шоколад Алина").SetLocalizedString(en, "Chocolate Alina");
вообще не круто. Интересно будет посмотреть как это выглядит в конце. Плюс вы не привязываетесь ни к winforms, ни к wpf (ни к чему-то еще?). А это как раз представляет больший интерес, чем простая возможность хранить строки для MessageBox.
В данный момент мы используем решение, где локализируемые строки выглядят как простой класс с «константами»:
public static class Lng
{
public static class Login
{
public static string Disconnect { get; private set; } = "Disconnect";
...
}
...
}
а «локализация» происходит за счет рефлексии. Для WPF это легко обернуть в MarkupExtension где нужные строки возвращаются по ключу (ключ — это путь к свойству, к примеру Login.Disconnect).
Проблемы с конкатенацией (на которой вы делаете упор, обьясняя необходимость нового типа) решается элементарно конкатенацией предложений или фраз с независимым от культуры разделителем (например, перевод строки). Понятно, что обьединять слова/фразы в предложение не получится (в русском, например, нужно правильно построить окончания).
Возвращать null на неизвестную строку? Боже упаси! Если строка отсутствует в файле локализации, то вернется значение по умолчанию (в нашем случае это псевдо-english, который по-хорошему следует локализовать в en-US/en-GB). Если неверен ключ, но вернется значение ключа "!!!" + key (в этом случае пользователь/тестер сможет оперативно сделать багрепорт, а программист — с легкостью найти ошибку).
А по-поводу статьи (далее обращаюсь к ТС), извините, но вводить новый тип для строк и использовать что-то вроде
var fu = new MultiCulturalString(ru, "Шоколад Алина").SetLocalizedString(en, "Chocolate Alina");
На практике такой код практически не встречается, ведь ресурсы динамически подгружаются из упомянутого CustomizedResourceManager
. Именно он нужен, например, для WinForms.
Возвращать null на неизвестную строку? Боже упаси!
Совершенно согласен. Это предположение введено в середине статьи, чтобы развить тему с IResourceFallbackProcess
позже. А потом сказано, что пустоты на UI, конечно же, недопустимы.
Несколько лет назад разработал класс Multistring как раз для подобных задач. Со всеми плюшками, сериализацией, сравнением, и т.д. и т.п.
В результате — другие методы работы практически вытеснили использование этого класса.
Ну мы старались не привязывать Multistring к чему-либо, а использовать именно как отдельный тип данных.
Видели два сценария использования этого типа.
Первый: тип данных для пользовательских данных, которые сам пользователь и наполняет (сделали удобные UI-компоненты для работы с типом).
Примеры:
- Тэги. Хозяин блога хочет добавить тэги к тексту (book = книга). Без Multistring так и добавляли: #book, #книга
- есть какая-то сущность с предопределёнными атрибутами. Например атрибут "размер" (=size). Без Multistring пользователь не мог добавить перевод с русского на английский.
В обоих случаях идея провалилась по той причине, что пользователи никак не могли понять концепции нескольких строк "в одной". Были бы программистами — скорее всего поняли бы, но мы работаем с простыми людьми — они не поняли.
В результате для блогов оставили как было, для атрибутов — используем стандартные структуры данных, добавляем таблицы, и т.д. — зависит от случая.
Второй: как хранилище строк для интерфейса — не оправдались затраты на написание кода. Файлы ресурсов оказались просто удобнее, т.к. эта информация редактируется на стороне программиста, а не заказчика.
Что-бы повысить удобство использования, рекомендую вам отказаться от .ToString() в той концепции, в которой вы его используете, а именно для получения текущего значения (по текущей культуре). Всё-таки .ToString() — это метод, описывающий объект, и подразумевается, что его результат от версии к версии может быть разным. Заменить .ToString() следует операторами, конвертирующими в/из System.String.
P.S. выкидывать сам .ToString() — не обязательно
По поводу использования операторов и переопределенного Object.ToString()
— спасибо, принято!
Все же методы ToString()/GetString()
использовать приходиться, по нескольку перегрузок каждого с нужными параметрами (локаль, использовать ли и какой именно fallback process, поставщик форматирования). Причем две перегрузки ToString()
нужны для корректного форматирования при подстановках.
Подмостки для Вавилонской башни, или О собственных типах данных для многоязычных приложений