Version Control Integration
Connect your code repository to Translato for seamless, two-way translation sync. When a developer pushes a new key to the codebase, it automatically appears in the TMS for translation. When translators finish their work, translations can be pushed back as a Pull Request.
Supported Providers
| Provider | Pull (repo → TMS) | Push (TMS → repo) | OAuth | Webhook |
|---|---|---|---|---|
| GitHub | ✅ | ✅ | ✅ | ✅ |
| GitLab | ✅ | ✅ | ✅ | ✅ |
| Bitbucket | ✅ | ✅ | — | ✅ |
Supported File Formats
| Format | Extensions | Structure |
|---|---|---|
| JSON | .json | Flat ("key": "value") or nested ("section": { "key": "value" }) |
| YAML | .yml, .yaml | Flat or locale-keyed (en: / de: top-level) |
| PO/Gettext | .po, .pot | Standard msgid/msgstr pairs |
| Properties | .properties | Java-style key=value lines |
Quick Start
1. Open VCS Settings
Navigate to your project → click "Version Control" in the sidebar menu.
2. Connect Your Repository
You have two options:
Option A — OAuth (Recommended for GitHub/GitLab)
Click "Connect with GitHub" or "Connect with GitLab". You'll be redirected to authorize Translato. After authorizing, you'll return to the setup wizard with:
- Your repositories listed in a dropdown
- An access token filled in automatically
- The default branch pre-selected
Option B — Manual Setup
- Paste your repository URL (e.g.
https://github.com/acme/webapp) - Set the branch to watch (default:
main) - Optionally add a Personal Access Token (required for private repos)
3. Configure File Paths
Tell Translato where your translation files live using glob patterns:
src/locales/*.json → matches src/locales/en.json, src/locales/de.json
public/locales/**/*.json → matches public/locales/en/common.json, etc.
i18n/*.yaml → matches i18n/en.yaml, i18n/fr.yaml
translations/*.po → matches translations/de.po, translations/es.po
The * wildcard is replaced with the language code when generating files. The ** wildcard matches any directory depth.
Pick your file format (JSON, YAML, PO, or Properties) and add one or more glob patterns. You can also click the suggested presets.
4. Connect
Click "Connect Repository" — Translato will:
- Run an initial sync, pulling all existing keys from your repo
- Show you a webhook URL to copy
5. Add the Webhook
Copy the webhook URL from the status card and add it to your repository settings:
<details> <summary><strong>GitHub</strong></summary>- Go to your repo → Settings → Webhooks → Add webhook
- Paste the webhook URL into Payload URL
- Set Content type to
application/json - Select "Just the push event"
- Click Add webhook
- Go to your repo → Settings → Webhooks
- Paste the webhook URL into URL
- The webhook secret is embedded in the URL — you can also enter it in the Secret token field for extra verification
- Check Push events
- Click Add webhook
- Go to your repo → Repository settings → Webhooks → Add webhook
- Give it a title (e.g. "Translato")
- Paste the webhook URL
- Select Repository push trigger
- Click Save
How It Works
Pull: Repository → TMS
Developer pushes code with new translation key
│
▼
Repository sends webhook to Translato
│
▼
Translato verifies webhook (HMAC signature + URL secret)
│
▼
Fetches modified i18n files from the repository API
│
▼
Parses keys from the files (JSON/YAML/PO/Properties)
│
▼
Diffs with existing keys in the project
│
├── New keys → added to the project (tagged "vcs-synced")
├── Existing keys → unchanged
└── Removed keys → tagged "vcs-removed" (not deleted)
Key behaviors:
- Only files matching your configured glob patterns are processed
- Only pushes to the configured branch trigger a sync (e.g.
main) - Keys removed from the repo are tagged, not deleted — translators review before cleanup
- The webhook responds with
202 Acceptedimmediately; sync runs asynchronously
Push: TMS → Repository
Translator completes translations
│
▼
Click "Push to repo" → select languages
│
▼
Translato generates i18n files from the database
│
▼
Creates a new branch (translato/translations-2026-03-17T...)
│
▼
Commits all translation files to the branch
│
▼
Opens a Pull Request targeting your configured branch
│
▼
Team reviews and merges the PR
The PR includes:
- One file per selected language (e.g.
src/locales/de.json,src/locales/fr.json) - Files generated in your configured format with proper nesting
- A descriptive PR body listing all changed files and languages
Manual Sync
Click the "Sync now" button on the VCS status card to trigger a full sync anytime. This is useful when:
- Setting up for the first time
- The webhook missed an event
- You want to re-sync after changing file path patterns
Sync History
Every sync event is logged with:
| Field | Description |
|---|---|
| Status | ✅ Success, ❌ Failed, ⏭ Skipped, 🔄 In Progress |
| Commit | SHA + message from the triggering push |
| Author | Who pushed the commit |
| Branch | Which branch was pushed to |
| Trigger | webhook, manual, or setup |
| Keys added | Number of new keys discovered |
| Keys flagged | Keys tagged as "vcs-removed" |
| Files processed | Number of i18n files parsed |
OAuth Setup (Admin)
To enable the "Connect with GitHub/GitLab" buttons, configure OAuth credentials in your environment:
GitHub OAuth App
- Go to GitHub Developer Settings
- Click "New OAuth App"
- Set:
- Application name:
Translato - Homepage URL:
https://your-translato-domain.com - Authorization callback URL:
https://your-api-domain.com/api/vcs/oauth/callback/github
- Application name:
- Copy the Client ID and Client Secret
GitLab OAuth Application
- Go to GitLab Applications
- Create a new application:
- Name:
Translato - Redirect URI:
https://your-api-domain.com/api/vcs/oauth/callback/gitlab - Scopes:
api,read_repository,write_repository
- Name:
- Copy the Application ID and Secret
Environment Variables
# GitHub OAuth (optional — enables "Connect with GitHub" button)
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
# GitLab OAuth (optional — enables "Connect with GitLab" button)
GITLAB_CLIENT_ID=your_gitlab_application_id
GITLAB_CLIENT_SECRET=your_gitlab_secret
# Public URLs for OAuth callbacks
APP_PUBLIC_URL=https://your-translato-domain.com
API_PUBLIC_URL=https://your-api-domain.com
If these are not configured, the OAuth buttons won't appear — users can still connect manually with a Personal Access Token.
API Reference
All VCS endpoints require cookie-based authentication unless noted.
Get Integration
GET /api/projects/:projectId/vcs
Returns the VCS integration for a project, including the webhook URL.
Create Integration
POST /api/projects/:projectId/vcs
{
"repoUrl": "https://github.com/acme/webapp",
"branch": "main",
"filePaths": ["src/locales/*.json"],
"fileFormat": "json",
"authToken": "ghp_xxxx",
"autoSync": true
}
Update Integration
PUT /api/projects/:projectId/vcs
{
"branch": "develop",
"filePaths": ["src/locales/*.json", "lib/i18n/*.json"],
"autoSync": false
}
Delete Integration
DELETE /api/projects/:projectId/vcs
Trigger Manual Sync
POST /api/projects/:projectId/vcs/sync
Push Translations (Create PR)
POST /api/projects/:projectId/vcs/push
{
"languages": ["de", "fr", "es"]
}
Response:
{
"result": {
"provider": "GITHUB",
"prUrl": "https://github.com/acme/webapp/pull/42",
"prNumber": 42,
"branchName": "translato/translations-2026-03-17T01-30-00",
"filesUpdated": 3,
"languagesPushed": ["de", "fr", "es"]
}
}
Get Sync Logs
GET /api/projects/:projectId/vcs/logs?page=1&limit=20
Webhook Receiver (Public — No Auth)
POST /api/webhooks/vcs/:projectId/:webhookSecret
This endpoint is called by your VCS provider when code is pushed. It verifies the webhook via:
- The secret embedded in the URL path
- Provider-specific HMAC signatures (GitHub:
X-Hub-Signature-256, GitLab:X-Gitlab-Token, Bitbucket:X-Hub-Signature)
Security
- Webhook verification: Double verification — secret URL segment + provider HMAC signatures
- OAuth state parameter: CSRF-protected with random nonce and 10-minute expiry
- Token storage: Access tokens are stored in the database (encrypt at rest in production)
- Cookie transport: OAuth session data passed via httpOnly cookies with 5-minute TTL
- Branch filtering: Only syncs pushes to the configured branch (ignores feature branches)
- No auto-delete: Removed keys are tagged, not deleted — prevents accidental data loss
Troubleshooting
Webhook not firing
- Verify the webhook URL is correct (check the status card)
- Ensure the webhook is configured for push events only
- Check the webhook delivery log in your provider's settings
- Make sure the API server is publicly accessible
Sync shows "Could not fetch" errors
- The repository may be private — add an access token in the integration settings
- The file path patterns may not match any files — check glob patterns
- The token may have expired — update it in the settings
Push fails with "write-access token required"
- The push feature requires a token with write permissions
- GitHub: token needs
reposcope - GitLab: token needs
apiorwrite_repositoryscope - Bitbucket: token needs repository write permissions
OAuth redirect fails
- Verify
APP_PUBLIC_URLandAPI_PUBLIC_URLenvironment variables - Check that the OAuth callback URL matches exactly in your provider's settings
- Ensure the OAuth app has the correct scopes
Keys showing as "vcs-removed" incorrectly
- This happens during full sync (manual/setup) when keys exist in the DB but not in the repo files
- If your repo only contains the source language file, keys from other language files won't be found
- Use more specific file patterns to avoid false positives