Подтвердить что ты не робот

Методы использования локализованных текстов в кросс-платформенных приложениях С++?

В текущем стандарте С++ (С++ 03) слишком мало спецификаций о локализации текста, что делает работу разработчика С++ более сложной, чем обычно, при работе с локализованными текстами (конечно, стандарт С++ 0x поможет здесь позже).

Предполагая следующий сценарий (который из реальных случаев разработки игр для ПК-Mac):

  • приложение для реагирования (в реальном времени): приложение должно минимизировать время без реагирования на "не заметное", поэтому важна скорость выполнения.
  • локализованные тексты: отображаемые тексты локализованы на более чем двух языках, потенциально больше - не ожидайте фиксированного количества языков, должны быть легко расширяемыми.
  • язык, определенный во время выполнения: тексты не должны компилироваться в приложении (и не иметь одного приложения на язык), вы получаете информацию о выбранном языке при запуске приложения - что подразумевает некоторую загрузку текста.
  • кросс-платформенный: приложение кодируется с учетом кросс-платформенной (Windows - Linux/Ubuntu - Mac/OSX), поэтому локализованная текстовая система также должна быть перекрестной платформой.
  • автономное приложение: приложение предоставляет все, что необходимо для его запуска; он не будет использовать какую-либо библиотеку среды или требует, чтобы пользователь установил что-либо, кроме ОС (например, большинство игр).

Каковы наилучшие методы управления локализованными текстами на С++ в этом приложении?

В прошлом году я заглянул в это, и единственное, что я уверен в том, что вы должны использовать std::wstring или std::basic_string<ABigEnoughType> для управления текстами в приложении. Я прекратил свое исследование, потому что больше работал над проблемой "текстового отображения" (в случае 3D в реальном времени), но я думаю, что есть некоторые рекомендации по управлению локализованными текстами на необработанном С++ за пределами этого и "использование Unicode",.

Итак, все лучшие практики, предложения и информация (кросс-платформенная делает это сложно, я думаю) приветствуются!

4b9b3361

Ответ 1

В небольшой компании видеоигр, Black Lantern Studios, я был ведущим разработчиком игры под названием Lionel Trains DS. Мы локализовались на английском, испанском, французском и немецком языках. Мы знали все языки впереди, поэтому включение их во время компиляции было единственным вариантом. (Они сжигаются в ПЗУ, вы видите)

Я могу дать вам информацию о некоторых вещах, которые мы сделали. Наши строки были загружены в массив при запуске на основе выбора языка для игрока. Каждый отдельный язык переходил в отдельный файл со всеми строками в том же порядке. Строка 1 всегда была названием игры, строка 2 всегда была первой опцией меню и так далее. Мы привязали массивы к enum, так как индексация integer очень быстрая, а в играх скорость - это все. (Решение, связанное в одном из других ответов, использует поиски string, которые я бы избегал.) При отображении строк мы использовали функцию типа printf() для замены маркеров значениями. "Поезд 3 покидает город 1".

Теперь для некоторых подводных камней.

1) Между языками порядок фразы совершенно другой. "Поезд 3 покидает город 1." переведенный на немецкий язык и обратно, заканчивается тем, что "из города 1, поезд 3 уходит". Если вы используете что-то вроде printf(), а ваша строка: "Поезд% d отправляется в город% d". немец в конце концов скажет: "Из города 3, поезд 1 уходит". что совершенно неверно. Мы решили это, заставив перевод сохранить тот же порядок слов, но мы закончили с довольно сломанным немецким. Если бы я сделал это снова, я бы написал функцию, которая берет строку и нулевой массив значений, которые нужно положить в нее. Затем я буду использовать маркеры типа %0 и %1, в основном встраивая индекс массива в строку. Обновление: @Джонатан Леффлер отметил, что совместимый с POSIX printf() поддерживает использование маркеров типа %2$s, где часть 2$ указывает printf() заполнить этот маркер вторым дополнительным параметром. Это было бы очень удобно, если бы оно было достаточно быстрым. Специальное решение может быть еще быстрее, поэтому вам нужно убедиться и проверить оба.

2) Языки сильно различаются по длине. Что было 30 символов на английском языке, иногда приходилось до 110 символов на немецком языке. Это означало, что это часто не соответствовало экранам, на которых мы его снимали. Это, вероятно, меньше беспокоит игры для ПК /Mac, но если вы выполняете какую-либо работу, где текст должен вписываться в определенную ячейку, вы захотите это рассмотреть. Чтобы решить эту проблему, мы разделили как можно больше прилагательных из нашего текста для других языков. Это сократило предложение, но сохранило смысл, если потерять немного вкуса. Позднее я разработал приложение, которое мы могли бы использовать, которое будет содержать шрифт и размер коробки, а также позволить переводчикам внести свои собственные изменения, чтобы текст вписывался в поле. Не уверен, что они когда-либо реализовали его. Вы можете также рассмотреть возможность прокрутки областей текста, если у вас есть эта проблема.

3) Что касается кросс-платформы, мы написали довольно чистый С++ для нашей системы локализации. Мы написали пользовательские закодированные бинарные файлы для загрузки и пользовательскую программу для преобразования из CSV текста языка в .h с перечислением и файлом на языковой карте и .lang для каждого языка. Самой конкретной платформой, которую мы использовали, были шрифты и функция printf(), но у вас будет что-то подходящее для того, где бы вы ни разрабатывались, или можете писать свои собственные, если это необходимо.

Ответ 2

Я категорически не согласен с принятым ответом. Во-первых, часть, касающаяся использования статических поисков в массиве для ускорения поиска текста , является контрпродуктивной преждевременной оптимизацией - вычисление макета для указанного текста и отрисовка указанного текста занимает на 2-4 порядка больше времени, чем поиск в хэше. Если кто-то хочет реализовать свою собственную языковую библиотеку, она никогда не должна основываться на статических массивах, потому что это меняет реальные преимущества (переводчикам не нужен доступ к коду) для мнимых преимуществ (увеличение скорости на ~ 0,01%).

Далее, написание собственной языковой библиотеки для использования в собственной игре еще хуже, чем преждевременная оптимизация. Есть несколько веских причин, по которым никогда не пишите свою собственную библиотеку локализации:

  1. Планирование времени использования существующей библиотеки локализации намного проще, чем планирование времени для написания библиотеки локализации. Библиотеки локализации существуют, они работают, и многие люди использовали их.

  2. Локализация сложна, так что вы все поймете неправильно. Каждый язык добавляет новую причуду, что означает, что всякий раз, когда вы добавляете новый язык в свою собственную доморощенную библиотеку локализации, вам нужно будет снова изменить код, чтобы учесть причуды. Знаете ли вы, что в некоторых языках имеется более 2 множественных форм, в зависимости от количества рассматриваемых предметов? Более 2 полов (более 10, даже)? Кроме того, форматы чисел и даты сильно различаются для разных языков.

  3. Когда ваше приложение станет успешным, вы захотите добавить поддержку большего количества языков. На языках никто в вашей команде не говорит свободно. Нанимать кого-то для написания перевода будет значительно дешевле, если они уже знают инструменты, с которыми работают.

Очень известной и полной библиотекой локализации является GNU Gettext, которая использует GPL, и поэтому ее следует избегать для коммерческой работы. Вместо этого вы можете использовать библиотеку надстройки boost.locale, которая работает с файлами Gettext и может свободно использоваться и модифицироваться для коммерческих и некоммерческих проектов любого рода.

Ответ 4

В стандарте С++ 0x не будет никаких дополнительных возможностей, насколько я могу судить. Я подозреваю, что Комитет считает это вопросом для сторонних библиотек.