Сейчас будет сложная история с техническими подробностями. История о сложностях работы с переводами на сайте.
История сложная по многим причинам:
Переводов больше десятка мегабайт
Не все переводы интерфейсные (интерфейсных только пара мегабайт), но тоже немало
Переводы хочется менять в рантайме из панели админа
В случае с SPA приложением хочется грузить переводы только для конкретной страницы.
Как решать такую проблему?
Мы пришли к такой модели:
В реакт-компонентах появляется поле static trls поле, которое является обычной Map<String, String>. В ней value — это ключ перевода.
В контексте храним информацию, какой язык перевода нужен (потому что может быть часть сайта на русском, а резюме — на английском).
При сборке кода babel плагин выдирает поле trls, собирает большую мапу "страница: список переводов". Этот файл подключается как конфиг и по нему для каждой страницы присылаются переводы.
На бекендах переводы подгружаются из базы при старте и затем время от времени получают обновления. В рантайме храним переводы в marisa-trie дереве.
Кстати, плагин, который выдирает статические поля вот: github.com/hhru/babel-plu…
Казалось бы, хорошее решение, легко использовать.
Все так, но в сентри начинают приходить баги, что перевода нет.
Добавляем логирование, начинаем трекать, в каких случаях происходит ошибка, что лежит в сторе, какие последние action происходили.
Делаем первый заход, понимаем, что страница например bla-bla, а данные в сторе от страницы foo-bar.
Разбираемся в проблеме, понимаем, что в некоторых редьюсерах данные применяются вот так: (state, action) => bla-bla return [...action.payload];
Quite a tricky thing in spread operator: you can spreadundefined
in object like{...undefined}
, but you can't do it with arrays [...undefined]. It happens, as spread syntax can be applied only to iterable objects except for spread properties (e.g. for objects). pic.twitter.com/ItVvJRZyyj
В чем соль 1: Оператор ... очень хитрый оператор. Я писал об этом здесь: twitter.com/xnimorz/status… И поэтому он может падать. если action.payload будет undefined

В чем соль 2: мы используем batch actions и делаем батчевый редьюсер. Если падает применение данных в одно из полей в сторе, стор не применяется, остается старое значение.
Переводы хранятся в сторе. И по стечению обстоятельств — переводы первыми вызываются на странице. Вот они и падают.
Окей, почему мы не получали правильную ошибку? Оказалось, потому что случайно поставили на promise, который обрабатывал данные от асинхронного запроса reject ветку без репорта ошибки :facepalm:
Вот так один неверный reject привел к долгой истории и правкам редьюсеров.
Но на этом все не закончилось! Ошибка всплывает опять. Начинаем разбираться, находим проблему в фетчере и асинхронной записи данных (рассказывать подробно не буду, слишком много специфики). Правим, проходит время — ошибка снова всплывает.
Пять подобных итераций и полгода спустя мы из-за этой ошибки отрефакторили кучу мест, каждый раз получалось ее воспроизвести и пофиксить, но ошибка все еще с нами. Мы думаем о том, чтобы дать ей почетное звание помощника в поиске инфраструктурно-архитектурных недостатков.