melikhov.dev – Telegram
melikhov.dev
4.63K subscribers
110 photos
2 videos
2 files
203 links
Фронтенд, фронт-бек и около. Всё, что в голову пришло. Иногда котики.
Download Telegram
Как устроена локализация в крупных проектах

В маленьких проектах всё просто — пишем все тексты в интерфейсах на одном языке и в ус не дуем. Если же проект вырастает и начинает экспансию, то вместе с ней приходит боль. Все тексты нужно заменять на ключи, выносить в файлики и базы данных (такой кортеж из ключа и набора переводов на разные языки) и заменять ключ на перевод по выбору пользователя.
Если говорить о фронтенде, то тут сразу отпадает вариант с динамической подстановкой текстов из загруженной в бандл бд — наборы переводов для всех языков просто раздуют бандл. Это значит, что нам нужно подготовить переведённые бандлы в момент сборки, чтобы пользователь грузил только те тексты, которые нужны в его выбранной локали. А есть же ещё RTL-языки, ох.
Это всё решаемо и это только полбеды.

Вторая половина беды состоит в том, что процесс перевода человеком — это синхронный процесс. В крупном проекте не можете выкатить релиз не переведя его на хотя бы базовый набор языков. Особенно, если ваш основной язык не международный. Фоллбечить немецкий в русский это просто смешно 🙂 Значит, вам нужно чтобы к релизу были готовы два языка — локальный + английский. Очевидно, что за переводы не должен отвечать разработчик или дизайнер, для этого есть ребята, прокачанные в мультиязычном написании текстов.

Для работы с переводчиками используют так называемые translation management systems которые в чём-то похожи на content managment systems. Это специальный UI, где переводчики видят какие свежие ключи приехали, как они лежат в контексте (неплохо, когда вместе с ключами едет подсказка их применения), переводят и аппрувят их.

Процесс выглядит примерно так

1. Разработчик расставляет в коде интерфейса ключи, а не тексты. Рядом заводит файлик, в котором описывает связки ключ -> { дефолтный текст, множестенные формы, подсказки переводчику }
2. В момент билда все ключи загружаются в TMS, находятся изменения и переводчик получает задачу на переводы
3. Билд стоит в ожидании перевода на базовый минимум языков
4. Переводчик в интерфейсе TMS делает свою работу и жмёт аппрув
5. По готовности переводов CI выкачивает их и собирает локализованные бандлы, билд едет на прод. Что не перевели — фоллбечится в английский (скорее всего). Остатки доедут в следующий релиз.

Тут конечно большой минус, что бутылочным горлышком становится переводчик. Потому дальше можно придумать различные варианты ускорения работы, например, что переводчик получает задачу уже в момент тестирования ветки QA и при релизе ветка переводов мерджится в main. Т.е. одна задача не должна блочить своими переводами весь релизный процесс, только себя.

Для примера TMS систем рекомендую посмотреть на Weblate
👍17🔥7
Интерфейс Weblate
🤡4👍2
Кот Кеша прощается с летом под https://youtu.be/-1I50VfyH1I
❤‍🔥27
Доехало видео с ЯЛФ.
TL;DR на картинке

https://youtu.be/dh3rKUYNlUw
15👍7
Божечки кошечки (спасибо @dskrylnikov за наводку)

https://github.com/microsoft/TypeScript/issues/50457
🔥10🐳3
Увидел тут в комментах, что не всем понятно, про какие edge-лямбды иногда я говорю. Ок, погнали.
Лямбды можно условно разделить на два вида:

1 Классические лямбды, о которых обычно и говорят. Призваны заменить «обычный» бэкенд, запускаются в тех же датацентрах, где расположены виртуалки, базы данных и т.д. Стоят дороже, умеют больше.
2 Edge-лямбды. Расположены на серверах CDN, их задача быть как можно ближе к пользователю. Стоят дешевле, умеют меньше (зато как быстро!), функциональность наращивается с помощью богатого API от провайдера.

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

Не сказать, что идея совсем новая, например, популярное решение вытащить часть логики в nginx чтобы она даже не долетала до ноды. Можно вот по наличию определённой куки или заголовка раздавать разный контент (такое вот A/B). Или вернуть редирект. Или проверить авторизацию. А теперь всю эту логику можно положить в JS-код и закинуть в edge-лямбду.

Что можно ещё сделать? Можно запилить прокси, которое будет отдавать браузеру данные с другого бэка, в который иначе не достучаться из-за CORS-политик. Можно сделать умный кэш (Cloudflare даёт доступ к Cache API и даже к Key/Value хранилищу).

Можно ли целиком построить простое приложение на edge-лямбдах и статике используя различные облачные API в качестве бэкенда? Думаю да, возможностей куча. Но это, конечно, не полноценное приложение на node.js, в отличие от классической лямбды. Вот Cloudflare Workers хороший пример — крутятся на V8 с минимальной обвязкой, а для ускорения запуска и удешевления эксплуатации создают пользовательские контексты в V8 изолятах.


Круто, конечно, вот только это совсем уж вендор-лок.
🔥6👍3
Адептом неста я перестал быть примерно сразу как отправил первый код в продакшен. И уж если был, то точно не жарким :)

А вообще чём больше вожусь с нодой, тем мнение Тимура всё менее радикальным кажется.
🤔16👍12😁1
Хэй, реактёры, а насколько хаком считается передать массив JSX-элементов в children? Оно конечно конечно работает, но работает на неявном приведении типов.

В документации я ни слова не нашёл, о том, что это дозволено. Однако вижу код, который эту возможность использует.

UPD

Разобрались :)
👍5
На примере предыдущего поста вы можете убедится, что некоторые фуллстеки действительно мифические. Пока ты копаешь в одну сторону, в другой стороне происходит много чего интересного. Вот несмотря на годы в JS, я для себя только-только открываю чудесный мир современных фронтенд-фреймворков. Могу накидать что-то на Реакте правильно работающее, но глубины в знаниях нет совершенно.

С другой стороны, я всё же уже способен удивляться, увидев код, обмазанный в три слоя useCallback и useMemo.
😁14👍4
Провёл лекцию по технике для онлайн-выступлений. Вот вам саммари

Минимальный уровень (вас слышно)

Проводные наушники с микрофоном на кабеле
Либо любой usb-микрофон + любые комфортные наушники

Хороший уровень (вы лучше многих онлайн-спикеров)

Второй монитор
Кликер
Динамический USB-микрофон (samson q2u, Audio-Technica atr2100x, Shure MV7)
Веб-камера 4k (Logitech Brio) либо Full HD (Logitech StreamCam)
Вместе с MacOS Ventura неплохой вариант использовать вместо веб-камеры iPhone
Светодиодный свет (например, кольцевой)

Мега супер пупер (вы великолепны)

Зеркальная или беззеркальная камера через карту захвата (Elgato CamLink)
Питание для камеры (никаких батареек!)
Два светодиодных источника света под 45 градусов каждый (Elgato Key Light)
Цветной LED-источник подкрасить комнату за спиной (Boling BL-P1)
Динамический XLR микрофон (любой, подбирается по личным хотелкам) + интерфейс (Rode AL-1)

Что мы не используем

Airpods и любые другие bluetooth-наушники в качестве микрофонов

Дорогие конденсаторные USB микрофоны, например Blue Yeti
👍216
А прямо до меня читал лекцию Саша Крайнов, и он дал один важный совет для спикеров, про который я раньше не задумывался специально, но подсознательно пришёл такому на последнем ЯЛФ (отмотав слайды назад).

Не ставьте на последний слайд «Спасибо», «Вопросы?», или свои контакты с QR-кодом. Поставьте вместо этого выжимку главных идей и пусть она висит, пока вы отвечаете на вопросы.
👍41
так-так, что у нас тут приехало
👍11
Я долго не мог победить один из главных своих пороков, любовь к «затащить сложную задачу за ночь». Да, это работает, но после 30 лет это гарантированно продолбанный следующий день. А самое обидное, что с утра такие задачи щёлкаются как орешки.

И тут появились эти два засранца — они вырубаются в 12 и просыпаются в 6:30-7:00. Если я хочу хоть как-то поспать, то пора бежать в кроватку.
🥰44👍7❤‍🔥1👏1
Лайфхак, как мы в Osome следим за структурой директорий в репках микросервисов через CODEOWNERS, чтобы команда лишнего не принесла

# keep directory structure
/src/pages/ @angryPlatformTeamLead
/src/pages/*/*/*.page.tsx @company/business-team
/src/pages/*/*/*.page.test.tsx @company/business-team
🔥7
Больше недели уже то тут, то там натыкаюсь на новость, про сигналы в Preact. Как человек, вошедший в современный Реакт относительно недавно, решил разобраться, что же там случилось. (А помогли мне тредики в https://news.1rj.ru/str/artalog)

Лонг стори шорт, для тех, кто как я и немножко прозевал последние годы.

Задача Реакта постоянно приводить отображение в соответствие с состоянием. Ну, т.е. у нас есть данные, данные меняются и нужно приводить картинку в соответствие этим данным. Состояние данных привязано к конкретному компоненту. Изменение состояния (стейта) компонента приводит к его ре-рендеру и ре-рендеру всех его потомков по цепочке (и не важно, какие пропсы потомки слушают. Просто все по цепочке все. до самого последнего, будут перерисованы). Получается, что чем ниже лежит у нас нужный стейт, тем лучше — меньший кусочек нашего фронта будет перерисован. Однако, часто мы вынуждены поднимать стейт выше, так как на нём завязано отображение нескольких компонент. А значит, чем выше мы поднимаем нужный стейт (чем более он общий), тем ре-рендерится больше, чем мы хотели бы.

Тут Реакт выкатывает нам в помощь
memo
, мы начинаем размечать компоненты как «чистые», чтобы предотвратить их ре-рендер и код становится всё более шумным. Тут же возникает проблема с
useContext
, который снова приводит к ре-рендеру и мы должны либо дробить контекст на более мелкие контексты, либо делать дополнительные обёртки над чистыми компонентами.

И нам говорят — Реакт быстрый, забейте, пусть рендерит лишнее. А в будущем, мол, мы выкатим автоматический memo. Но как-то неприятно, мы же тут алгоритмы на собеседованиях сдаём, чтобы квадраты не пилить, мы же за скорость, ну.

Короче, команда Preact смотрела на это, смотрела на другие решения на рынке (тот же Solid и Vue) и решила — хватит это терпеть, Реакт достал быть хуже всех, давайте всё сломаем сделаем хорошо. И добавили новый реактивный примитив Signal, который выглядит как объект-контейнер с полем
.value
.

Идея такая, что мы просто создаём Signal за пределами компонента и используем его значение внутри компонента через чтение
.value
. Так как сам Signal является объектом, то его можно безопасно прокидывать везде — ссылка не меняется, нет причин для ре-рендера. В то же время сам фреймворк обновит компонент точечно в тот момент, когда изменится значение сигнала — такой вот удобный биндинг. И никаких массивов зависимостей.

Более того, нам предлагают не просто читать
.value
, а положить сигнал целиком в строку или в аттрибут — и фреймворк точечно обновит этот участок в DOM (никакого VDOM!).

---
// Instead of this:
<p>Value: {count.value}</p>

// … we can pass the signal directly into JSX:
<p>Value: {count}</p>

// … or even passing them as DOM properties:
<input value={count} />
---

Такая вот революция — мы взяли то, что уже было у других, сделали попроще, но зато вы можете максимально просто использовать уже сейчас в вашем любимом продукте. Да, вы можете использовать это в «обычном» React. Но надёжность и совместимость с будущими версиями React вызывает большое беспокойство.
👍201
А, ещё там забавный пассаж, мол в обычном реакте перфоманс opt-in (мы можем сделать быстро, если обмажемся мемоизацией), а с бородой сигналами, у-ух просто, всё так быстро, что мы можем только opt-out ухудшать перфоманс, избавлясь от сигналов.
Ругался в сегодняшнем подкасте, поругаюсь и тут. Ребята из webpagetest грязно манипулируют цифрами 🙂

TL;DR в статье несколько утверждений
1 Клиентский рендер медленный (согласен)
2 Запускать JS поверх прилетевшего с сервера HTML быстрее (согласен)
3 Вас спасёт SSR (крайне не согласен)

Ребята взяли ответ Твиттера и AirBnB, положили отрендеренный результат в прокси-прослойку а потом сравнили цифры. И получилось, что тот же Твиттер начал открываться за 3.4с вместо 12.1.

Ну и вывод в статье do some googling for terms like "SSR" and "server rendering"

Но это так не работает. Серверный рендер не бесплатный и не мгновенный. В кэш всё не упакуешь. Если Твиттер начнёт генерировать динамические странички на сервере, с какой скоростью он будет отвечать? Я не знаю. Никто не знает, кроме инженеров Твиттера. Да, SSG нас спасает, но SSG работает только для статического контента. Динамический же контент мы можем разве что на зоны разбить и отдавать часть (лэйаут, например) как статику со множеством реакт-рутов, наполняемых на клиенте.

Как обычно, there are three kinds of lies: lies, damned lies, and statistics
👍22🔥4
Пока нет сил на технический пост про программирование, вот вам удивительное про микрофоны. Как писал тут микрофон нужно брать динамический, а не вот эти все разрекламированные геймерские конденсаторники, a la Blue Yeti. И тут вдруг поддержка откуда не ждали — и Blue и Elgato выпустили для стримеров динамические модели.

Blue Sona красавец подороже, со встроенным предварительным усилителем. Elgato Wave DX подешевле, без предвака, но с припиской, что бустер и не нужен.

Ну и конечно приписка радует supercardioid pickup pattern focuses tightly on your voice and rejects sound coming from all other directions. А раньше-то вы чего ждали, когда выпускали конденсаторники для неподготовленных помещений с кардиодой, больше похожей на восьмёрку?
👍5🥰1
Недавно обсуждали с одним высокогрейдовым программистом — что же такое динамическое программирование (DP)? Книжки по алгоритмам нам говорят, что есть такое вот загадочное программирование, которым решается задача о рюкзаке* (вот решение, запомни) или можно посчитать расстояние Левенштейна (вот решение, вызубри).

*Напомню, что задача о рюкзаке это задача о том, как засунуть в рюкзак набор вещей максимальной ценности, если вместимость рюкзака ограниченна.

При этом обычно не даётся никакого универсального алгоритма, как решить с помощью DP любую задачу. Точнее, алгоритм такой — разбиваем задачу на меньшие подзадачи, решаем их, результаты мемоизируем и собираем ответ для исходной задачи. Похоже на «Разделяй и властвуй», но с неоднократным переиспользованием результатов каждого уровня.

И кажется мне, что сами задачи в полной мере и описывают принцип, потому везде через задачи и обьясняют DP. В чём проблема того же рюкзака? Если мы будем решать задачу жадно (берём на каждой итерации самую дорогую вещь, которая сейчас влазит в рюкзак), то решение может быть неоптимальным. Для примера, есть на полке айфоны и макбуки. Айфон занимает 1 позицию в рюкзаке и стоит 100k. Макбук занимает 4 позиции и стоит 200k. Вместимость рюкзака — 4 позиции. Жадный алгоритм говорит нам взять макбук. Динамическое программирование говорит: представь, что у тебя рюкзак на 1 позицию. Что туда влезет идеальное? А на 2 позиции? А на три? А на 4, что выгоднее — докинуть к максимуму из 3-х позиций ещё один айфон или взять макбук?
Итого DP даёт решение «4 айфона».

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

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

Желаю вам избежать DP-задач на собеседованиях.
👍26🤔51
Forwarded from { между скобок } анонсы 📣 (Grisha Skobelev)
CHAPTER 9: Design a web crawler

В публичный доступ вышло интересное и полезное обсуждение про web crawler. Советую вам заценить - Сергея UfoCoder сделал классную презентацию, где разобрал в деталях как работает поисковый робот. Так же обсудили на что стоит обратить внимание и какие возможные проблемы могут быть. Еще в самом начале Андрей показал нам артефакты из прошлого ❤️ Получилось очень лампово.

Видео уже на YouTube

PS поддержите пожалуйста нас лайком и хорошим комментарием на YouTube 🙏 Ваша поддержка помогает нам дальше заниматься этим
15👍2