Пользователь
5,6
рейтинг
18 января 2013 в 02:25

Разработка → Локализация и глобализация

В статье автор пытается очертить некоторые необходимые для понимания аспекты интернационализации в .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.CurrentThread.CurrentUICulture. UI-культура может быть как специфичной, так и нейтральной. А вот Thread.CurrentThread.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


Статьи


Инструменты


Владислав @vlio
карма
15,0
рейтинг 5,6
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (16)

  • +2
    А как обрабатывается ситуация, когда пользователь в системный настройках выбрал страну и язык, а потом заменил запятую на точку?
    • +1
      О том, что культура может не соответствовать дефолтным настройкам, сигнализирует свойство CultureInfio.UseUserOverride. Этот же параметр отражен в перегрузках конструкторов CultureInfo. Для культур потока значение true, а, например, для полученных через CultureInfo.GetCultureInfo(String)false.
      • 0
        Ага. А всё же вопрос — есть некая системная локаль (культура), пользователь переопределил в её свойствах запятую/точку. Запускаем программу. Что будет использоваться в программе по-умолчанию?
        • 0
          Предполагаю, что пользовательское. Так как UseUserOverride, по умолчанию, true.
        • 0
          Если самому не переприсваивать CurrentCulture потока, то культура будет соответствовать настройкам Windows. И в .NET мы тоже получим, например, точку вместо стандартной запятой.
    • 0
      Как раз сегодня столкнулся при парсинге числа из строки в double, что надо не запятую, а точку :)
      • +2
        Тоже сталкивался с такой проблемой и в инете нашёл решение.

        string s = s.Replace(",", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator);
        decimal count = decimal.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
        
  • –14
    Почему в .NET нет культуры MD? Потому что это расшифровывается как Must Die:)
  • +2
    Environment.NewLine

    Так вот откуда его брать! Спасибо!
    • +4
      Там еще много всякого, рекомендую заглянуть в сам класс :)
  • +4
    А я помню багу в одном разрабатываемом на .net продукте, которая проявлялась только при использовании турецкого языка.
    Суть баги в том, что в турецком языке есть две буквы «i» — одна с точкой и одна без точки. Поэтому если например делать toUpper для маленькой английской буквы «i», то в результате получится большая турецкая буква "İ" с точкой.
    В результате все методы сравнения строк в которых использовалось изменение регистра давали ложные срабатывания.
    Бага фиксается путем замены методов сравнения на специальные, которые используют invariant culture, но тогда конечно об этом никто не думал.
  • 0
    Владислав, плюсую!
  • +2
    Если ваш продукт очень тесно связан с глобализацией и локализацией, то скорее всего вы подписаны на уведомления об изменениях в стандарте www.iso.org/iso/home/standards/country_codes.htm
    За последнее время Microsoft сильно причесала информацию по языкам, переименовала страны, вынесла из названия Latin и Cyrillic, добавила много новых культур (Windows 7). В .Net с этим всё попроще, а вот в плюсах приходится иметь дело с LCID и там всё куда запутанней. Одно разделение хорватского на три языка (primary language остался одинаковым) столько геморроя создало. Сейчас существует уже несколько языков у которых название зависит от страны Sami, Norway Bokmal, Upper Sorbian. Так что приходится постоянно следить, чтобы языки в интерфейсе отображались адекватно и группировка по primary language ничего не сломала.
    • 0
      Спасибо, теперь подписан.
  • +1
    > Да, код региона был MO, а не MD как сейчас. Стандарт ISO поменялся?

    Да, в 2008 году из ISO убрали молдавский, как отдельный язык.
    www.loc.gov/standards/iso639-2/php/code_changes.php

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.