ФункціїПлагінЦіниРесурси
Змінити мову
РесурсиЯк автоматизувати переклад файлів .po у вашому CI/CD пайплайні

Як автоматизувати переклад файлів .po у вашому CI/CD пайплайні

SimplePoTranslate Team9 червня 2026 р.
Як автоматизувати переклад файлів .po у вашому CI/CD пайплайні

Ваша команда робить злиття в main чотирнадцять разів на день. Кожне з цих злиттів може додати новий рядок, який бачить користувач – мітку кнопки, повідомлення про помилку, підказку. І кожен з цих рядків спочатку існує лише англійською мовою. Десь у вашому процесі релізу людина має помітити нові рядки, експортувати їх, перекласти, вставити назад і перекомпілювати. Ця людина є вузьким місцем, і в команді, що швидко випускає продукти, це вузьке місце означає, що ваші німецькі користувачі бачать англійські заповнювачі протягом двох спринтів.

Рішення полягає в тому, щоб автоматизувати переклад у вашому CI/CD пайплайні, щоб нові рядки перекладалися під час того ж злиття, що їх і ввів – без людини на критичному шляху для рутинних випадків. Це повністю досяжно сьогодні, але тільки якщо ви уникнете пастки, в яку потрапляє більшість команд: створення вручну викликів до LLM, які тихо пошкоджують ваші заповнювачі %s та структуру .po. Цей посібник демонструє реалістичну модель CI пайплайну, робочий скелет GitHub Actions та дизайнерські рішення – ідемпотентність, переклад лише нового, шлюзи перевірки – які відрізняють корисний пайплайн від того, що випускає пошкоджені збірки.

Чому ручний переклад є вузьким місцем релізу

Відповідь на запитання «чому б просто не перекладати перед кожним релізом» полягає в тому, що час ніколи не збігається. Рядки додаються розробниками постійно, але переклад відбувається партіями іншою людиною за іншим графіком. Розрив між цими двома каденціями – це місце, де накопичується ваш локалізаційний борг.

Проблема двох каденцій

Уявіть собі ручний процес. Розробник додає __( 'Export to CSV', 'mytextdomain' ) і робить злиття. Ніхто не відтворює файл .pot. Через два тижні хтось запускає wp i18n make-pot, помічає сорок нових неперекладених рядків (деякі від розробників, які вже поїхали у відпустку), здогадується про намір половини з них і надсилає файл .po перекладачу. Переклад повертається, вставляється, і можливо, заповнювачі збереглися, а можливо, ні. Тим часом три релізи були випущені з цими рядками англійською мовою.

Кожен крок тут ручний, пакетний та схильний до помилок. Мета CI/CD автоматизації – звести це до чогось, що запускається автоматично при злитті, перекладає лише те, що справді змінилося, і голосно повідомляє про збій, якщо щось виглядає неправильно – перетворюючи переклад з періодичної рутини на невидиму частину пайплайну.

Модель пайплайну: Вилучення, Порівняння, Переклад, Збереження

На високому рівні, автоматизоване завдання перекладу виконує чотири кроки при злитті в main: відтворення шаблону, виявлення нового, переклад лише цих рядків та збереження результатів. Все це має бути ідемпотентним – запуск його при злитті без змін у рядках повинен призводити до нульової різниці та нульового шуму.

Вилучення та Порівняння

Крок перший – вилучення. Відтворіть файл .pot з поточного джерела, щоб він відображав кожен рядок у кодовій базі:

wp i18n make-pot . languages/myplugin.pot

Крок другий – порівняння. Це найважливіше дизайнерське рішення у всьому пайплайні. Ви не хочете перекладати кожен рядок при кожному злитті – це марнує API-виклики, ризикує повторно перекласти рядки, які людина вже виправила, і створює величезні різниці для перегляду. Замість цього, ви об'єднуєте свіжий .pot у кожен існуючий .po і перекладаєте лише ті записи, які тепер порожні (справді нові рядки):

# Merge new template into each locale, preserving existing translations.
# Newly-added strings appear with empty msgstr; --backup=none keeps the tree clean.
for po in languages/*.po; do
    msgmerge --update --backup=none "$po" languages/myplugin.pot
done

Після msgmerge лише абсолютно нові рядки мають порожній msgstr. Все, що було перекладено раніше, залишається недоторканим. Ця властивість робить пайплайн ідемпотентним і зберігає ваші різниці для перегляду невеликими та придатними для перевірки.

Переклад та Збереження

Крок третій надсилає лише ці порожні записи на етап перекладу. Крок четвертий зберігає оновлений .po, компілює .mo та відтворює будь-який JSON. Далі ми все це інтегруємо в GitHub Actions.

Скелет GitHub Actions

Ось відповідь на запитання «як це насправді виглядає у файлі робочого процесу»: завдання, яке запускається при push до main, виконує цикл вилучення-порівняння-перекладу-збереження та відкриває pull request з результатами, замість того, щоб зберігати прямо в main.

Файл робочого процесу

name: Translate on merge

on:
  push:
    branches: [ main ]

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install WP-CLI and gettext
        run: |
          sudo apt-get update && sudo apt-get install -y gettext
          curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
          sudo mv wp-cli.phar /usr/local/bin/wp && sudo chmod +x /usr/local/bin/wp

      - name: Regenerate template and merge into locales
        run: |
          wp i18n make-pot . languages/myplugin.pot
          for po in languages/*.po; do
            msgmerge --update --backup=none "$po" languages/myplugin.pot
          done

      - name: Translate only new (empty) strings
        env:
          TRANSLATE_API_KEY: ${{ secrets.TRANSLATE_API_KEY }}
        run: ./scripts/translate-new-strings.sh languages/

      - name: Compile .mo and JSON
        run: |
          wp i18n make-mo languages/
          wp i18n make-json languages/ --no-purge

      - name: Open pull request with translations
        uses: peter-evans/create-pull-request@v6
        with:
          branch: chore/auto-translations
          title: "chore: auto-translate new strings"
          commit-message: "chore: auto-translate new strings"

Де ховається справжня робота

Крок translate-new-strings.sh – це місце, де відбувається власне переклад. Наївна версія цього скрипта читає кожен порожній запис, надсилає його до LLM API по одному рядку за раз і вставляє відповідь назад. Ця наївна версія і є тією пасткою, і варто чітко пояснити, чому.

Чому створені вручну виклики до LLM пошкоджують ваші файли

Коротка відповідь: сирі виклики LLM розглядають ваші рядки .po як прозу, а ваші рядки .po не є прозою – це структуровані дані з заповнювачами, формами множини та контекстом, які виклик чат-комплешн із задоволенням руйнує.

Пошкодження заповнювачів, яке ви не побачите в тестах

Надішліть Deleted %d of %s files на загальну кінцеву точку чату, і ви можете отримати у відповідь Supprimé %d des %s fichiers (добре) або Supprimé %d de % s fichiers (пробіл потрапив у заповнювач, і тепер sprintf() видає помилку під час виконання). Надішліть запис у множині, і модель може об'єднати дві форми в одну, порушуючи мови з більш ніж двома категоріями множини. Надішліть рядок з <a href="%s">, і модель може перекласти URL або видалити тег. Жодна з цих помилок не з'явиться у ваших тестах, якщо ви спеціально не перевіряєте відрендерений вивід у кожній локалі – вони з'являються як помилки виконання у продакшені для користувачів, від яких ви не можете отримати звіти про помилки.

Пастка обслуговування

Ви можете спробувати захиститися від цього за допомогою промпт-інжинірингу та пост-обробки регулярними виразами, і багато команд так роблять. Проблема в тому, що ви тепер підтримуєте крихкий механізм перекладу як побічний проект, заново відкриваючи кожен граничний випадок заповнювачів, який екосистема Gettext вже вирішила. Ми каталогізуємо конкретні способи спотворення змінних – і як їх запобігти – у статті переклад файлів PO без пошкодження кодових змінних. Урок тут застосовується безпосередньо: якість моделі рідко є проблемою; проблема в структурній обробці навколо неї.

Інтеграція хмарного перекладача з API у CI

Саме тут API-орієнтований сервіс перекладу замінює ваші здогадки з translate-new-strings.sh. Замість того, щоб вручну створювати виклики до LLM та обробляти регулярні вирази, ваш CI-крок завантажує змінений .po до сервісу, який вже розуміє структуру Gettext і повертає чистий результат. Форма пайплайну залишається ідентичною – вилучення, порівняння, переклад, збереження – але крихкий проміжний крок стає одним викликом API.

API-виклик, який замінює ваш скрипт

SimplePoTranslate створений саме для цього. Він надає хмарний API, придатний для автоматизації та CI, тому етап перекладу вашого робочого процесу стає запитом, який передає .po і отримує назад перекладений, без циклічного обходу кожного рядка. Його Syntax Locking автоматично утримує %s, %1$s, {count}, HTML та кодові токени на місці – весь клас помилок з попереднього розділу обробляється рушієм, а не регулярними виразами, які ви підтримуєте. Завдяки повній підтримці множини Gettext та msgctxt форми множини та контекст витримують повний цикл, чого не може гарантувати виклик чат-комплешн.

Ідемпотентність та шлюз перевірки залишаються вашими

Два дизайнерські рішення все ще залишаються за вами, незалежно від того, який двигун ви використовуєте. По-перше, ідемпотентність: продовжуйте перекладати лише порожні записи після msgmerge, щоб злиття без операцій не створювали різниці. По-друге, шлюз перевірки: нехай завдання відкриває pull request, як це робить наведений вище скелет, замість того, щоб зберігати прямо в main. Машинний переклад чудово підходить для швидкого запуску рядків, але людський погляд перед злиттям ловить рідкісні пропуски контексту – і PR дає вам цей погляд, не блокуючи звичайний випадок. Команди, що працюють з багатьма локалями або багатьма клієнтськими сайтами, впізнають цю форму з ідеального робочого процесу локалізації для агентств, де той самий шаблон автоматизації-потім-перегляду масштабується на десятки проектів.

Висновок

Щоб автоматизувати переклад у CI/CD без випуску пошкоджених збірок, шаблон є послідовним: відтворити .pot при злитті, msgmerge його в кожну локаль, щоб виявити лише нові рядки, перекласти лише ці рядки та зберегти результат за шлюзом перевірки pull request. Ідемпотентність зберігає ваші різниці чистими; шлюз перевірки не допускає рідкісних поганих перекладів у продакшен.

Частина, яку потрібно правильно налаштувати, – це сам етап перекладу. Створені вручну виклики LLM пошкодять заповнювачі та форми множини таким чином, що ваші тести не зможуть їх виявити, і ви витратите більше часу на підтримку перекладацької «склеювальної» логіки, ніж на код функції, якій вона служить. API-орієнтований механізм з Syntax Locking усуває весь цей режим збою, тому ваш пайплайн перекладає нові рядки під час того ж злиття, що їх і ввів – і ваші неангломовні користувачі припиняють бачити англійські заповнювачі.

Готові автоматизувати переклад у вашому пайплайні, не пошкоджуючи заповнювачі? Спробуйте SimplePoTranslate безкоштовно — кредитна картка не потрібна. Почніть з безкоштовного рівня, перевірте API на власних файлах .po та інтегруйте його в CI, коли будете готові.

Поділитися цією статтею