Skip to main content
Technology & EngineeringI18n Services271 lines

Weblate

"Weblate: open-source translation management, VCS integration, automatic suggestions, quality checks, API, glossary, translation memory"

Quick Summary11 lines
Weblate is an open-source, self-hostable translation management system with tight version control integration. Unlike SaaS-only platforms, Weblate can be self-hosted or used via the hosted instance at hosted.weblate.org. It integrates directly with Git, GitHub, GitLab, Bitbucket, and other VCS providers, committing translations back to your repository automatically. Weblate provides built-in quality checks (format strings, placeholders, length, glossary consistency), automatic translation suggestions via machine translation backends (DeepL, Google Translate, LibreTranslate), and translation memory shared across projects. It supports 50+ file formats including PO, JSON, XLIFF, Android XML, iOS strings, Fluent, and more.

## Key Points

- Lock components during release deployments to prevent translators from editing strings that are about to ship, then unlock after the deploy completes
- Enable translation memory sharing across projects so common strings ("Cancel", "Save", "Loading...") are translated consistently without duplicating effort
- Use Weblate's glossary feature to define authoritative translations for brand names, technical terms, and domain-specific vocabulary
- Setting up the push URL incorrectly causes Weblate to commit translations locally but never push them upstream, making it appear that translations are lost after a repository re-sync
- Running multiple Weblate components against the same repository branch without configuring merge handling leads to push conflicts between components
skilldb get i18n-services-skills/WeblateFull skill: 271 lines
Paste into your CLAUDE.md or agent config

Weblate

Core Philosophy

Weblate is an open-source, self-hostable translation management system with tight version control integration. Unlike SaaS-only platforms, Weblate can be self-hosted or used via the hosted instance at hosted.weblate.org. It integrates directly with Git, GitHub, GitLab, Bitbucket, and other VCS providers, committing translations back to your repository automatically. Weblate provides built-in quality checks (format strings, placeholders, length, glossary consistency), automatic translation suggestions via machine translation backends (DeepL, Google Translate, LibreTranslate), and translation memory shared across projects. It supports 50+ file formats including PO, JSON, XLIFF, Android XML, iOS strings, Fluent, and more.

Setup

Self-hosting with Docker

# Clone Weblate Docker repository
git clone https://github.com/WeblateOrg/docker-compose.git weblate-docker
cd weblate-docker

# Configure environment
cp .env.example .env

# Edit .env with required settings:
# WEBLATE_ADMIN_EMAIL=admin@example.com
# WEBLATE_ADMIN_PASSWORD=your-admin-password
# WEBLATE_SITE_DOMAIN=weblate.example.com
# WEBLATE_EMAIL_HOST=smtp.example.com
# WEBLATE_DEFAULT_FROM_EMAIL=weblate@example.com
# WEBLATE_SECRET_KEY=your-secret-key

# Start Weblate
docker-compose up -d

# Weblate is available at http://localhost:8080

Adding a translation project via the API

const WEBLATE_URL = "https://weblate.example.com/api";
const headers = {
  Authorization: `Token ${process.env.WEBLATE_API_TOKEN}`,
  "Content-Type": "application/json",
};

// Create a project
const project = await fetch(`${WEBLATE_URL}/projects/`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "My App",
    slug: "my-app",
    web: "https://github.com/my-org/my-app",
  }),
}).then((r) => r.json());

// Add a translation component (links to your repository)
const component = await fetch(
  `${WEBLATE_URL}/projects/my-app/components/`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({
      name: "Frontend",
      slug: "frontend",
      repo: "https://github.com/my-org/my-app.git",
      branch: "main",
      filemask: "src/locales/*.json",
      template: "src/locales/en.json",
      file_format: "json-nested",
      new_lang: "add",
      push: "git@github.com:my-org/my-app.git",
      push_branch: "weblate-translations",
    }),
  }
).then((r) => r.json());

Core Patterns

API-driven translation management

const WEBLATE_URL = "https://weblate.example.com/api";
const headers = {
  Authorization: `Token ${process.env.WEBLATE_API_TOKEN}`,
  "Content-Type": "application/json",
};

// List translations for a component
const translations = await fetch(
  `${WEBLATE_URL}/components/my-app/frontend/translations/`,
  { headers }
).then((r) => r.json());

for (const t of translations.results) {
  console.log(
    `${t.language.name}: ${t.translated_percent}% translated, ` +
    `${t.failing_checks} failing checks`
  );
}

// Get translation units for a specific locale
const units = await fetch(
  `${WEBLATE_URL}/translations/my-app/frontend/fr/units/`,
  { headers }
).then((r) => r.json());

// Update a specific translation unit
await fetch(`${WEBLATE_URL}/units/${units.results[0].id}/`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({
    target: ["Bonjour le monde"],
    state: 20, // 20 = translated, 30 = approved
  }),
});

Triggering repository operations

// Force pull from upstream repository
await fetch(
  `${WEBLATE_URL}/components/my-app/frontend/repository/`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({ operation: "pull" }),
  }
);

// Commit pending translations
await fetch(
  `${WEBLATE_URL}/components/my-app/frontend/repository/`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({ operation: "commit" }),
  }
);

// Push committed translations to upstream
await fetch(
  `${WEBLATE_URL}/components/my-app/frontend/repository/`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({ operation: "push" }),
  }
);

Configuring quality checks

# settings.py (Weblate configuration)
# Weblate includes 50+ built-in quality checks. Enable/disable per component.

# Check categories:
# - Format checks: Python format, C format, ICU MessageFormat, etc.
# - Length checks: maximum length, unchanged translation
# - Consistency: same translation for same source across components
# - Glossary: flag translations that deviate from glossary terms
# - Punctuation: trailing/leading spaces, ellipsis, etc.

# Custom check via Weblate addon API:
# Addons -> Add addon -> "Automatic translation" or custom script

# Example: enforce max character length via component settings
# In component settings:
#   check_flags = "max-length:100"
#   enforced_checks = ["max_length", "placeholders"]

Machine translation integration

# settings.py — configure automatic suggestion backends

# DeepL
MT_SERVICES = ("weblate.machinery.deepl.DeepLTranslation",)
MT_DEEPL_KEY = "your-deepl-api-key"

# Google Translate
# MT_SERVICES = ("weblate.machinery.google.GoogleTranslation",)
# MT_GOOGLE_KEY = "your-google-api-key"

# LibreTranslate (self-hosted, no API key needed)
# MT_SERVICES = ("weblate.machinery.libretranslate.LibreTranslateTranslation",)
# MT_LIBRETRANSLATE_API_URL = "http://libretranslate.local"

# Multiple backends simultaneously
# MT_SERVICES = (
#     "weblate.machinery.deepl.DeepLTranslation",
#     "weblate.machinery.weblatetm.WeblateTranslation",  # Weblate TM
# )

CI/CD integration with wlc (Weblate Client)

# Install Weblate client
pip install wlc

# Configure authentication
# ~/.config/weblate:
# [weblate]
# url = https://weblate.example.com/api/
# key = your-api-token

# Lock component before deployment (prevent translation edits)
wlc lock my-app/frontend

# Pull latest translations
wlc download my-app/frontend --output src/locales/

# Unlock after deployment
wlc unlock my-app/frontend

# Check component status in CI
wlc stats my-app/frontend

Webhook for build triggers

# .github/workflows/weblate-pull.yml
name: Pull Weblate Translations
on:
  repository_dispatch:
    types: [weblate]

jobs:
  pull:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: main
          fetch-depth: 0
      - name: Merge Weblate branch
        run: |
          git fetch origin weblate-translations
          git merge origin/weblate-translations --no-edit
          git push origin main

Best Practices

  • Lock components during release deployments to prevent translators from editing strings that are about to ship, then unlock after the deploy completes
  • Enable translation memory sharing across projects so common strings ("Cancel", "Save", "Loading...") are translated consistently without duplicating effort
  • Use Weblate's glossary feature to define authoritative translations for brand names, technical terms, and domain-specific vocabulary

Common Pitfalls

  • Setting up the push URL incorrectly causes Weblate to commit translations locally but never push them upstream, making it appear that translations are lost after a repository re-sync
  • Running multiple Weblate components against the same repository branch without configuring merge handling leads to push conflicts between components

Anti-Patterns

Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.

Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.

Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.

Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.

Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.

Install this skill directly: skilldb add i18n-services-skills

Get CLI access →