Comments 35

2018 год на дворе, а люди по-прежнему занимаются копи-пастингом переводов из/в CSV и Excel.


Существуют же gettext а также миллиард программ и сервисов для перевода файлов gettext. Строки автоматически собираются из исходного кода (никто не должен вручную проверять и вносить правки при изменении кода/ресурсов), переводчики сразу видят строки, которые надо перевести, переводы валидируются (например, чтобы не возникало ошибок из-за опечаток в названиях placeholder'ов, таких как Сишные %s, %d), после перевода все переведенные строки автоматически доступны приложению.


Для перевод po файлов существует просто прорва онлайн сервисов, GUI программ, self-hosted сервисов (я, например, использую сейчас Weblate).


Как при всем таком многообразии инструментов, которые значительно автоматизируют и облегчают процесс, рекомендовать до сих пор CSV и Excel?.. Неужели Unity не поддерживает ничего лучше?

Наверное в играх строк немного. Просто я работал в проектах, где локализация производится через Excel. Это просто средневековье какое-то. Людям приходится вручную заниматься монотонным копированием строк туда-сюда, в котором очень легко ошибиться. Неблагодарная тяжелая работа, которая элементарно автоматизируется, и решения для автоматизации придуманы очень давно (GNU gettext появился в 1995 году).

Стоит также учесть специфику разработки. Если вы начинающий инди-разработчик, который пришел к Unity именно из-за низкого порога входа и C#, то вряд ли вам нужно все то, о чем вы рассказываете. Мне, например, определенно хватает и CSV.

Так в gettext ничего сложного нет. Строки помечаются в исходном тесте (чаще всего используется синтаксис _("строка")). Утилиты gettext сканируют исходный текст, находят отмеченные строки и записывают в .po файл.


.po файл можно переводить вручную, как тот же csv, можно преобразовать в csv, можно редактировать GUI редактором (существует множество), можно отдать на аутсорс, можно загрузить в онлайн-сервис наподобие OneSky, можно установить свой веб-сервис, такой как Weblate (у Weblate есть еще не менее 5 аналогов).


Затем полученный переведенный файл распространяется вместе с продуктом, и во всех местах вызов gettext (_("строка")) подставляет переведенный текст. Все, кроме собственно перевода, полностью автоматизировано. Перевод, в зависимости от выбранного решения, тоже можно полностью или частично автоматизировать.


Один раз настраивается, затем все происходит автоматически. Один раз попробовав, уже не захочется ничего другого.


Ну а CSV я тоже работал, конечно. Когда строк больше сотни, тут уже на каждой итерации требуется значительное количество ручного труда.

Обычно фиксированных строк в геймдеве мало и они пробиваются не в коде, а в метаданных сцены юнити (можно принудительно заставить сериализовать эти данные в YAML, а потом долго и мучительно выковыривать их, но...), большая же часть собирается динамически и практически никогда не забивается строками на «original»-языке в коде, а сразу едет как токен в локализацию. Т.е сканировать как бы и нечего.
Тут фишка в том, что CSV можно использовать не только как данные локализации, но и как таблицы для баланса игровой механики — неплохая замена ScriptableObject и не завязано на юнити и ее апдейты, которые всегда-что-то ломают.
Ох, внешние параметры для игры — это отдельная тема. Но Unity работают над этим, может что-то и выйдет в ближайшее время.
Ну вот простой пример.
Задаются ограничители данных, списки выбора и т.п. Первая строка — имена JSON-полей, вторая строка — символы для экранирования (по сути определяют тип данных). От дизайнера защищены как 2 первые строки, так и первый столбец. Это все дампится через CSV, потом автоматически конвертится в JSON и складывается в Resources. В рантайме уже происходит загрузка и маппинг прямо на внутренние классы через JsonUtility или что-то иное.
Я раньше делал еще проще — хранил сериализованный конфиг игры. А игра еще и умела его подгружать в процессе, чтобы билды новые не делать из-за изменения баланса. Но это уже совсем другая история)
Ну тут смысл такой же — дизайнеру даже не нужно иметь установленную юнити, можно прикрутить подгрузку / парсинг в рантайме. Ну и совместное редактирование с историей правок и возможностью применения формул для балансировки.
А мы подняли сервер на гуглокоде, который через api docs по схожему принципу вытаскивает данные и просто отдает JSON в Unity, где его из промежуточного формата (если требуется) превращают в ScriptableObject.
Тоже вариант, хотя и более тяжелый по реализации и поддержке. Основной посыл был — избавиться от необходимости крутить циферки в юнити и рисования кастомных инспекторов. Ну и если потребуется — это все можно провернуть на лету, немного подхачив код, тогда вообще можно будет подсасывать данные прямо в билде. Со SO такое невозможно.
Реализации да, поддержка — нет. Один раз настроил и работает. У каждой таблицы добавляется еще одна вкладка Schema — которая описывает формат JSON который нужно сформировать для Unity.
Но там без связей. И вот связи приходится на клиенте восстанавливать. Инспектор кастомный делать не нужно, кнопку скачивания можно вынести в меню.
То что надо на лету подсасывать, это уже должно быть из нормальной базы данных с нормальным бекэндом. Api гугла медленное, чтобы не злоупотребляли. Для ГД еще можно сделать, для игроков — нет. Поэтому скрипты для работы я в Editor засунул.
Я про поддержку апи гугля — оно может меняться и за этим нужно следить. А стаскивание данных по прямому урлу работает с момента существования гуглодоков. Работает довольно быстро и без временного лага до 30 минут, как в в штатной публикации в веб. Понятно, что это не для продакшна, но для быстрого тестирования у дизайнеров / внутренних тестировщиков с изменением циферок на лету — вполне сойдет.
За год не сталкивался с такими изменениями. Поживем увидим :). Главное чтобы данные не пропали, а как их достать — всегда можно придумать.
Как обрабатываются многострочные ячейки? Имеется ввиду строка, в которой прямо внутри набиты переводы строк (не через пару символов "\n"). Такие csv тоже являются, как ни странно, валидными, пусть и строка таблицы разбита в файле на несколько подстрок.
У меня не было проблем с ними. Ведь служебные символы так и переезжают в локализацию, где их рендерит компонент Text.
Это проблема парсинга таких CSV, это специальный кейс, который нужно обрабатывать — это делается?
Да, только кавычки остаются. Но код открытый, простой и будет только рад допиливанию)
Кстати, переносы строк экранируются двойными кавычками при скачивании CSV. Так что проблем не будет. Главное, чтобы парсер корректно это прочитал. Мой парсер такое обрабатывает, но оставляет кавычки. Исправлю как нибудь)
Т.е есть уже один известный баг. :)
А вообще, тема довольно старая и уже деланная-переделанная много раз, даже на хабре были ссылки.
*) Локализатор: поддерживает загрузку фиксированных данных, а так же «динамических» (можно подгружать / выгружать с возможностью переопределения ключей).
*) Локализация компонента Text uGui: поддерживает автоматическое обновление при смене языка в локализаторе.
*) Локализация компонента Image uGui: поддерживает автоматическое обновление при смене языка в локализаторе.
*) Скачивание нескольких CSV-файлов с сохранением url / путей: поддерживает конвертацию в JSON-array / JSON-dict.
*) Скачивание нескольких CSV-файлов в виде отдельного репозитория без зависимостей.
Видел и статьи, и гитхабы. Но хочется же ничего не делать, чтобы оп — и готово. Пришлось делать самому.
А как решается проблема, когда нужно изменение самого UI? Например, на разных языках размер элемента должен быть разным.
Ну есть же всякие ContentSizeFitter и HorizontalLayoutGroup. Хотя я обычно просто устанавливаю динамичный размер текста, чтобы он вписывался в размер элемента.
Если вы используете Best Fit для текстовых компонентов, то он плохо сказывается на производительности, особенно если у вас есть какие-то прокручиваемые списки с кучей текстов
Я человек простой. Если пользователи жалуются на тормоза — я буду искать причину. В остальное время я нацелен на конечный результат. В процессе работы меня мало интересуют нюансы реализации локализации и специфика работы uGUI. Главное, чтобы заработало побыстрее. Все эти паттерны, рефакторинги и оптимизация — кому они нужны, если проект не зашел? Да, есть и такие, кто считает, что в паттернах и всяких крутых вещах, вроде gettext, и есть суть разработки. Их право.
Те которые жалуются, это лишь малая, большинство недовольных просто закрывают апп и удаляют. И вы даже не узнаете причину.
Есть и другая точка зрения. Если приложение или игра того стоит — то буду жаловаться. А если нет, то оптимизация не поможет.

Кроме того, аналитику и Performance Reporting никто не отменял.
CSV (Comma Separated Values). Это текстовый файл, в котором все ячейки разделены разделителем — либо запятой (","), либо точкой с запятой (";")


Наличие точки или точки с запятой в самом тексте в одной из ячеек не порушит структуру файла?
Only those users with full accounts are able to leave comments. Log in, please.