Skip to main content
Technology & EngineeringI18n Services246 lines

Transifex

"Transifex: translation management, Native SDK, OTA delivery, API v3, CLI, GitHub/GitLab integration, ICU plurals"

Quick Summary11 lines
Transifex is a cloud-based translation management platform focused on continuous localization. Its Native SDK enables over-the-air (OTA) delivery of translations without redeployment — source strings are collected automatically from your code at runtime, sent to Transifex for translation, and delivered back to the application in real time. Transifex also supports traditional file-based workflows via its CLI and API, with integrations for GitHub, GitLab, and Bitbucket that automate the sync between your repository and the translation platform. It handles ICU MessageFormat, plurals, and context metadata natively.

## Key Points

- Use Transifex Native for web apps to eliminate the file sync step entirely — strings are collected and delivered over the wire automatically
- Set `minimum_perc` in `.tx/config` so partially translated locales are not pulled into production, preventing users from seeing half-translated UIs
- Add `_context` to ambiguous source strings (e.g., "File", "Save", "Post") so translators know the intended meaning
- Calling `t()` before `tx.init()` completes returns source strings only — ensure initialization finishes before rendering translated content
- Pushing translations with `tx push --translation` without `--force` skips strings that already have translations, which can silently drop migration data if you expect overwrites
skilldb get i18n-services-skills/TransifexFull skill: 246 lines
Paste into your CLAUDE.md or agent config

Transifex

Core Philosophy

Transifex is a cloud-based translation management platform focused on continuous localization. Its Native SDK enables over-the-air (OTA) delivery of translations without redeployment — source strings are collected automatically from your code at runtime, sent to Transifex for translation, and delivered back to the application in real time. Transifex also supports traditional file-based workflows via its CLI and API, with integrations for GitHub, GitLab, and Bitbucket that automate the sync between your repository and the translation platform. It handles ICU MessageFormat, plurals, and context metadata natively.

Setup

CLI installation and configuration

# Install Transifex CLI (tx)
curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash
# Or: pip install transifex-client
# Or: brew install transifex-cli

# Initialize project
tx init
# Creates .tx/config in your project root

# Authenticate
tx login
# Stores token in ~/.transifexrc

File-based project configuration

# .tx/config
[main]
host = https://app.transifex.com

[o:my-org:p:my-project:r:messages]
file_filter = src/locales/<lang>.json
source_file = src/locales/en.json
source_lang = en
type = KEYVALUEJSON
minimum_perc = 80

Transifex Native SDK setup (JavaScript)

import { tx, t } from "@transifex/native";

// Initialize the Native SDK
tx.init({
  token: process.env.TRANSIFEX_TOKEN,
  // Optional: filter by tags
  filterTags: "web-app",
  // Optional: missing translation policy
  missingPolicy: { handle: (sourceString) => sourceString },
});

// Set the active locale
await tx.setCurrentLocale("fr");

// Fetch available languages
const languages = await tx.getLanguages();
// [{ code: "fr", name: "French" }, { code: "de", name: "German" }, ...]

Core Patterns

Using Transifex Native for runtime translations

import { t } from "@transifex/native";

// Simple translation
const greeting = t("Hello, world!");

// With parameters
const welcome = t("Welcome, {username}!", { username: "Alice" });

// With context to disambiguate identical source strings
const menuFile = t("File", { _context: "menu" });
const documentFile = t("File", { _context: "document-type" });

// With ICU plural support
const itemCount = t(
  "{count, plural, one {# item} other {# items}} in your cart",
  { count: 3 }
);

// With tags for filtering which strings load
const onboarding = t("Complete your profile", { _tags: "onboarding,v2" });

// With max character limit hint for translators
const buttonLabel = t("Submit", { _charlimit: 10 });

React integration

import { T, useLanguages, useTX } from "@transifex/react";
import { tx } from "@transifex/native";

function LanguageSwitcher() {
  const languages = useLanguages();
  const currentLocale = useTX().currentLocale;

  return (
    <select
      value={currentLocale}
      onChange={(e) => tx.setCurrentLocale(e.target.value)}
    >
      {languages.map((lang) => (
        <option key={lang.code} value={lang.code}>
          {lang.name}
        </option>
      ))}
    </select>
  );
}

function WelcomePage({ user }) {
  return (
    <div>
      <h1>
        <T _str="Welcome, {username}!" username={user.name} />
      </h1>
      <p>
        <T
          _str="{count, plural, one {# notification} other {# notifications}}"
          count={user.notificationCount}
        />
      </p>
    </div>
  );
}

CLI workflow for file-based projects

# Push source strings to Transifex
tx push --source

# Push existing translations (e.g., migrating from another system)
tx push --translation --languages fr,de,es

# Pull translations (only locales above minimum_perc threshold)
tx pull --all --minimum-perc 80

# Pull a specific language
tx pull --languages fr

# Check translation status
tx status

API v3 for automation

const TRANSIFEX_API = "https://rest.api.transifex.com";
const headers = {
  Authorization: `Bearer ${process.env.TRANSIFEX_API_TOKEN}`,
  "Content-Type": "application/vnd.api+json",
};

// List resources in a project
const resources = await fetch(
  `${TRANSIFEX_API}/resources?filter[project]=o:my-org:p:my-project`,
  { headers }
).then((r) => r.json());

// Get translation stats for a resource
const stats = await fetch(
  `${TRANSIFEX_API}/resource_language_stats?filter[project]=o:my-org:p:my-project&filter[resource]=o:my-org:p:my-project:r:messages`,
  { headers }
).then((r) => r.json());

for (const entry of stats.data) {
  const locale = entry.relationships.language.data.id.replace("l:", "");
  const { translated_strings, total_strings } = entry.attributes;
  const pct = ((translated_strings / total_strings) * 100).toFixed(1);
  console.log(`${locale}: ${pct}% translated`);
}

GitHub integration

# .github/workflows/transifex-sync.yml
name: Transifex Sync
on:
  push:
    branches: [main]
    paths: ["src/locales/en.json"]
  schedule:
    - cron: "0 */6 * * *" # Pull translations every 6 hours

jobs:
  push-source:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: transifex/cli-action@v2
        with:
          token: ${{ secrets.TX_TOKEN }}
      - run: tx push --source

  pull-translations:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: transifex/cli-action@v2
        with:
          token: ${{ secrets.TX_TOKEN }}
      - run: tx pull --all --minimum-perc 80
      - uses: peter-evans/create-pull-request@v5
        with:
          title: "chore: update translations from Transifex"
          branch: transifex/translations-update

Best Practices

  • Use Transifex Native for web apps to eliminate the file sync step entirely — strings are collected and delivered over the wire automatically
  • Set minimum_perc in .tx/config so partially translated locales are not pulled into production, preventing users from seeing half-translated UIs
  • Add _context to ambiguous source strings (e.g., "File", "Save", "Post") so translators know the intended meaning

Common Pitfalls

  • Calling t() before tx.init() completes returns source strings only — ensure initialization finishes before rendering translated content
  • Pushing translations with tx push --translation without --force skips strings that already have translations, which can silently drop migration data if you expect overwrites

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 →