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

ProviderPull (repo → TMS)Push (TMS → repo)OAuthWebhook
GitHub
GitLab
Bitbucket

Supported File Formats

FormatExtensionsStructure
JSON.jsonFlat ("key": "value") or nested ("section": { "key": "value" })
YAML.yml, .yamlFlat or locale-keyed (en: / de: top-level)
PO/Gettext.po, .potStandard msgid/msgstr pairs
Properties.propertiesJava-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

  1. Paste your repository URL (e.g. https://github.com/acme/webapp)
  2. Set the branch to watch (default: main)
  3. 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:

  1. Run an initial sync, pulling all existing keys from your repo
  2. 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>
  1. Go to your repo → SettingsWebhooksAdd webhook
  2. Paste the webhook URL into Payload URL
  3. Set Content type to application/json
  4. Select "Just the push event"
  5. Click Add webhook
</details> <details> <summary><strong>GitLab</strong></summary>
  1. Go to your repo → SettingsWebhooks
  2. Paste the webhook URL into URL
  3. The webhook secret is embedded in the URL — you can also enter it in the Secret token field for extra verification
  4. Check Push events
  5. Click Add webhook
</details> <details> <summary><strong>Bitbucket</strong></summary>
  1. Go to your repo → Repository settingsWebhooksAdd webhook
  2. Give it a title (e.g. "Translato")
  3. Paste the webhook URL
  4. Select Repository push trigger
  5. Click Save
</details>

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 Accepted immediately; 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:

FieldDescription
Status✅ Success, ❌ Failed, ⏭ Skipped, 🔄 In Progress
CommitSHA + message from the triggering push
AuthorWho pushed the commit
BranchWhich branch was pushed to
Triggerwebhook, manual, or setup
Keys addedNumber of new keys discovered
Keys flaggedKeys tagged as "vcs-removed"
Files processedNumber of i18n files parsed

OAuth Setup (Admin)

To enable the "Connect with GitHub/GitLab" buttons, configure OAuth credentials in your environment:

GitHub OAuth App

  1. Go to GitHub Developer Settings
  2. Click "New OAuth App"
  3. Set:
    • Application name: Translato
    • Homepage URL: https://your-translato-domain.com
    • Authorization callback URL: https://your-api-domain.com/api/vcs/oauth/callback/github
  4. Copy the Client ID and Client Secret

GitLab OAuth Application

  1. Go to GitLab Applications
  2. Create a new application:
    • Name: Translato
    • Redirect URI: https://your-api-domain.com/api/vcs/oauth/callback/gitlab
    • Scopes: api, read_repository, write_repository
  3. 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:

  1. The secret embedded in the URL path
  2. 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 repo scope
  • GitLab: token needs api or write_repository scope
  • Bitbucket: token needs repository write permissions

OAuth redirect fails

  • Verify APP_PUBLIC_URL and API_PUBLIC_URL environment 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