Comments 50
Open-source софтины частенько имеют вынесенные отдельно тексты, игры.
В своё время пришёл к такому же выводу, пошёл и сделал (неавтоматизированное) решение на базе Google Sheets.
опциональный платный сервис, где можно заказывать переводы, проверку орфографии и так далее.
уже предлагается каждой второй компанией по переводу и вычитке текстов.
Алконост, из тех, что публикуется на Хабре.
Ну почему же, например, в Django есть пакет локализации на основе gnu gettext — и работает это очень похоже на то, что у вас описано.
Для frontend-а первое, что приходит на ум:
- https://www.i18next.com/
- https://github.com/airbnb/polyglot.js
- https://github.com/globalizejs/globalize
либо более специфичные для фреймворков:
Для бекенда также практически для каждого фреймворка есть возможности локализации, https://laravel.com/docs/8.x/localization, например.
Эти и другие решения существуют и развиваются уже не первый год, они замечательно выполняют свои задачи, однако приносят с собой и новые. Если нужно изменить текст "только вот этой кнопки", все равно надо подключать программиста. А еще надо постоянно контролировать процент перевода в проекте, и горе тем, у кого инструменты не могут выдать статистику. Чтобы снизить количество "мусора", упоминаемого в https://habr.com/ru/post/533980/#comment_22443216, желательно озаботиться генерацией файлов перевода из исходных кодов приложения. Кроме того, неплохо иметь референсную базу, в которой будет указано, где и как использовать самые распространенные вещи. Таким образом, чтобы все хорошо работало, нужно вводить серьезные изменения в рабочий процесс многих людей. Можно вводить меньше изменений, смириться с рядом проблем, держать в голове ньюансы.
На проектах малого и среднего размера возникает вопрос — а зачем? Если дешевле сотню раз поменять текст на кнопках вручную, чем внедрять и использовать какой-нибудь крутой тул? Единственный аргумент — если гарантированно будут локализации для разных языков, построение подобных процессов снизит сложность и риски, а стандартизация и изоляция от исходного кода позволит привлекать сторонние команды для локализации приложения. Если язык только один, то порой экономический смысл в использовании инструментов локализации теряется. Однако, если приложение совсем небольшое — можно попробовать, без серьезных вложений в процессы, так как удержать в голове, где что используется, еще представляется возможным. Но с точки зрения бизнеса смысла в этом нет, только если нет запроса на локализацию продуктов и установки развивать внутренние процессы с целью удовлетворения этого запроса.
Я думаю, что даже без локализации вероятно было бы удобно. Я работаю в среднем проекте с одним языком, и регулярно бывает что 1) нетехнический сотрудник нашел ошибку и хочет поправить 2) кто-то где-то нашел опечатку или грамматическую ошибку 3) сотрудники, включая программистов, не «помнят», что мы говорим юзеру в ситуации X или Y, потому что сообщения часто меняли. То есть немного не хватает одного места, откуда можно контроллировать коммуникации с пользователями — когда и что мы говорим. Даже если это один язык, просто с несколькими важность еще выше.
Тексты могут храниться вместе с кодом, а могут загружаться отдельно, например по http, но пока не будем усложнять, рассматривая эту ситуацию. Вопрос только в том, как они в исходный код попадают. Допустим, в коде программист упоминает условный $t('some.term'). При отправке коммита в репозиторий запускается CI, происходит анализ исходного кода и происходит генерация или обновление файла определений переводов, который тут же отправляется в какую-нибудь систему управления переводами (например, https://weblate.org/). В этой системе переводчик (или любой нетехнический сотрудник с правом доступа) заполняет недостающие строки переводов (или обновляет имеющиеся). После каждого завершенного изменения автоматически происходит коммит в репозиторий исходного кода, несущий в себе готовый перевод. Эти файлы никто и никогда не правит вручную. Все решает автоматизация, и, таким образом, чтобы коммитить в код переводов, необязательно даже знать о существовании репозитория, да и исходного кода в целом. Это решает проблемы 1.1), 2.1) и 2.2)
Еще подобные инструменты, в случае с gettext например, могут показать информацию о том, где в исходном коде какой перевод используется (например, такой функционал есть в редакторе poedit), а также может помочь в анализе того, а нет ли таких же переводов, но с другим ключом. Возможно, это решает 2.3)? Хотя я не уверен, что правильно понял ситуацию.
Остается 1.2), решением которой может быть использование:
- констант (объектов и строк)
- типов (если например пишем на TypeScript)
- и того, и другого
Совершить ошибку при использовании $t(glossary.some.term) сложнее, чем при использовании $t('some.term'), так как если в "glossary" свойство не определено, это будет видно в IDE как ошибка. Бонусом идет удобный код-комплит.
Когда и что мы говорим, всегда решается в коде, ведь именно для этого он и пишется. Просто это делается не на обычном человеческом языке, но суть утверждения от этого не меняется. А система управления переводами — как раз то место, где можно перевести это "что" на язык, понимаемый человеком (даже если перевод происходит только на один человеческий язык).
В итоге получаем то, что описано в статье (если я правильно понял ее посыл). Вариантов конкретных реализаций подобных процессов множество, я только за последние лет 5 могу вспомнить десяток проектов, где использовал разные инструменты, в зависимости от проекта, потребностей и бюджета. Больше всего мне нравится работать с gettext, как наиболее старым (первый релиз — 1990г., если не ошибаюсь), распространенным и универсальным форматом из всех имеющихся, но были и другие варианты. Так что в мире фронта и бекенда, на мой взгляд, с локализацией все обстоит достаточно неплохо.
Так что в мире фронта и бекенда, на мой взгляд, с локализацией все обстоит достаточно неплохо.
а я считаю, что плохо. Аргументирую
- все эти локализации ломаются на кейсах
1.a. микс различных языков
1.b. микс LTR и RTL
1.c. т.к. в разных языках разная плотность информации — строчки получаются разной длины — в худшем случае элементы наползают друг на друга, а при адаптивной верстке могут быть внезапные эффекты с размером элементов тоже
1.d. подстановки текста в текст — например, в одном языке нет родов, склонений и падежей, а в другом есть. И получается в духе "отправить письмо Виктор Викторович" - нет удобных способов редактирования
- если в системе необходимо одно и то же слово перевести на другой язык в разных контекстах по-разному — избежать дублирования строчек не избежать.
Если что — я не спец по локализациям. Рядом стоял. Свечку держал.
Так что — есть куда развивать это направление. Хотя оно действительно не прям в зачаточном состоянии
1.а, 1.b -вопрос к дизайнеру UI: «Зачем так делать?»
1.c — вопрос к верстальщику и переводчику, пусть договорятся как-то
1.d — сложно, да. Задача для всей команды (дизайнер-верстальщик-переводчик-программист). Мне как-то попадалась в руки какая-то китаймашина, гда в русском языке интерфейса формат картиники (поменять) и формат SD карты (выполнить) сумрачные гении перевели одинаково Оо. Не надо так.
неплохо !== идеально. И развиваться есть куда, не спорю, есть еще много мест в работе с локализациями приложений, которые нуждаются в усовершенствовании. Вопросы, вроде 1.d — решаемы, но нужны дополнительные телодвижения, чтобы был распознан контекст сообщения (https://pythonhosted.org/pymorphy/usage/django.html — когда-то использовал например, и там род можно было указать) П.3. — это не дублирование строк, это абсолютно разные термины, для которых надо пояснять, что они означают и в каком контексте используются. Про остальное в соседнем комменте вроде ответили. Что касается "удобных способов редактирования" — есть разные способы, на вкус и цвет, как говорится. Поделитесь, что и в каких инструментах не устроило?
выделение и хранение текста для локализации порождает другую, не менее серьезную проблему. придется выстраивать очень четкие правила как для программистов, так и для тех, кто будет текст добавлять, удалять, править. при большом объеме проекта это становится тяжело, так или иначе появится мусор, дублирующий текст, тексты, которые почти похожи друг на друга. ну и само собой, все участники проекта, которые влияют на текст, должны этим правилам следовать (чего не будет), а также договариваться друг с другом, в какой форме, в каком месте и какой текст будет применяться. и опять таки это не решает проблемы, что программисту придется править код после очередной правки текста.
Программист на этапе добавления нового функционала с новым текстом должен нагуглить, есть ли уже такой и можно ли его переиспользовать.
Лингвист должен понимать, а лучше видеть где этот текст показывается, в каком контексте.
Лингвист в конечном счёте несёт ответственность за корректность текста.
Никто ни с кем не должен договариваться, должно быть чёткое разделение ответственности.
в теории да, все должны все понимать и делать правильно. на практике, когда размер проекта становится больше критического, это правило начинает нарушаться. программисту становится проще вбить текст в код, чем искать и применять тот, который уже вбит в базу. по итогу, получаем много дублирующего или очень похожего по смыслу одинакового мусора, который никто не хочет чистить и в котором нужно разбираться
и опять таки, это не решает проблемы, когда из за изменившейся локализации текста (например, при переключении с английского на русский), может запросто поехать UI. то есть, в любом случае нужно тестировать руками и делать правки в коде, чтобы учесть те или иные нюансы в дизайне.
я хочу сказать, что задача, которую вы поставили в статье, давно уже решена примерно таким способом, который вы предложили. но решается она лишь частично, не полностью. так как начинают вылазить другие проблемы, о которых я упомянул выше
Тестировать руками должен лингвист. Он же должен сказать что там что-то отображается неправильно.
по итогу, получаем много дублирующего или очень похожего по смыслу одинакового мусора
Это не совсем так. То, что может одинаково выглядеть, например, в английском, может по разному выглядеть в других языках в зависимости от контекста. Это значит что эти похожие или даже одинаковые фразы должны быть редактируемыми независимо.
Если речь про изначально одинаковые сущности — то это проблема архитектуры. Например, вместо переиспользования модуля таблица
и сущности заголовок таблицы "проект"
дублируется существующий функционал (например, другим программистом) с дублированием всех текстов. Тут уж никуда не денешься, нужно делать рефакторинг.
Зависит.
Опять же, возьмём для примера какие-нибудь репорты компании в виде таблиц.
Есть ряд репортов, в большинстве из которых присутствует колонка "проект".
Изначально с большой долей вероятности, это должна быть одна и та же строка, редактируемая в одном месте.
Если вдруг в каком-то из репортов возникнет необходимость использовать другую строку — тогда нужно добавлять новый id и привязывать к нему другую строку.
То же касается ссылок на сущности, предположим на другой колонке в таблице есть ссылка на одну из существующих колонок (расход1, расход2, сумма = расход1 + расход2) — в таком случае они не должны быть забиты текстом, а ссылаться именно по id, так чтоб если имя колонки меняется — оно менялось в связанных местах. Это же часто встречается в настройках/фильтрах таблицы (например строка "сортировка по умолчанию — номер проекта").
Во всех остальных местах, где тексты явно независимы — должны быть использованы уникальные идентификаторы изначально, даже если текст выглядит одинаково.
Дело не только в маркетинге, тут ещё другие языки "помогают". Например, на английском языке в разных местах может быть кнопка/ссылка "About". В русском переводе назвать её просто "О" не получится, придётся уточнять...
выделение и хранение текста для локализации порождает другую, не менее серьезную проблему. придется выстраивать очень четкие правила как для программистов, так и для тех, кто будет текст добавлять, удалять, править.
Не совсем понял, почему именно отделение текста от кода порождает эту проблему? Четкие правила должны существовать изначально в любом случае, если текст будет писаться как кому угодно, то нет абсолютно никакой разницы, в коде он или в ресурсных файлах. А вот вынесение текста в ресурсы, как раз упрощает контроль за соблюдения этих правил и общую стандартизацию.
И подобные тексты по сути меняются крайне редко. Иначе админка и так позволяет модификации контента.
Дополнительную проблему создают стили. Замена строки на слишком длинную или слишком короткую может иметь неприятные последствия в виде поехавших стилей или просто выглядить не красиво из за переноса строки. Поэтому лучше изменения как минимум тестировать визуально.
Проблема в том, что остальные сотрудники даже не знают о существовании нового текста, который программист добавил (и программист плохо владеет русским/английским/тд языками)
Сложно следить — значит легко получить беспорядок, что видно на большом количестве сайтов, к примеру.
Проблема в том, что остальные сотрудники даже не знают о существовании нового текста, который программист добавил
Проблема в том, что у программиста task trackerа нет.Никто не знает, чего он наворотил и зачем.
Даже при наличии таск трекера всё равно может сложиться ситуация "никто не знает, чего он наворотил и зачем". Чтобы вовремя готовить и переводить тексты — нужно прежде всего включение этого шага в рабочий процесс, а используется ли при этом таск трекер — десятое дело.
Даже при наличии таск трекера всё равно может сложиться ситуация «никто не знает, чего он наворотил и зачем».
При отсутствии трекера — обязательно сложится. Это не серебрянная пуля, но для сопровождения задач от постановки до завершения отлично помогает.
Чтобы вовремя готовить и переводить тексты — нужно прежде всего включение этого шага в рабочий процесс
А как иначе? Оо
Такое уже давно есть. В некоторых проектах (Андроид, например) вместо текстов пишут идентификаторы вроде
tr('welcome.message')
А в отдельном конфиге хранится соответствие идентификатора и текста. Это также позволяет организовать перевод.
Хотя по моему, использование идентификаторов вместо текстов делает код менее понятным.
Хотя по моему, использование идентификаторов вместо текстов делает код менее понятным.
Наоборот, R.string.welcome_message в коде намного информативнее, чем сам текст приветствия, достаточно чтобы имена ресурсов были самодокументирующими, но это правило должно распространяться на весь код в целом
Стоит уточнить, что на ситуацию я смотрю со стороны разработчика десктопных и мобильных приложений на C# и Kotlin, когда и в языках и в IDE имеется поддержка ресурсных файлов «из коробки»
Мы разделяем код и текст в большом проекте openpapyrus много лет (c++). Должен признать проблема очень тяжелая и технически и организационно. Текст и код разделяются руками — автоматического разделения нет, хотя и планируем реализовать хотя бы частичное.
https://github.com/papyrussolution/OpenPapyrus/tree/master/Src/Rsrc/Str
Основной файл ppstr2.txt при сборке проекта парсится, формирует идентификацию, код получает строки по специальным идентификаторам частью целочисленным, частью символьным.
Вы изобрели локализацию, gettext, и тыщи онлайн сервисов для перевода.
Честно говоря, до сих пор не уверен как лучше. Вот вы пишите что можно легко отвязывать финальный текст от исходного в коде, а касается ли это системных текстов вроде тех же статусов заказов?
Или на одной и той же форме видишь Применить и Cancel (потому что на русский переведены не все сообщения). Локализация отдельно от кода это архиправильно, и так делают все, особенно если программа поддерживает >1 язык.
Да, появятся дополнительные сложности. Результат желательно давать вычитывать native speakerу. Перевод без понимания контекста использования может выглядеть странно.
Полностью согласен. Хуже того — программист начинает с некоторого момента делать конкатенацию и подстановку строк, а она, к сожалению, при переводе на другие языки ломается на раз как минимум хотя бы из-за структуры языков, падежов/склонений и пр.
Им пользуются множество проектов, включая Django, VLC, а также Bitbucket и Sourcetree от Atlassian.
А можно пруф, что это сервис атлашиан? На сайте там написано только лишь то, что Atlassian ТОЖЕ пользуются Transifex, но никак не про владение компанией...
Немного уровень более низкий (репозитории, а не фреймворки), наверное, будет требоваться, чтобы тексты в отдельных файлах лежали, типа lang-файлов каких-то
на практике же получится что текст имеет несколько ревизий, встанет вопрос какую именно ревизию использовать
Я слышал, бывают git/mercurial/etc… А в них бывают субрепозитории…
Это всё неплохо, вот только автор использование любых репозиториев записывает в недостатки:
Чтобы исправить текст — надо делать коммит в репозиторий с кодом. Для некоторых членов команды (компании), не программистов, это может быть непросто
Да… Непросто… Инструкция по базовому workflow работы с репозиторием (с картинками) — 1-2 листа А4. Даже с прожженым гуманитарием можно договориться. Он же как то с Word разобрался?
Если проводить параллель с тем же Laravel, то это похоже на использование встроенной функции для переводов __(), но тексты хранятся не в локальных файлах внутри этого же репозитория
Крупные компании, наверняка, уже давно это делают, используя какие-то свои собственные решения, но для всех остальных — нет никакой готовой утилиты или фреймворка.
Это не так. В Yii можно задать базу данных как источник переводов. Для Laravel по запросу "laravel translations database" я нашел вот этот пакет https://laravel-news.com/translation-loader-package.
Разделение кода и текста: мысли вслух