Push a change to your .pot file. The pipeline translates it and opens a merge request with the results.
The job in .gitlab-ci.yml is the recipe PTC prints on its CI/CD setup screen, copied here unchanged.
.gitlab-ci.yml the pipeline
.ptc-config.yml what to translate, and where results go
languages/
examplewp.pot source strings
examplewp-*.po translations, as PTC delivers them
examplewp-*.mo
source_locale: en
files:
- file: languages/examplewp.pot
output: languages/examplewp-{{lang}}.po
additional_translation_files:
- type: mo
path: languages/examplewp-{{lang}}.mo
- type: wp
path: languages/examplewp-{{lang}}.json
- type: php
path: languages/examplewp-{{lang}}.l10n.phpOne .pot in, four files out per language. {{lang}} becomes de_DE, es, fr_FR…
That's the WordPress part: the .mo WordPress loads, the .json its JavaScript reads, and the .l10n.php that WordPress 6.5+ prefers — all from one translation pass, no msgfmt in your build.
- Copy
.gitlab-ci.ymland.ptc-config.yml, point the config at your.pot. - Settings → CI/CD → Variables → add
PTC_API_TOKEN(masked; protected if your default branch is). - Let the job push. It uses
CI_JOB_TOKEN, which needs Settings → CI/CD → Job token permissions → Allow Git push requests to the repository (GitLab 18.4+, off by default). Prefer not to? AddPTC_GIT_PUSH_TOKEN— a project access token withwrite_repository.
The job runs only on a push to the default branch, pushes to ptc/translations, and skips any commit marked [skip translations] — which is what its own commit says.
The marker is checked in rules:, not written as [skip ci]: [skip ci] would create no pipeline at all, leaving the translation MR untested and unmergeable wherever Pipelines must succeed is on.
The CLI writes a manifest of the files it produced, and the commit stages that — not git add .. Anything else in the working tree stays out, including the CLI itself, which is downloaded to /tmp.
Use the PTC Translate action.