Зачем нам нужна библиотека локализации, когда стандартные грани C++ (должны) обеспечивают большую часть требуемой функциональности:
Преобразование дела осуществляется с использованиемstd::ctypeграна
Коллекция поддерживаетсяstd::collateи имеет хорошую интеграцию сstd::locale.
Естьstd::num_put,std::num_get,std::money_put,std::money_get,std::time_putиstd::time_getдля чисел, времени и валютного форматирования и парсинга.
Существует классstd::messages, который поддерживает локализованное форматирование сообщений.
Так зачем нам такая библиотека, если у нас есть все функции в стандартной библиотеке?
Почти каждый (!) грань имеет конструктивные недостатки:
std::collateподдерживает только один уровень сопоставления, не позволяя выбрать, следует ли проводить сравнения, чувствительные к случаю или акценту.
std::ctype, который отвечает за преобразование случая, предполагает, что все преобразования могут быть сделаны на основе одного символа. Это, вероятно, верно для многих языков, но в целом это неправильно.
Преобразование корпуса может изменять длину строки. Например, немецкое слово «grüßen» должно быть преобразовано в «GRÜSSEN» в верхнем случае: буква «ß» должна быть преобразована в «SS», но функцияtoupperработает на основе одного символа.
Конверсия контекста чувствительна. Например, греческое слово «.ΔγΣΣΕ.Σ» должно быть преобразовано в «.δυσσεύς», где греческая буква «Σ» преобразована в «σ» или в «ς», в зависимости от его положения в слове.
Преобразование в случае не может предполагать, что символ является одной точкой кода, что неверно как для кодировок UTF-8, так и для кодировок UTF-16, где отдельные точки кода представлены до 4charили двумяwchar_tна платформе Windows. Это делаетstd::ctypeсовершенно бесполезным с этими кодировками.
std::numpunctиstd::moneypunctвообще не указывают кодовые точки для представления цифр, поэтому они не могут форматировать числа с цифрами, используемыми под арабскими локализациями. Например, число «103» должно отображаться как «.» вar_EGместоположении. std::numpunctиstd::moneypunctпредположим, что тысяча сепараторов — единый характер. Это неверно для кодирования UTF-8, где только диапазон Unicode 0-0x7F может быть представлен как один символ. В результате локализованные числа не могут быть правильно представлены в местах, которые используют символ Unicode «EN SPACE» для тысяч разделителей, таких как русский. Это на самом деле вызывает реальные проблемы в компиляторах GCC и SunStudio, где форматирование чисел под русским локалом создает недействительные последовательности UTF-8.
std::time_putиstd::time_getимеют несколько недостатков:
Они предполагают, что календарь всегда григорианский, используяstd::tmдля представления времени, игнорируя тот факт, что во многих странах даты могут отображаться с использованием разных календарей.
Они всегда используют глобальный часовой пояс, не допуская спецификации часового пояса для форматирования. Стандартstd::tmвообще не включает поле часовой зоны.
std::time_getне симметричноstd::time_put, поэтому вы не можете разобрать даты и времена, созданныеstd::time_put. (Этот вопрос рассматривается в C++0x и некоторых реализациях STL, таких как стандартная библиотека C++ Apache.)
std::messagesне обеспечивает поддержку форм множественного числа, что делает невозможным правильную локализацию таких простых строк, как «В каталоге есть X-файлы».
Кроме того, многие функции на самом деле не поддерживаются std::locale вообще: часовые пояса (как упоминалось выше), анализ границ текста, написание чисел и многие другие. Таким образом, очевидно, что стандартные локализации C++ являются проблематичными для реальных приложений.
Why use an ICU wrapper instead of ICU?
ICU — очень хорошая библиотека локализации, но имеет несколько серьезных недостатков:
Он абсолютно недружелюбен для разработчиков C++. Он игнорирует популярные идиомы C++ (STL, RTTI, исключения и т. д.), вместо этого в основном имитируя Java API.
Он поддерживает только один тип строки, UTF-16, когда некоторые пользователи могут захотеть другие кодировки Unicode. Например, для XML или HTML обработка UTF-8 намного удобнее и UTF-32 проще в использовании. Также нет поддержки «узких» кодировок, которые все еще очень популярны, таких как кодировки ISO-8859.
Boost.Locale обеспечивает прямую интеграцию с iostream, что позволяет более естественно форматировать данные. Например:
Why an ICU wrapper and not an implementation-from-scratch?
ICU является одной из лучших доступных библиотек локализации/Unicode. Он состоит из около полумиллиона строк хорошо протестированного, проверенного на производстве исходного кода, который сегодня предоставляет современные инструменты локализации.
Реализация даже небольшой части способностей реанимации — это неосуществимый проект, который потребует многих человеко-лет. Таким образом, вопрос не в том, нужно ли нам заново внедрять алгоритмы Unicode и локализации с нуля, а в том, нужна ли нам хорошая библиотека локализации в Boost
Таким образом, рост. Locale оборачивает ICU современным интерфейсом C++, позволяя в будущем реализовывать детали с лучшими альтернативами, но обеспечивая поддержку локализации для Boost сегодня, а не в не столь близком будущем.
Why is the ICU API not exposed to the user?
Да, весь API ICU скрыт за непрозрачными указателями, и пользователи не имеют к нему доступа. Это делается по нескольким причинам:
В какой-то момент лучшие инструменты локализации могут быть приняты будущими стандартами C++, поэтому они могут не использовать ICU напрямую.
В какой-то момент должна быть возможность переключить базовый механизм локализации на что-то другое, возможно, нативный API операционной системы или какой-то другой инструментарий, такой как GLib или Qt, который обеспечивает аналогичную функциональность.
Не вся локализация осуществляется в рамках ICU. Например, форматирование сообщений использует каталоги сообщений GNU Gettext. В будущем больше функциональных возможностей может быть реализовано непосредственно в Boost. Местная библиотека.
Повышаю. Locale был разработан с учетом стабильности ABI, поскольку эта библиотека разрабатывается не только для Boost, но и для нужд CppCMS C++. Web Framework.
Why use GNU Gettext catalogs for message formatting?
Существует множество доступных форматов локализации. Наиболее популярными на сегодняшний день являются OASIS XLIFF, файлы GNU gettext po/mo, каталоги POSIX, файлы Qt ts/tm, свойства Java и ресурсы Windows. Однако последние три полезны только в своих конкретных областях, а каталоги POSIX слишком просты и ограничены, поэтому есть только два разумных варианта:
Стандартный формат локализации OASIS XLIFF.
GNU Gettext бинарные каталоги.
Первое, как правило, кажется более правильным решением локализации, но для загрузки документов требуется XML-разбор, это очень сложный формат, и даже ICU требует предварительной компиляции его в пакеты ресурсов ICU.
С другой стороны:
GNU Бинарные каталоги Gettext имеют очень простой, надежный и в то же время очень полезный формат файлов.
В настоящее время это самый популярный и де-факто стандартный формат локализации (по крайней мере, в мире Open Source).
Он имеет очень простую и мощную поддержку множественных форм.
В качестве ключа он использует оригинальный английский текст, что значительно облегчает процесс интернационализации, поскольку всегда доступен хотя бы один базовый перевод.
Существует множество инструментов для редактирования и управления каталогами gettext, таких как Poedit, kbabel и т. Д.
Таким образом, даже если формат каталога GNU Gettext mo не является официально утвержденным форматом файла:
Это стандарт де-факто и самый популярный.
Его реализация намного проще и не требует анализа и проверки XML.
Note
Boost.Locale does not use any of the GNU Gettext code, it just reimplements the tool for reading and using mo-files, eliminating the biggest GNU Gettext flaw at present – thread safety when using multiple locales.
Why is a plain number used for the representation of a date-time, instead of a Boost.DateTime date or Boost.DateTime ptime?
Есть несколько причин:
Грегорианец Дата по определению не может быть использована для представления локально-независимых дат, потому что не все календари являются григорианскими.
ptime– определенно можно использовать, но у него есть несколько проблем:
Он создается в GMT или локальных часах времени, когдаtime()дает представление, которое не зависит от часовых поясов (обычно время GMT), и только позже он должен быть представлен в часовом поясе, который запрашивает пользователь. Временная зона — это не свойство самого времени, а скорее свойство форматирования времени.
ptimeуже определяетoperator<<иoperator>>для форматирования и разбора времени.
Существующие грани форматирования и разбораptimeне были разработаны таким образом, чтобы пользователь мог переопределить. Основные функции форматирования и разбора не являются виртуальными. Это делает невозможным повторное выполнение функций форматирования и разбораptime, если разработчики Boost. Библиотека DateTime решила изменить их. Кроме того, граниptimeне «правильно» разработаны с точки зрения разделения информации форматирования и информации о местоположении. Форматирование информации должно храниться вstd::ios_base, а информация о локальном форматировании должна храниться в самой грани. Пользователю библиотеки не следует создавать новые грани для изменения простой форматирующей информации, такой как «отображать только дату» или «отображать как дату, так и время».
Таким образом, на данный момент ptime не поддерживается для форматирования локализованных дат и времени.
Why are POSIX locale names used and not something like the BCP-47 IETF language tag?
Есть несколько причин:
Локальные имена POSIX имеют очень важную особенность: кодирование символов. Когда вы указываете, например, fr-FR, вы фактически не знаете, как текст должен быть закодирован; UTF-8, ISO-8859-1, ISO-8859-15 или, возможно, Windows-1252. Это может варьироваться между различными операционными системами и зависит от текущей установки. Поэтому крайне важно предоставить всю необходимую информацию.
ICU полностью понимает локализации POSIX и знает, как их правильно лечить.
Они являются нативными именами для большинства API-интерфейсов операционной системы (за исключением Windows).
Why most parts of Boost.Locale work only on linear/contiguous chunks of text
Есть две причины:
Повышаю. Locale в значительной степени зависит от сторонних API, таких как ICU, POSIX или Win32 API, все они работают только на линейных фрагментах текста, поэтому предоставление нелинейного API просто скроет реальную ситуацию и не принесет реального преимущества производительности.
На самом деле, все известные библиотеки, которые работают с Unicode: ICU, Qt, Glib, Win32 API, POSIX API и другие принимают ввод как единый линейный фрагмент текста и для этого есть веская причина:
Большинство поддерживаемых операций с текстом, таких как сопоставление, обработка корпуса, как правило, работают с небольшими фрагментами текста. Например, вы, вероятно, никогда не захотите сравнивать две главы книги, а скорее их названия.
Следует помнить, что даже очень большие тексты требуют довольно небольшого объема памяти, например, вся книга «Война и мир» занимает всего около 3 МБ памяти. Однако:
Есть API, которые поддерживают обработку потоков. Например: преобразование набора символов с использованиемstd::codecvtAPI работает на потоках любого размера без проблем.
Когда в Boost вводится новый API. Локальность в будущем, такая, что она, вероятно, работает на больших кусках текста, обеспечит интерфейс для нелинейной обработки текста.
Why all Boost.Locale implementation is hidden behind abstract interfaces and does not use template metaprogramming?
Существует несколько основных причин:
Именно так строится класс C++std::locale. Каждая функция представлена с использованием подклассаstd::locale::facet, который обеспечивает абстрактный API для конкретных операций, над которыми она работает, см.Поддержка локализации стандартной библиотеки C++.
Этот подход позволяет переключать базовый API без изменения фактического кода приложения даже во время выполнения в зависимости от производительности и требований к локализации.
Такой подход значительно сокращает время компиляции. Это очень важно для библиотеки, которая может использоваться практически в каждой части конкретной программы.
Why Boost.Locale does not provide char16_t/char32_t for non-C++0x platforms.
Есть несколько причин:
C++0x определяетchar16_tиchar32_tкак различные типы, поэтому замена чем-то вродеuint16_tилиuint32_tне будет работать, как, например, записьuint16_tвuint32_tпоток будет писать число для потока.
Система локализации C++ будет работать только в том случае, если в существующий экземплярstd::localeустановлены стандартные грани, подобныеstd::num_put, однако во многих стандартных библиотеках C++ эти грани специализированы для каждого конкретного символа, который поддерживает стандартная библиотека, поэтому попытка создать новый аспект потерпит неудачу, поскольку он не является специализированным.
Именно по этим причинам Boost. Locale не работает с текущей поддержкой ограниченных символов C++0x на GCC-4.5 (вторая причина) и MSVC-2010 (первая причина)
Таким образом, в основном невозможно использовать символы, не относящиеся к C++, с фреймворком локализации C++.
Лучшим и наиболее портативным решением является использование кодировок типа char C++ и UTF-8.
Статья Boost.Locale: Design Rationale раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.