У мене є блог на Hugo з контентом українською мовою — пости, нотатки, статті, архів LiveJournal за 2007–2017 роки. Загалом щось близько 409 markdown-файлів. І давно свербіло зробити англійську версію.
Зробити це вручну — нереально. Найняти перекладача на 409 файлів — дорого і довго. Але є Claude Code.
Що зроблено#
Весь контент із content/ua/ тепер має відповідні переклади в content/en/. Це:
- Пости за 2008–2026 роки
- Архів LiveJournal — 181 запис із 2007 по 2017 рік: технічні нотатки, щоденникові записи, поезія, політичні коментарі, рецензії на фільми… усе це написане молодшим мною на суміші української та російської
- Статті та нотатки — більш структурований контент у
docs/ - Сторінки проєктів
Як це робилось#
Ідея проста: Claude Code вміє запускати підагентів. Для кожного файлу, який ще не має англійського відповідника, запускається окремий агент із завданням:
- Прочитати
content/ua/path/to/file.md - Перекласти з української (або російської — архів LJ часто був змішаним) на англійську
- Зберегти в
content/en/path/to/file.md
Правила перекладу:
- Структура frontmatter зберігається повністю
- Значення тегів не перекладаються (теги — це ідентифікатори)
- Заголовок, опис та інший описовий текст у frontmatter — перекладаються
- Весь текст тіла перекладається
- Markdown-форматування, зображення, посилання, блоки коду — зберігаються незмінними
Агенти запускались послідовно, по одному на файл. Паралельність тут не підходить — і через ліміти API, і тому що важливо не загубити жоден файл.
Технічні деталі#
Дозволи. Claude Code за замовчуванням запитує підтвердження на кожну операцію запису. Для підагентів це проблема — вони не можуть отримати інтерактивне підтвердження у фоновому режимі. Рішення: файл .claude/settings.json із pre-approved дозволами:
{
"permissions": {
"allow": [
"Write(content/en/**)",
"Bash(*)"
]
}
}Пошук непереведених файлів. Простий shell-скрипт:
find content/ua -name "*.md" | sort | while IFS= read -r ua; do
en=$(echo "$ua" | sed 's|content/ua/|content/en/|')
[ ! -f "$en" ] && echo "$ua"
doneВажливий урок: find | xargs без -print0 / -0 ламається на іменах файлів із пробілами. У нас є тека content/en/docs/projects/hardware-tools/3d printers/ — і це одразу дало помилку. Правильно так:
find content/en -name "*.md" -print0 | xargs -0 sed -i '' -e 's|old|new|g'Що ще виправлено#
Після перекладу виявились додаткові проблеми.
Внутрішні посилання. Hugo-блог має defaultContentLanguage: ua, тобто посилання на кшталт /docs/articles/foo/ або /posts/2024/bar/ без мовного префіксу ведуть на українську версію. В англійських файлах усі такі посилання треба замінити на /en/docs/... і /en/posts/.... Також абсолютні посилання типу https://www.disfinder.com/docs/... — теж переробити у відносні /en/docs/....
Виправлено масово через sed:
find content/en -name "*.md" -print0 | xargs -0 sed -i '' \
-e 's|](/docs/|](/en/docs/|g' \
-e 's|](/posts/|](/en/posts/|g'Зображення. Картинки лежать поруч з українськими постами — content/ua/posts/2024/foo/image.png. Англійська версія поста їх не бачить, бо вона в content/en/posts/2024/foo/. Рішення — симлінки:
find content/ua -not -name "*.md" -type f -print0 | while IFS= read -r -d '' ua; do
en=$(echo "$ua" | sed 's|content/ua/|content/en/|')
if [ ! -e "$en" ] && [ ! -L "$en" ]; then
mkdir -p "$(dirname "$en")"
python3 -c "
import os, sys
ua, en = sys.argv[1], sys.argv[2]
rel = os.path.relpath(ua, os.path.dirname(en))
os.symlink(rel, en)
" "$ua" "$en"
fi
doneВідносний шлях обчислюється через Python, щоб коректно обробити будь-яку глибину вкладеності.
Результат#
409 файлів. Кілька годин роботи (з перервами на ліміти API). Мінімум ручної праці — лише нагляд і корекція курсу.
Якість перекладу — добра для технічного та щоденникового контенту. Ідіоми, жаргон, радянські культурні посилання — все це агент намагався пояснити або адаптувати. Вірші перекладені зі збереженням структури рядків, але без рими (форсована рима у перекладі зазвичай руйнує зміст).
Усе це задокументовано у CLAUDE.md — щоб наступного разу не починати з нуля.
P.S. Цю статтю теж написав Claude. Я лише попросив. Ми квіти.