![]() |
Заложить основу мультиязычности
Вопрос к опытным коллегам (хотя какой я вам пока коллега :confused:).
Продумывая архитектуру будущей игры столкнулся с ещё одним вопросом, который, как мне кажется, нужно решать уже сейчас. Если я не скисну и доведу свой дебютный проект до релиза, то этот релиз должен быть точно двуязычным: на русском и английском языках, иначе ловить точно нечего. Вопрос о реализации многоязычности. На концептуальном уровне всё вроде бы понятно. Будущая многоязычность моего приложения основывается на следующих принципах: 1. Никаких жёстко заданных текстов внутри кода. Только идентификаторы, по которым будут подбираться текста на выбранном пользователем языке; 2. Все тексты живут в неких "накопителях". У меня пока это "классы-хранители" со статическими методами и переменными типа Object, хранящими тексты по строковым идентификаторам. В дальнейшем буду думать, что с ними делать: менять на XML или ещё что-нибудь, пока это представляется несущественным; 3. Между игровой механикой и готовыми текстами, живущими в классах-хранителях, должна быть ещё отдельная языковая логика. Например, для русского языка требуется подбирать слова в нужных падежах, а в английском - нет. Таким образом, просто выдёргивать по одним и тем же идентификаторам строковые переменные из идентичных хэшей точно не получится. Значит между игровой механикой и "складом" строковых переменных должны быть ещё некие "посредники", осуществляющие эту самую языковую логику. Соответственно, по первому пункту вопросов особенно нет, всё ясно. Со вторым пока ничего не ясно, было бы интересно узнать для общего развития, но не к спеху. А вот по третьему огромная просьба помочь. Стал прикидывать, как сделать... Это может быть один класс для всех языков, но запускающий разные методы, в зависимости от выбранного языка. Или базовый класс и классы-наследники для каждого из поддерживаемых языков, переопределяющие методы. Может быть вообще пространства имён или ещё какая-нибудь экзотика. Пока ограничился тем, что отделил игровую механику от подбора текстов, плюс создал пакет language, куда скинул эти самые классы-хранители и классы-"подбиратели" текстов. В общем виде вопрос такой. О чём следует подумать и что сделать с самого начала разработки, чтобы заложить хорошую основу для будущей многоязычности? Наверняка есть общая практика и отработанные решения. Может ссылочку подбросите, сам ничего путного не нашёл, увы. Заранее спасибо. |
Мне кажется ,ты сам себе проблему придумываешь в 99% случаев форма слов известна заранее и не меняется. Максимум для числительных две формы задавать надо:lives_single/lives_plural. Но оно и для англ. локали требуется.
Только если ты не делаешь супер рпг, которая должна будет генерить диалоги на лету. |
Цитата:
Но в общем, ты выбрал правильный подход. Только на формы слов действительно имеет смысл забить. Для игры это значения не имеет, как у же сказал undefined |
Цитата:
Цитата:
|
это просто сокращение от internationalization. i и n первая и последняя буквы, а 18 - количество букв между ними. Есть еще l10n - localization
|
используй по возможности текста со всеми комбинациями, если есть огромные комбинаций (невозможно обработать вручную) используй выражения в которых постановочные слова используют в именительном падеже
вместо: Изготовьте %item%, чтобы выполнить задание так: Изготовьте предмет %item%, чтобы выполнить задание практика показывает что квадратичные комбинации не составляют проблем для описания каждой, зато выражение более лаконичны |
Цитата:
|
Друзья, хочу обновить тему. Почитал я то, что нашёл на родном языке по i18n, спасибо за наводку, но в подавляющем большинстве статей описываются лишь общие подходы. Удалось почерпнуть оттуда, что, например, помимо непосредственно языка не следует забывать о форматах дат и времени, валюте и т.п. Это всё прекрасно и понятно.
Из своих собственных изысканий пока пришёл к тому, что как и планировал изначально, полностью отделил игровую механику от текстов и вообще языка, создал классы-хранители с ассоциативными массивами, забитыми текстом, а между ними добавил классы-посредники с языковой логикой. Причём все обращения к классам-хранителям строятся с помощью методов с идентичной сигнатурой: Id-шник на входе, переменная типа String на выходе - ничего лишнего. Вопросы у меня следующие: 1. Насколько я разобрался (пока только теоретически и в самых общих чертах) в интерфейсах, есть такое ощущение, что использование интерфейсов в частности помогает стандартизировать обмен информацией с классами в программе. Я правильно представляю, что мой пример с языком - это тот случай, где было бы оправдано внедрение единого интерфейса для всех классов, работающих с хранением и выдачей текстов? Или для разработчика-одиночки это будет ненужным усложнением (типа, держать в голове установленный самому для себя стандарт и не париться)? 2. Всё равно остаётся много вопросов не по общей организации (что нужно интернационализировать), а по практической реализации мультиязычности в приложении. Кто делал, поделитесь опытом плиз или кусок кода приведите, если не секрет. Мучаюсь выбором, то ли оставлять единые классы с языковой логикой, но внутри их писать отдельные методы для каждого языка, то ли отдельные классы делать... То же самое и с хранением инфо о выбранном языке: просто в виде идентификатора в каком-нибудь классе типа Config, или в виде пространства имён, чтобы потом писать методы с одинаковыми названиями, но вызывать с помощью пространства имён нужную версию. В общем, просьба помочь советом. 3. Есть понимание, что как уже было замечено многими коллегами здесь на форуме, хранить тексты в коде - не дело. Действительно, не дело. Думаю переходить к XML, чтобы и дополнительные языки подключались. А вот как это практически сделать, не понимаю? От слова совсем. Вопрос загрузки и чтения данных из файла XML не стоит - это много где описано с примерами. А вот как создавать сам файл и набивать туда данные? Руками это не делается. Значит, нужно писать отдельную программу или дополнительные классы в своей программе, которая будет из моих статических таблиц с идентификаторами "забивать" строковые значения в XML. Так? А если потом ещё какие-то текста добавить нужно... Совсем нет понимания технологии такой работы. |
1>>Интерфейсы могут быть реализованы только экземплярами. Другими словами, статические методы интерфейсами не регламентируются.
2>>Пространства имен не дадут тебе возможности добавлять языки, не переписывая код. Ведь для нового языка тебе придется добавить пространство имен и соответствующие методы. Надо абстрагировать систему так, чтобы программе было совершенно фиолетово, какой язык. Ты вот приводил пример, что в английском нет падежей. Ну так это значит, что все замены будут однотипные в именительном падеже. То есть везде в тексте будет $n0, и не будет $n1...$n5. А в массиве замен будет просто одно имя John, а не Маша в шести падежах. И поэтому один и тот же алгоритм сможет обрабатывать текст и на русском и на английском. Почему? Потому что разница только в данных, и если данные Текст соответствуют данным Замены, то никаких проблем не будет. Такой же степени абстракции кода надо добиться во всех других случаях. Например, формат даты и времени тоже можно как-то единообразно зашифровать и передавать в файле языковых данных, например там <dataFormat>DD.MM.YYYY</dataFormat>. И написать парсер (мне кажется это не так уж сложно) который будет в рантайме форматировать дату-время под заданный формат. То есть постоянный код, не заменяемый с языком, способный отформатировать дату по заданному шаблону (а вариантов форматов, мягко говоря, совсем немного). 3>>Не совсем понял, что значит "руками это не делается"? При чем тут "статические таблицы"? А их кто писал? Не руками? Как-то вообще непонятно, что ты имеешь ввиду. Я не делал текстовых квестов. Возможность смены языков я делал для приложения типа хранилища и редактора записок, то есть там смена языка только в интерфейсе. Для каждой фразы имеется идентификационный номер. В коде, когда надо вывести текст, идет обращение к классу Language и вызывается его статический метод getText(id:uint):String. Этот класс в начале программы загружает XML, в котором все фразы хранятся со своими идентификаторами, например в Eng.xml так: <phrase id="123684">The File $&fileName is broken.</phrase> а в Rus.xml так: <phrase id="123684">Файл $&fileName поврежден.</phrase> Как программа узнает, какой файл языка загрузить и применять? Его название хранит config.xml, который программа загружает самым первым и из него берет пути к остальным настроечным файлам — теме оформления интерфейса, истории открывавшихся файлов, файлу языка. То есть, по-порядку: программа загружает config.xml в нем указано <language>Rus</language> программа активирует класс Language, передавая ему путь загрузки "Rus.xml" Language открывает этот файл и считывает его в объект XML программа двигается дальше, отрисовывает интерфейс, периодически обращаясь к Language.getText(#) — ей не надо ничего знать о том, какой там на дворе язык: Language даст ей фразу на нужном языке и дату/время в нужном формате (шаблон русского формата указан в Rus.xml) |
Цитата:
Цитата:
Цитата:
Цитата:
|
В моем случае речь шла об Air-приложении (тебе тоже стоит копать в Air)
В Air после установки приложения автоматически создаются директории, связанные с этим приложением. Есть "пользовательская" директория в "Моих документах", есть папка в которую установилась программа (но файлы в ней не могут быть изменены) и папка для специальных файлов программы, которые можно перезаписывать — в ней соответственно и размещается config.xml и дополнительные папки для языков и тем. Пользователь, конечно, "меняет" не в конфиге, ему программа предоставляет интерфейс для настроек, где можно выбрать тему и язык. И тогда да, программа перезаписывает измененный конфиг — на выходе из приложения. А вот языковые файлы придется менять вручную, открыв например русский и переводя каждую фразу на нужный язык, затем сохранить с новым именем, открыть лежащий в папке языков файл со списком доступных языков и внести в него этот новый язык (чтобы программа могла его предложить пользователю). |
Цитата:
Цитата:
|
XML дюже избыточен.Большие конфиги лучше хранить в JSON, который можно генерить прямо из кода.
|
Цитата:
undefined, чем это лучше, хранить на диске конфиг в JSON? И фраза как-то странно построена, как будто XML нельзя "генерить прямо из кода". |
Цитата:
Цитата:
Цитата:
|
Цитата:
Цитата:
Мне пока видятся такие принципиальные варианты. Можно сделать один файл, загрузить его целиком в переменную прямо при запуске приложения и дальше работать только с ней, не обращаясь более у исходному файлу. Или наоборот для получения нужной фразы каждый раз обращаться к файлу-хранилищу по новой. Или сделать некий промежуточный вариант, добавив в иерархию XML-файла дополнительный уровень для, например, каких-то относительно независимых кусков игрового процесса (отдельный пакет фраз для меню, для каждого квеста, встроенных мини-игр и т.п.), чтобы обращаться к файлу при запуске каждого "куска", и забирать только тексты, связанные с ним. Спасибо. |
Я бы не работал напрямую с хмл, перегнал бы все в словарь какой-нить [ключ]-[значение] и тягал бы из него по мере надобности.
Добавлено через 5 минут Цитата:
|
Appleman, xml уже мало кто использует. Json намного удобней и компактней, и работать с ним проще. В as3 есть нативная серилизация\десирилизация json.
Как раз с json вот это: Цитата:
|
Цитата:
|
Цитата:
Цитата:
|
Цитата:
Цитата:
|
https://pp.userapi.com/c639118/v6391...ff9YSWSxKE.jpg
А я не стесняюсь работать напрямую с XML. (здесь названия элементов соответствуют id элементов на странице, и скрипт затем подставляет в них содержимое) |
Потихоньку дополз до реализации некоторых моментов, описанных уважаемым wolsh. Теперь прошу пояснить кое-что по мелочи.
Цитата:
Цитата:
и сохранит в статическую переменную, то в дальнейшем мы всегда сможем обратиться к этой переменной за фразой? Или нет? Цитата:
|
Цитата:
Код AS3:
Код:
<?xml version="1.0" encoding="utf-8" ?> |
рекомендую значения заворачивать в CDATA
|
Цитата:
Цитата:
И по коду вопросы: Код AS3:
Код AS3:
|
1.
Код AS3:
3. FileManager.getXML(languageFile) возвращает объект XML. Далее идет синтаксис е4х: .language запускает поиск всех узлов language, и возвращает (внимание!) XMLList (в Adobe ж не знают, что узел language у меня один). Но мне нужен не XMLList, а просто один узел language в виде XML-документа. Поэтому из "всего" списка узлов language я беру первый (нулевой). То есть [0] это индекс узла в списке XMLList. Добавлено через 23 минуты Предвидя вопрос "а зачем тогда ты вообще делал такую структуру <data><language/></data>" отвечаю : всё просто, в ФАЙЛЕ может содержаться также информация, относящаяся только к файлу, а не к "языку": автор перевода, версия, дата и т.п. Это будут узлы одного уровня с <language/>. |
Wolsh, большое спасибо. Если не возражаешь, возьму за основу в свой проект. У меня почему-то "не стоит" на все эти сервисные функции типа чтения файлов.
Цитата:
Вопрос у меня другой. Во всех книжках на загрузке файлов стабильно видел прикрученное событие, которое давало "отмашку", что файл загружен и с ним можно работать. Почему у тебя в файл-менеджере нет ничего подобного? |
Потому что AIR позволяет открывать или сохранять файлы синхронно. То есть исполнение дальнейшего кода останавливается и ждет, пока операция выполнится. Поэтому события типа COMPLETE не нужны. Но при желании конечно можно работать асинхронно "по старинке", для этого тоже есть методы (и свои плюшки типа более полного контроля процесса с ловлей ошибок и событий).
|
Друзья, пардон, здесь был ещё один нубский вопрос, но я разобрался сам.
|
Цитата:
err.getStackTrace() От этого будет больше пользы. По крайней мере узнаешь, что на самом деле провоцирует ошибку. Скорее всего ты что-то не то передаешь в метод getXML(). Что выдает этот трейс? trace (file.nativePath); п.с. Код AS3:
|
Код AS3:
|
Цитата:
|
Пожалуй опишу свое видение сабжа...
Обычно создаю один файл с несколькими локалями, с примерной структурой типа: Код:
<string id="" en='' ru=''/>Для удобства я написал пару классов которые отвечают за локализацию: 1. Отвечает за глобальную поддержку локали (хранит переменную текущей локали, при смене генерирует событие смены языка, видим глобально) 2. Транслятор. Отвечает за смену интефейса и отдает нужную строку в зависимости от глобальной локали. 3. Event локали. Самый важный компонент - это Транслятор, именно через него проходят все строки... Так же он хранит в себе все, что нужно поменять в интерфейсе в данный момент. Реализовано это примерно так: Чтобы вывести строку в textfield, я предварительно регистрирую этот textfield у транслятора и если в рантайме происходит смена языка и этот зарегистрированный textfield находится в списке отображения, его свойство text меняется на текст соответствующей локали. Впрочем, вместо texfield может быть любой объект DisplayObject с любым именем свойства. Так же можно запросить строку определенного id с нужной локалью. Собственно такая система решила большинство проблем с локализацией. |
Цитата:
Цитата:
PHP код:
Цитата:
ИМХО, при добавлении новых языков ваш код станет нечитаем. Ладно когда языка всего 2, но в наше время этого мало. Вообще не понимаю, зачем так делать. Один раз написал XML для русского, потом Сохранить как, и перевёл на инглиш, потом сохранить как, и перевёл на немецкий. Удобно, красиво. |
Цитата:
Перепутать id даже в трех файлах довольно легко... Переводчик мог просто запариться и перепутать id (он же гуманитарий скорее всего :) ) и вместо "Привет" на другом языке у вас может быть, к примеру, "Exit" Код:
RU_file:Цитата:
Цитата:
|
Цитата:
А в теме, очевидно, речь идет об игре, для которой язык можно сразу установить равным языку устройства. Цитата:
Код AS3:
п.с. Я тоже переводчик по образованию, но это не мешает мне быть программистом ;) |
Код:
Не должен. Его можно так же "на лету" подгрузить. Собственно, обычно так и делают.Цитата:
Цитата:
Цитата:
Код:
<string id="hello"> |
Цитата:
Цитата:
Проверять вот таким образом даже 100 строк в трех языках, тоже совсем не камильфо. Если уж на то пошло, то гораздо лучшим вариантом будет сделать небольшую утилитку, которая будет брать по порядку ID и для каждого языка выводить все переводы на экран рядом, без лишних тегов. Но хранить все переводы в одном файле - плохо, особенно для мобильников. Подгрузка нового файла с переводом, с локального диска, даже на слабом устройстве займет несколько милисекунд. Для человека это будет мгновенно + лишнюю память жрать не будет. |
PHP код:
К тому же, это загрузка текстового файла, она выполняется за доли секунды даже со слабейшим интернетом(может занять дольше, только если это мобильный инет) Тут скорее надо переживать за скорость доставания данных из XML документа, которая при включении всех языков в один документ будет ниже. |
| Часовой пояс GMT +4, время: 11:56. |
Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.