В статье автор пытается очертить некоторые необходимые для понимания аспекты интернационализации в .NET, особенности китайской и не только локализаций, и несколько забавных моментов.
Терминология
Приведу несколько вольно переведенных и отредактированных определений из MSDN:
Локализация (Localization) — это процесс перевода ресурсов приложения в локализованные версии для каждого языка и региональных параметров, которую поддерживает приложение. Переход локализации должен происходить только после того, как выполнена условие Локализуемости (Localizability), т.е. исполняемый код отделен от любых элементов пользовательского интерфейса.
Глобализация (Globalization) — это процесс проектирования и разработки приложения, которое поддерживает локализованный пользовательский интерфейс и региональные данные для пользователей из разных культур. Cведения о конкретных языковых и региональных параметрах могут включать в себя: систему письма, используемые календари, соглашения о формате даты и времени, чисел, денежных и физических величин, правила сортировки и даже форматы адреса, телефона, размера бумаги по умолчанию.
Так что то, чем занимаются команды, приводящие к потребному виду изначально нелокализуемые продукты, называется громким словом — глобализацией, но никак не локализацией.
Язык и региональные стандарты
Итак, в .NET есть главный класс, представляющий информацию о языковых и региональных стандартах (для неуправляемого кода по-английски называемых ''locale'') — System.Globalization.CultureInfo
. Рядом с ним есть еще Calendar
, RegionInfo
, NumberFormatInfo
, DateTimeFormatInfo
и мн. др.
У культуры есть имя (по сути — код), в этих терминах удобно общаться. У инвариантной культуры имя пустое, поэтому мы будем обозначать её как ivl
.
Две культуры потока
У любого потока — экземпляра Thread
есть два свойства: public CultureInfo CurrentCulture {get; set;}
и public CultureInfo CurrentUICulture {get; set;}
Первая культура используется для форматирования чисел, дат и пр. региональных настроек, а вторая используется в алгоритме поиска подходящих локализованных ресурсов.
Так зачем же нужно две культуры? В этом есть резон: для потомка англосаксов, родившегося и живущего в Индии, родной язык — английский. На нем он и хочет видеть интерфейсы программ на своем ноутбуке. Однако, при работе в Excel'е он скорее всего будет оперировать рупиями (буква रु на хинди), и при этом знает, что площадь родной страны составляет 32,87,590.01
км2.
Структура дерева культур
Культуры образуют дерево. Т.е. у каждой культуры есть родительская.
В корне дерева расположена «никакая» культура — инвариантная. Она не содержит в себе информацию о регионе, представляет несуществующий инвариантный язык, правила форматирования в котором странным образом похожи на американские. Родителем инвариантной культуры является другая инвариантная культура, и так до stack overflow.
Противоположностью являются определенные (специфичные, specific) культуры. Они содержат в себе информацию и о языке/письме, и о регионе, и о форматировании чисел и дат. Примеры: ru-RU
, en-US
, en-IN
.
Родителями специфичных культур культур являются нейтральные культуры. Назначение таких культур — нести информацию о языке и письме. До .NET 4.0
нейтральные культуры и не могли содержать информацию о форматировании и регионе, теперь эта информация берется из доминирующей специфичной культуры. Примеры: ru
, en
, mn-Cyrl
(монгольский, кириллица), mn-Mong
(старомонгольское письмо).
Вопрос на засыпку внимательному читателю: кто может быть родителем нейтральной культуры?
Распространенные заблуждения
Итак, мы с легкостью представляем веточку дерева культур на примере ivl <— ru <— ru-RU
. Но неверно утверждать, что иерархия всегда состоит из трех культур. Так, например, думали авторы книги Язык программирования C# 2005 для профессионалов в примере к 17-й главе, и тогда это было почти верно.
Но языки с несколькими видами письма ломают стереотип.
До .NET 4.0 все было совсем запутанно: существовали специфичные культуры, родителем которых была инвариантная. См. тулу.
Китайский куст
На китайском говорят свыше 1,3 млрд человек, официальным является в Китайской Народной Республике, Китайской Республике (aka Тайвань) и Сингапуре. И не забудьте о специальных административных районах — Гонконге и Макао.
Существует два вида китайского письма: упрощенное (с 1956 г.) и традиционное. Традиционно китайцы писали сверху вниз, а столбцы шли справа налево. Совсем недавно, с 2004 г., на Тайване перестали официально использовать вертикальное письмо. Сейчас используется «европейский» способ письма — горизонтально слева направо.
Вернемся к .NET'у. Культуры zh-CHS
и zh-CHT
в .NET 2.0 были объявлены устаревшими и заменены на zh-Hans
и zh-Hant
. В дереве культур zh-Hans
является родителем zh-CHS
для корректной работы fallback process. В дальнейшем с любым патчем устаревшие культуры могут пропасть.
Отдельно подчеркну, что на территории КНР используются оба вида письма: в Гонконге и Макао — традиционное, на остальной бoльшей территории — упрощенное.
Fallback process
Для поиска подходящих ресурсов (текста, координат и размеров контролов, иконок и мн. др.) экземпляр ResourceManager
смотрит на Thread.Current.CurrentUICulture
. UI-культура может быть как специфичной, так и нейтральной. А вот Thread.Current.CurrentCulture
м.б. только специфичной культурой.
Сначала менеджер ресурсов пытается найти ресурсы, чья культура совпадает с UI-культурой. Если не находит, то берет родительскую культуру и повторяет поиск. Если таким образом мы дойдем до инвариантной культуры, то придется использовать ресурсы по умолчанию (нейтральные) (часто они расположены в основной сборке, но не обязательно).
Правда, ресурсы по умолчанию тоже могут быть помечены культурой. См. подробности в MSDN.
Косяки от MS
№1
Вашему вниманию представлен еще один кустик культур — узбекский:
Понятно, что произошло: после 1991 г. когда-то переведенные на кириллицу языки стали усиленно от кириллицы избавлять.
У класса CultureInfo
есть свойство string NativeName
, т.е. название культуры на описываемом языке. Для культуры uz-Latn-UZ
значение NativeName
равно U'zbek (U'zbekiston Respublikasi)
, хотя на самом деле должно быть O'zbek (O'zbekiston Respublikasi)
.
Багу уже много версий .NET.
№2
Поговорим про бывшую союзную республику Молдавию, самоназвание ''Молдова''. Говорят молдаване на молдавском языке. Хотя ученые-филологи спорят, что это не самостоятельный язык, а диалект румынского.
По факту, существует три румынских языка:
- румынский в Румынии (латиница);
- румынский в Приднестровье (кириллица), оставшийся в том виде, в каком был на момент распада Союза;
- румынский в Молдавии (латиница), со своим вариантом латинизации, не совпадающем с принятым в Румынии.
Казалось бы, в .NET мы можем ожидать увидеть три специфичные румынские культуры, ну, или две — по политическим причинам (Приднестровье). Но нет, Молдавии в Windows NLS API нет. Есть только культура ro-RO
, Румынский (Румыния). Именно такой локалью молдавские пользователи и пользуются. Зато Microsoft в Молдавии есть.
Ну и конечно же, .NET позволяет создавать собственные культуры.
Интересно, что когда-то давно, в первых .NET'ах и старых операционках были замечены культуры ru-MO
и ro-MO
. Да, код региона был MO
, а не MD
как сейчас. Стандарт ISO
поменялся?
Табу для локализуемых приложений
Список не может быть полным, зато примеры из личного опыта отлавливания багов локализованных приложений.
№1
Очевидно, что никогда нельзя зашиваться на имена системных папок. Хотя, казалось бы, куда могут подеваться Program Files
? По какой-то нелепости в локализованной на русский Windows эту папку не стали переименовывать. Но так не во всех локализациях!
В испанской локализации папка гордо именуется Archivos de programa
. Рекомендую: перевод Гугла с испанского на русский.
№2
Настоящим бичом глобализуемого-локализуемого приложения являются строки. Конкатенирующиеся. Но даже если строки с подстановками, то переводчикам содержимое подстановок без комментариев неочевидно: "{0}" вызвало ошибку "{1}".{2}Обратитесь к {3}
. А под {2}
имеется в виду банальный Environment.NewLine
.
Ссылки
MSDN
- .NET Framework 4: What's New in Globalization and Localization
- Globalizing and Localizing .NET Framework Applications (.NET Framework 4.5)
- National Language Support (NLS) API Reference
- WPF: Globalization and Localization
Статьи
- Глава из книги “ Язык программирования C# 2005 для профессионалов” с примерами
- zh-Hans, zh-Hant and the «old» zh-CHS, zh-CHT
- The 'Program Files' folder in different languages
Инструменты
- Исправленное дерево культур на основе примера
Автор: vlio