Pull to refresh

Comments 50

На самом деле, это много где так или иначе уже используется.
Open-source софтины частенько имеют вынесенные отдельно тексты, игры.
В своё время пришёл к такому же выводу, пошёл и сделал (неавтоматизированное) решение на базе Google Sheets.
опциональный платный сервис, где можно заказывать переводы, проверку орфографии и так далее.

уже предлагается каждой второй компанией по переводу и вычитке текстов.
Алконост, из тех, что публикуется на Хабре.
Да — я только в играх и мобильных приложениях что-то подобное и видел. Не смог нагуглить ни одного варианта для backend/frontend разработчиков. Похоже, что все проекты от малого до среднего размера — для них нет никакого решения

Ну почему же, например, в Django есть пакет локализации на основе gnu gettext — и работает это очень похоже на то, что у вас описано.

Для frontend-а первое, что приходит на ум:



либо более специфичные для фреймворков:



Для бекенда также практически для каждого фреймворка есть возможности локализации, https://laravel.com/docs/8.x/localization, например.


Эти и другие решения существуют и развиваются уже не первый год, они замечательно выполняют свои задачи, однако приносят с собой и новые. Если нужно изменить текст "только вот этой кнопки", все равно надо подключать программиста. А еще надо постоянно контролировать процент перевода в проекте, и горе тем, у кого инструменты не могут выдать статистику. Чтобы снизить количество "мусора", упоминаемого в https://habr.com/ru/post/533980/#comment_22443216, желательно озаботиться генерацией файлов перевода из исходных кодов приложения. Кроме того, неплохо иметь референсную базу, в которой будет указано, где и как использовать самые распространенные вещи. Таким образом, чтобы все хорошо работало, нужно вводить серьезные изменения в рабочий процесс многих людей. Можно вводить меньше изменений, смириться с рядом проблем, держать в голове ньюансы.


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

Посмотрел ссылки — все-таки не то, неудобно. 1) Тексты хранятся вместе с кодом, просто в отдельных файлах. Чтобы редактировать — надо делать коммиты в код, хотя логика не изменилась. 2) Ломается flow программиста, нужно замедляться и искать правильные ID текстов вроде polyglot.t(«nav.sidebar.welcome») — сделал опечатку в «nav.sidebar.welcome» и получил некрасивую страницу

Я думаю, что даже без локализации вероятно было бы удобно. Я работаю в среднем проекте с одним языком, и регулярно бывает что 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. все эти локализации ломаются на кейсах
    1.a. микс различных языков
    1.b. микс LTR и RTL
    1.c. т.к. в разных языках разная плотность информации — строчки получаются разной длины — в худшем случае элементы наползают друг на друга, а при адаптивной верстке могут быть внезапные эффекты с размером элементов тоже
    1.d. подстановки текста в текст — например, в одном языке нет родов, склонений и падежей, а в другом есть. И получается в духе "отправить письмо Виктор Викторович"
  2. нет удобных способов редактирования
  3. если в системе необходимо одно и то же слово перевести на другой язык в разных контекстах по-разному — избежать дублирования строчек не избежать.

Если что — я не спец по локализациям. Рядом стоял. Свечку держал.
Так что — есть куда развивать это направление. Хотя оно действительно не прям в зачаточном состоянии

Кому нужно — тот чинит эти кейсы.
1.а, 1.b -вопрос к дизайнеру UI: «Зачем так делать?»
1.c — вопрос к верстальщику и переводчику, пусть договорятся как-то
1.d — сложно, да. Задача для всей команды (дизайнер-верстальщик-переводчик-программист). Мне как-то попадалась в руки какая-то китаймашина, гда в русском языке интерфейса формат картиники (поменять) и формат SD карты (выполнить) сумрачные гении перевели одинаково Оо. Не надо так.

неплохо !== идеально. И развиваться есть куда, не спорю, есть еще много мест в работе с локализациями приложений, которые нуждаются в усовершенствовании. Вопросы, вроде 1.d — решаемы, но нужны дополнительные телодвижения, чтобы был распознан контекст сообщения (https://pythonhosted.org/pymorphy/usage/django.html — когда-то использовал например, и там род можно было указать) П.3. — это не дублирование строк, это абсолютно разные термины, для которых надо пояснять, что они означают и в каком контексте используются. Про остальное в соседнем комменте вроде ответили. Что касается "удобных способов редактирования" — есть разные способы, на вкус и цвет, как говорится. Поделитесь, что и в каких инструментах не устроило?

выделение и хранение текста для локализации порождает другую, не менее серьезную проблему. придется выстраивать очень четкие правила как для программистов, так и для тех, кто будет текст добавлять, удалять, править. при большом объеме проекта это становится тяжело, так или иначе появится мусор, дублирующий текст, тексты, которые почти похожи друг на друга. ну и само собой, все участники проекта, которые влияют на текст, должны этим правилам следовать (чего не будет), а также договариваться друг с другом, в какой форме, в каком месте и какой текст будет применяться. и опять таки это не решает проблемы, что программисту придется править код после очередной правки текста.

Программист на этапе добавления нового функционала с новым текстом должен нагуглить, есть ли уже такой и можно ли его переиспользовать.
Лингвист должен понимать, а лучше видеть где этот текст показывается, в каком контексте.
Лингвист в конечном счёте несёт ответственность за корректность текста.
Никто ни с кем не должен договариваться, должно быть чёткое разделение ответственности.

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


и опять таки, это не решает проблемы, когда из за изменившейся локализации текста (например, при переключении с английского на русский), может запросто поехать UI. то есть, в любом случае нужно тестировать руками и делать правки в коде, чтобы учесть те или иные нюансы в дизайне.


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

Тестировать руками должен лингвист. Он же должен сказать что там что-то отображается неправильно.


по итогу, получаем много дублирующего или очень похожего по смыслу одинакового мусора

Это не совсем так. То, что может одинаково выглядеть, например, в английском, может по разному выглядеть в других языках в зависимости от контекста. Это значит что эти похожие или даже одинаковые фразы должны быть редактируемыми независимо.


Если речь про изначально одинаковые сущности — то это проблема архитектуры. Например, вместо переиспользования модуля таблица и сущности заголовок таблицы "проект" дублируется существующий функционал (например, другим программистом) с дублированием всех текстов. Тут уж никуда не денешься, нужно делать рефакторинг.

UFO just landed and posted this here

Зависит.
Опять же, возьмём для примера какие-нибудь репорты компании в виде таблиц.
Есть ряд репортов, в большинстве из которых присутствует колонка "проект".
Изначально с большой долей вероятности, это должна быть одна и та же строка, редактируемая в одном месте.
Если вдруг в каком-то из репортов возникнет необходимость использовать другую строку — тогда нужно добавлять новый id и привязывать к нему другую строку.
То же касается ссылок на сущности, предположим на другой колонке в таблице есть ссылка на одну из существующих колонок (расход1, расход2, сумма = расход1 + расход2) — в таком случае они не должны быть забиты текстом, а ссылаться именно по id, так чтоб если имя колонки меняется — оно менялось в связанных местах. Это же часто встречается в настройках/фильтрах таблицы (например строка "сортировка по умолчанию — номер проекта").


Во всех остальных местах, где тексты явно независимы — должны быть использованы уникальные идентификаторы изначально, даже если текст выглядит одинаково.

Дело не только в маркетинге, тут ещё другие языки "помогают". Например, на английском языке в разных местах может быть кнопка/ссылка "About". В русском переводе назвать её просто "О" не получится, придётся уточнять...

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

Не совсем понял, почему именно отделение текста от кода порождает эту проблему? Четкие правила должны существовать изначально в любом случае, если текст будет писаться как кому угодно, то нет абсолютно никакой разницы, в коде он или в ресурсных файлах. А вот вынесение текста в ресурсы, как раз упрощает контроль за соблюдения этих правил и общую стандартизацию.
Сомнительная идея. Поправить текст — минутное дело. В нормальном проекте QA зарегистрирует баг, которые любой программист вскоре исправит.

И подобные тексты по сути меняются крайне редко. Иначе админка и так позволяет модификации контента.

Дополнительную проблему создают стили. Замена строки на слишком длинную или слишком короткую может иметь неприятные последствия в виде поехавших стилей или просто выглядить не красиво из за переноса строки. Поэтому лучше изменения как минимум тестировать визуально.
Поправить текст — минутное дело для программиста, знающего где он находится
Проблема в том, что остальные сотрудники даже не знают о существовании нового текста, который программист добавил (и программист плохо владеет русским/английским/тд языками)
Сложно следить — значит легко получить беспорядок, что видно на большом количестве сайтов, к примеру.
Проблема в том, что остальные сотрудники даже не знают о существовании нового текста, который программист добавил


Проблема в том, что у программиста 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, и тыщи онлайн сервисов для перевода.

Slack — это переделанный mIRC. Переделывать что-то старое, используя новые технологии и более удобный интерфейс — это очень даже хорошо. Речь о том, что все существующее — очень устаревшее и неудобное. Медленно, дорого, неэффективно
В проекте все тексты лежат в i18n-файлах, но используется только английский язык. Когда стало нужно поменять название статусов заказов на немного другие, была идея просто поправить языковые файлы, но показалось что лучше этого избежать и обновить таблицу с названиями напрямую.
Честно говоря, до сих пор не уверен как лучше. Вот вы пишите что можно легко отвязывать финальный текст от исходного в коде, а касается ли это системных текстов вроде тех же статусов заказов?
Особенно забавно это выглядит, когда вместо «Применить» ты видишь «NO_TEXT(TEMPLATES.FORM.Button2)» или вместо описания какой-нибудь карточки "$(DESCRIPTION_CARD_18)".

Или на одной и той же форме видишь Применить и Cancel (потому что на русский переведены не все сообщения). Локализация отдельно от кода это архиправильно, и так делают все, особенно если программа поддерживает >1 язык.
Да, появятся дополнительные сложности. Результат желательно давать вычитывать native speakerу. Перевод без понимания контекста использования может выглядеть странно.

Вот-вот, про это и речь — проблема видна на многих сайтах. Требовать от программистов находить правильно ID текста существующего — не сильно лучше, чем требовать от них писать красивые тексты.
Вот гнаться за уникальностью строк и переисопльзованием — это может сыграть злую шутку. В разных местах — одно и то же на английском может знаичть совсем разное. Мне больше понравился вариант, встречал, когда под каждую вьюху создается своя модель с текстом. и при сериализации/десериализации — получается что все строки всегда с контекстом их использования. И пусть будут дубли, но зато мы можем делать однозначно корректную локализацию.

Полностью согласен. Хуже того — программист начинает с некоторого момента делать конкатенацию и подстановку строк, а она, к сожалению, при переводе на другие языки ломается на раз как минимум хотя бы из-за структуры языков, падежов/склонений и пр.

Да — примерно в этом идея и заключается, мы запоминаем в какой вьюхе на каких строках текст использовался (контекст), а дальше уже дело других сотрудников (не программистов) решить, переиспользовать один текст/перевод или сделать новый

А можно пруф, что это сервис атлашиан? На сайте там написано только лишь то, что Atlassian ТОЖЕ пользуются Transifex, но никак не про владение компанией...

Спасибо за комментарий, вы правы.
Вот это уже ближе ага, в Гугле не мог найти его
Немного уровень более низкий (репозитории, а не фреймворки), наверное, будет требоваться, чтобы тексты в отдельных файлах лежали, типа lang-файлов каких-то
в теории все здорово на практике же получится что текст имеет несколько ревизий, встанет вопрос какую именно ревизию использовать, кто устанавливает параметр используемой ревизии, разрастающаяся история изменений, при больших объемах работы обсуждение правок и ревизий будут занимать больше времени, чем сами правки, как пример можно привести википедию, правка статьи занимает 1 час, а одобрение правки ( попросту выбор наиболее подходящей ревизии текста) может затянутся до полугода. И есть аспект защищенности информации, чем больше Вы храните во внешних источниках тем более тщательно нужно прорабатывать каналы и способы их взаимодействия, любое обращение к внешнему источнику это потенциальная уязвимость для проекта.
проблемы будут, но их станет меньше же — текущее решение еще хуже. ревизий много и они все от разных программистов, с разными ошибками. невозможно даже проверить — используем ли мы один тон общения везде на сайте. насчет внешних источников — эти тексты и так все публичные как правило, не должно быть проблемой
на практике же получится что текст имеет несколько ревизий, встанет вопрос какую именно ревизию использовать


Я слышал, бывают git/mercurial/etc… А в них бывают субрепозитории…

Это всё неплохо, вот только автор использование любых репозиториев записывает в недостатки:


Чтобы исправить текст — надо делать коммит в репозиторий с кодом. Для некоторых членов команды (компании), не программистов, это может быть непросто

Да… Непросто… Инструкция по базовому workflow работы с репозиторием (с картинками) — 1-2 листа А4. Даже с прожженым гуманитарием можно договориться. Он же как то с Word разобрался?

Это неудобно — даже программистам. Много файлов, внутри много кода, перемешанного с текстом. Удобство наших инструментов повышает КПД
Много файлов, внутри много кода, перемешанного с текстом.


Код — отдельно, текст — отдельно. Можно нужно даже в разные репозитории. Об этом и текст над комментариями.
не поверите но у меня есть прожженные продавцы и они очень круто продают, но когда речь заходит о работе с пк это целая эпопея, сопли, слюни во стороны и все в таком роде.

s/продавцы/программисты/, s/работать с ПК/писать отчёт о проделанной работе/ ;)

Если проводить параллель с тем же Laravel, то это похоже на использование встроенной функции для переводов __(), но тексты хранятся не в локальных файлах внутри этого же репозитория
Крупные компании, наверняка, уже давно это делают, используя какие-то свои собственные решения, но для всех остальных — нет никакой готовой утилиты или фреймворка.

Это не так. В Yii можно задать базу данных как источник переводов. Для Laravel по запросу "laravel translations database" я нашел вот этот пакет https://laravel-news.com/translation-loader-package.

Sign up to leave a comment.

Articles