Why Automate Translation Workflows
Manual translation workflows don't scale. As products grow from supporting 3 languages to 15+, from 500 translation keys to 10,000+, and from quarterly releases to continuous deployment, human coordination bottlenecks emerge at every step. Developers forget to notify translators about new keys, translators wait for manual assignment, completed translations sit undeployed, missing translations escape detection until production, and status tracking requires constant manual updates across spreadsheets and emails.
Workflow automation eliminates these bottlenecks through event-driven architecture where systems communicate automatically. When developers push code with new translation keys, automated pipelines detect changes and trigger translation workflows. When translators complete work, webhooks notify downstream systems to deploy updates. When quality issues arise, automated checks flag problems immediately rather than during manual review cycles. This continuous, automated approach transforms localization from a periodic batch process into a seamless extension of software delivery.
Automation Opportunities in Translation Workflow
Development Phase
Automated Key Extraction: Scan code for new translation keys during development. Extract keys from source code, generate translation files, and create TMS entries automatically.
Missing Translation Detection: CI pipeline checks for hardcoded strings and validates all user-facing text uses i18n keys before allowing merge.
Format Validation: Automatically validate translation file syntax (JSON, YAML, PO) to prevent malformed files from reaching production.
Translation Phase
Automatic Assignment: When new keys appear, auto-assign to translators based on language expertise, workload, or round-robin distribution.
Notification Triggers: Send Slack/email notifications when new translation tasks become available, urgent translations are needed, or deadlines approach.
Machine Translation Pre-fill: Automatically generate MT suggestions for new keys to accelerate translator productivity.
Review Phase
Quality Check Automation: Run automated QA checks (placeholder matching, length limits, glossary compliance) before human review.
Auto-Approval: Configure rules to auto-approve translations that pass all quality checks from trusted translators.
Review Assignment: Route completed translations to appropriate reviewers based on language, domain expertise, or project ownership.
Deployment Phase
Auto-Export: When translations are approved, automatically export to target format and commit to repository.
CI/CD Integration: Translation updates trigger builds and deployments just like code changes.
OTA Updates: Push translation updates to mobile/web apps without full redeployment using over-the-air update mechanisms.
Cache Invalidation: Automatically invalidate CDN caches for translation files when updates publish.
Webhook Events and Handlers
What are Webhooks?
Webhooks are HTTP callbacks that TMS platforms send to your servers when specific events occur. Instead of polling the TMS API repeatedly to check for changes, your system receives real-time notifications pushed by the platform.
Webhook Flow:
- Event occurs in TMS (e.g., translation completed)
- TMS sends HTTP POST request to your configured endpoint
- Your server receives payload and processes event
- Your server responds with 200 OK to confirm receipt
Common Webhook Events
Translation Events:
translation.created: New translation addedtranslation.updated: Existing translation modifiedtranslation.approved: Translation passed reviewtranslation.rejected: Translation failed review
Key Events:
key.created: New translation key addedkey.updated: Key metadata modifiedkey.deleted: Translation key removed
Project Events:
project.created: New project initializedproject.updated: Project settings changedlanguage.added: New language added to projectlanguage.removed: Language removed from project
Export Events:
export.completed: Translation export finishedimport.completed: Translation import finished
Setting Up Webhook Endpoints
Express.js Example:
JavaScript1const express = require('express'); 2const crypto = require('crypto'); 3 4const app = express(); 5app.use(express.json()); 6 7// Webhook signature validation 8function validateSignature(payload, signature, secret) { 9 const hash = crypto 10 .createHmac('sha256', secret) 11 .update(JSON.stringify(payload)) 12 .digest('hex'); 13 14 return `sha256=${hash}` === signature; 15} 16 17app.post('/webhooks/intlpull', (req, res) => { 18 const signature = req.headers['x-intlpull-signature']; 19 const webhookSecret = process.env.INTLPULL_WEBHOOK_SECRET; 20 21 // Validate webhook authenticity 22 if (!validateSignature(req.body, signature, webhookSecret)) { 23 return res.status(401).json({ error: 'Invalid signature' }); 24 } 25 26 const { event, data } = req.body; 27 28 // Route to appropriate handler 29 switch (event) { 30 case 'translation.approved': 31 handleTranslationApproved(data); 32 break; 33 case 'key.created': 34 handleKeyCreated(data); 35 break; 36 case 'export.completed': 37 handleExportCompleted(data); 38 break; 39 default: 40 console.log(`Unhandled event: ${event}`); 41 } 42 43 // Acknowledge receipt 44 res.status(200).json({ received: true }); 45}); 46 47function handleTranslationApproved(data) { 48 const { projectId, keyId, language, value } = data; 49 50 console.log(`Translation approved: ${keyId} (${language})`); 51 52 // Trigger deployment pipeline 53 triggerDeploy(projectId, language); 54 55 // Notify team via Slack 56 notifySlack(`New translation approved for ${language}: ${keyId}`); 57 58 // Invalidate cache 59 invalidateCache(`translations/${language}`); 60} 61 62app.listen(3000, () => { 63 console.log('Webhook server listening on port 3000'); 64});
Security Best Practices:
- Signature Validation: Always verify webhook signatures to prevent spoofed requests
- HTTPS Only: Configure webhook URLs with HTTPS endpoints
- Secret Rotation: Rotate webhook secrets periodically
- IP Allowlisting: Restrict webhook requests to TMS platform IP ranges (if provided)
Webhook Retry and Reliability
Failed Delivery Handling: TMS platforms typically retry failed webhook deliveries with exponential backoff.
Retry Schedule Example:
- First failure: Retry after 1 minute
- Second failure: Retry after 5 minutes
- Third failure: Retry after 15 minutes
- Fourth failure: Retry after 1 hour
- Fifth+ failure: Retry every 6 hours for 48 hours, then give up
Best Practices:
- Respond with 200 OK quickly (< 5 seconds)
- Perform heavy processing asynchronously
- Implement idempotency (handle duplicate events gracefully)
- Log all webhook receipts for debugging
Idempotent Handler Example:
JavaScript1const processedWebhooks = new Set(); 2 3app.post('/webhooks/intlpull', async (req, res) => { 4 const webhookId = req.body.id; 5 6 // Check if already processed 7 if (processedWebhooks.has(webhookId)) { 8 console.log(`Duplicate webhook ignored: ${webhookId}`); 9 return res.status(200).json({ received: true }); 10 } 11 12 // Process webhook 13 await processWebhook(req.body); 14 15 // Mark as processed 16 processedWebhooks.add(webhookId); 17 18 res.status(200).json({ received: true }); 19});
CI/CD Pipeline Patterns
Pattern 1: Sync on Translation Approval
Workflow:
- Translator completes and approves translation
- Webhook fires:
translation.approved - Webhook handler triggers GitHub Actions workflow
- Workflow pulls latest translations from TMS API
- Commits translation files to repository
- Creates pull request for review
- After PR approval, merges and deploys
GitHub Actions Workflow:
YAML1name: Sync Translations 2 3on: 4 repository_dispatch: 5 types: [translation_approved] 6 7jobs: 8 sync: 9 runs-on: ubuntu-latest 10 steps: 11 - uses: actions/checkout@v3 12 13 - name: Download translations from IntlPull 14 run: | 15 curl -X GET "https://api.intlpull.com/v1/projects/$PROJECT_ID/export" \ 16 -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 17 -o translations.zip 18 19 - name: Extract translations 20 run: | 21 unzip translations.zip -d locales/ 22 23 - name: Create Pull Request 24 uses: peter-evans/create-pull-request@v5 25 with: 26 commit-message: 'i18n: sync translations from IntlPull' 27 title: 'Update translations' 28 body: 'Automated translation sync triggered by approval' 29 branch: translations/sync-${{ github.run_number }}
Trigger from Webhook:
JavaScript1// Webhook handler triggers GitHub Actions via repository_dispatch 2function handleTranslationApproved(data) { 3 const { Octokit } = require('@octokit/rest'); 4 const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); 5 6 octokit.repos.createDispatchEvent({ 7 owner: 'your-org', 8 repo: 'your-repo', 9 event_type: 'translation_approved', 10 client_payload: data 11 }); 12}
Pattern 2: Continuous Translation Sync
Workflow:
- Scheduled job runs every hour
- Checks for new/updated translations via TMS API
- If changes detected, pulls translations and commits to repo
- Triggers automated tests and deployment
GitHub Actions Scheduled Workflow:
YAML1name: Sync Translations (Scheduled) 2 3on: 4 schedule: 5 - cron: '0 * * * *' # Run every hour 6 workflow_dispatch: # Allow manual trigger 7 8jobs: 9 sync: 10 runs-on: ubuntu-latest 11 steps: 12 - uses: actions/checkout@v3 13 14 - name: Fetch translation status 15 id: status 16 run: | 17 LAST_UPDATE=$(curl -s -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 18 "https://api.intlpull.com/v1/projects/$PROJECT_ID/status" \ 19 | jq -r '.lastUpdated') 20 21 echo "last_update=$LAST_UPDATE" >> $GITHUB_OUTPUT 22 23 - name: Download if updated 24 if: steps.status.outputs.last_update > env.LAST_SYNC_TIME 25 run: | 26 # Download and extract translations 27 curl -X GET "https://api.intlpull.com/v1/projects/$PROJECT_ID/export" \ 28 -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 29 -o translations.zip 30 unzip translations.zip -d locales/ 31 32 - name: Commit changes 33 run: | 34 git config user.name "Translation Bot" 35 git config user.email "bot@intlpull.com" 36 git add locales/ 37 git diff --staged --quiet || git commit -m "i18n: sync translations" 38 git push
Pattern 3: Bidirectional Sync
Workflow:
- Developers push code with new translation keys
- CI pipeline extracts new keys and uploads to TMS
- Translators work in TMS
- Completed translations sync back to repository
Extract and Upload New Keys:
YAML1name: Extract Translation Keys 2 3on: 4 push: 5 branches: 6 - main 7 paths: 8 - 'src/**' 9 10jobs: 11 extract: 12 runs-on: ubuntu-latest 13 steps: 14 - uses: actions/checkout@v3 15 16 - name: Extract translation keys 17 run: npm run i18n:extract 18 19 - name: Upload new keys to IntlPull 20 run: | 21 curl -X POST "https://api.intlpull.com/v1/projects/$PROJECT_ID/keys/bulk" \ 22 -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 23 -H "Content-Type: application/json" \ 24 -d @extracted-keys.json
Key Extraction Script Example (React):
JavaScript1// scripts/extract-keys.js 2const fs = require('fs'); 3const parser = require('@babel/parser'); 4const traverse = require('@babel/traverse').default; 5 6const extractedKeys = new Set(); 7 8function extractFromFile(filePath) { 9 const code = fs.readFileSync(filePath, 'utf8'); 10 const ast = parser.parse(code, { 11 sourceType: 'module', 12 plugins: ['jsx', 'typescript'] 13 }); 14 15 traverse(ast, { 16 CallExpression(path) { 17 // Find t('key') calls 18 if (path.node.callee.name === 't') { 19 const keyArg = path.node.arguments[0]; 20 if (keyArg.type === 'StringLiteral') { 21 extractedKeys.add(keyArg.value); 22 } 23 } 24 } 25 }); 26} 27 28// Recursively extract from all source files 29// ... (walk directory and call extractFromFile) 30 31// Output to JSON 32const output = Array.from(extractedKeys).map(key => ({ 33 key, 34 language: 'en', 35 value: key // Placeholder, developer should provide real value 36})); 37 38fs.writeFileSync('extracted-keys.json', JSON.stringify(output, null, 2));
Slack and Email Notifications
Slack Notification Patterns
Pattern 1: New Translation Tasks Notify translators in Slack channel when new keys need translation.
JavaScript1const { WebClient } = require('@slack/web-api'); 2const slack = new WebClient(process.env.SLACK_BOT_TOKEN); 3 4async function notifyNewTranslations(language, keyCount) { 5 await slack.chat.postMessage({ 6 channel: '#translations', 7 text: `🌐 New translation task available`, 8 blocks: [ 9 { 10 type: 'section', 11 text: { 12 type: 'mrkdwn', 13 text: `*New translations needed*\n${keyCount} keys require translation to *${language}*` 14 } 15 }, 16 { 17 type: 'actions', 18 elements: [ 19 { 20 type: 'button', 21 text: { type: 'plain_text', text: 'Start Translating' }, 22 url: `https://app.intlpull.com/translate/${language}`, 23 style: 'primary' 24 } 25 ] 26 } 27 ] 28 }); 29}
Pattern 2: Translation Completion Alerts Notify product team when language reaches 100% completion.
JavaScript1async function notifyCompletionMilestone(language, projectName) { 2 await slack.chat.postMessage({ 3 channel: '#product', 4 text: `✅ ${language} translations complete for ${projectName}!`, 5 blocks: [ 6 { 7 type: 'section', 8 text: { 9 type: 'mrkdwn', 10 text: `:tada: *Translation milestone reached*\n${language} translations are now 100% complete for *${projectName}*. Ready for deployment!` 11 } 12 } 13 ] 14 }); 15}
Pattern 3: Quality Issue Alerts Escalate quality check failures to translation leads.
JavaScript1async function notifyQualityIssues(issues) { 2 const issueList = issues.map(i => `• ${i.key}: ${i.message}`).join('\n'); 3 4 await slack.chat.postMessage({ 5 channel: '#translation-quality', 6 text: `⚠️ Translation quality issues detected`, 7 blocks: [ 8 { 9 type: 'section', 10 text: { 11 type: 'mrkdwn', 12 text: `*Quality check failures*\n${issueList}` 13 } 14 } 15 ] 16 }); 17}
Email Notification System
Automated Email on Assignment:
JavaScript1const nodemailer = require('nodemailer'); 2 3const transporter = nodemailer.createTransport({ 4 host: process.env.SMTP_HOST, 5 port: 587, 6 auth: { 7 user: process.env.SMTP_USER, 8 pass: process.env.SMTP_PASS 9 } 10}); 11 12async function emailTranslationAssignment(translator, language, keyCount) { 13 await transporter.sendMail({ 14 from: '"IntlPull" <noreply@intlpull.com>', 15 to: translator.email, 16 subject: `New translation task: ${keyCount} keys in ${language}`, 17 html: ` 18 <h2>New Translation Assignment</h2> 19 <p>Hello ${translator.name},</p> 20 <p>You have been assigned ${keyCount} new translation keys for <strong>${language}</strong>.</p> 21 <p><a href="https://app.intlpull.com/translate/${language}">Start Translating →</a></p> 22 <p>Deadline: ${translator.deadline}</p> 23 ` 24 }); 25}
Auto-Publishing Translations
Automatic Deployment on Approval
Workflow:
- Translation approved in TMS
- Webhook triggers deployment pipeline
- Translations exported and committed to repository
- Automated build and deployment initiated
- New translations live in production
Deployment Script:
JavaScript1async function deployTranslations(projectId, language) { 2 // 1. Export translations from IntlPull 3 const response = await fetch( 4 `https://api.intlpull.com/v1/projects/${projectId}/export?language=${language}`, 5 { headers: { 'X-API-Key': process.env.INTLPULL_API_KEY } } 6 ); 7 8 const translations = await response.json(); 9 10 // 2. Write to file system 11 fs.writeFileSync( 12 `./public/locales/${language}.json`, 13 JSON.stringify(translations, null, 2) 14 ); 15 16 // 3. Build application 17 execSync('npm run build'); 18 19 // 4. Deploy to hosting (example: Vercel) 20 execSync('vercel --prod'); 21 22 // 5. Invalidate CDN cache 23 await invalidateCDN(`/locales/${language}.json`); 24}
Staged Deployments
Workflow:
- Translations approved → Deploy to staging automatically
- QA team validates on staging
- Manual approval triggers production deployment
GitHub Actions Multi-Environment:
YAML1name: Deploy Translations 2 3on: 4 repository_dispatch: 5 types: [translation_approved] 6 7jobs: 8 deploy-staging: 9 runs-on: ubuntu-latest 10 environment: staging 11 steps: 12 - name: Deploy to staging 13 run: ./deploy.sh staging 14 15 deploy-production: 16 runs-on: ubuntu-latest 17 needs: deploy-staging 18 environment: production 19 steps: 20 - name: Deploy to production 21 run: ./deploy.sh production
GitHub Actions for i18n
Action 1: Validate Translation Files
YAML1name: Validate Translations 2 3on: 4 pull_request: 5 paths: 6 - 'locales/**' 7 8jobs: 9 validate: 10 runs-on: ubuntu-latest 11 steps: 12 - uses: actions/checkout@v3 13 14 - name: Validate JSON syntax 15 run: | 16 for file in locales/**/*.json; do 17 echo "Validating $file" 18 jq empty "$file" || exit 1 19 done 20 21 - name: Check for missing keys 22 run: npm run i18n:check-missing 23 24 - name: Validate placeholders 25 run: npm run i18n:check-placeholders
Action 2: Auto-Generate Missing Translations with MT
YAML1name: Generate Missing Translations 2 3on: 4 schedule: 5 - cron: '0 2 * * *' # Daily at 2 AM 6 7jobs: 8 auto-translate: 9 runs-on: ubuntu-latest 10 steps: 11 - uses: actions/checkout@v3 12 13 - name: Find missing translations 14 id: missing 15 run: | 16 npm run i18n:find-missing > missing.json 17 18 - name: Generate translations via IntlPull API 19 if: steps.missing.outputs.count > 0 20 run: | 21 curl -X POST "https://api.intlpull.com/v1/projects/$PROJECT_ID/auto-translate" \ 22 -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 23 -d @missing.json 24 25 - name: Download generated translations 26 run: | 27 curl -X GET "https://api.intlpull.com/v1/projects/$PROJECT_ID/export" \ 28 -H "X-API-Key: ${{ secrets.INTLPULL_API_KEY }}" \ 29 -o translations.zip 30 31 - name: Create PR 32 uses: peter-evans/create-pull-request@v5 33 with: 34 title: 'i18n: auto-generated translations' 35 body: 'Machine-generated translations for missing keys. Review required.'
Action 3: Translation Coverage Report
YAML1name: Translation Coverage 2 3on: 4 push: 5 branches: [main] 6 pull_request: 7 8jobs: 9 coverage: 10 runs-on: ubuntu-latest 11 steps: 12 - uses: actions/checkout@v3 13 14 - name: Calculate coverage 15 id: coverage 16 run: | 17 npm run i18n:coverage > coverage.json 18 19 - name: Comment PR with coverage 20 if: github.event_name == 'pull_request' 21 uses: actions/github-script@v6 22 with: 23 script: | 24 const coverage = require('./coverage.json'); 25 const body = `## Translation Coverage\n\n${Object.entries(coverage).map(([lang, pct]) => `- ${lang}: ${pct}%`).join('\n')}`; 26 27 github.rest.issues.createComment({ 28 issue_number: context.issue.number, 29 owner: context.repo.owner, 30 repo: context.repo.repo, 31 body 32 });
IntlPull's Webhook and API Capabilities
IntlPull provides comprehensive automation infrastructure to enable fully automated translation workflows without custom development overhead.
Webhook Events
IntlPull fires webhooks for all critical events:
- Translation CRUD operations (created, updated, approved, rejected)
- Key lifecycle (created, updated, deleted)
- Language additions/removals
- Import/export completions
- Project changes
Configure webhook endpoints in IntlPull dashboard, and receive real-time notifications for all translation activity.
Comprehensive REST API
Translation Management:
- Create/update/delete translation keys
- Fetch translations by key, language, or project
- Bulk operations for efficiency
- Search and filter with advanced queries
Workflow Automation:
- Trigger auto-translation for specific languages
- Export translations in multiple formats
- Import translations from external systems
- Manage translation assignments and approvals
Analytics and Reporting:
- Translation progress by language
- Translator productivity metrics
- Quality check results
- Missing translation reports
GitHub Integration
IntlPull's native GitHub integration automates bidirectional sync:
- Detects new translation keys from commits
- Automatically creates translation tasks
- Commits completed translations via pull requests
- Syncs on configurable schedules or event triggers
No custom webhook handlers or API code required—IntlPull handles sync automatically.
Slack Integration
Connect IntlPull to Slack for automated notifications:
- New translation assignments posted to channels
- Completion milestones celebrated
- Quality issues escalated
- Daily/weekly progress summaries
Configure notification preferences and channel routing in IntlPull dashboard.
By providing these automation primitives out-of-box, IntlPull enables sophisticated workflows without requiring teams to build and maintain custom integration code.
Frequently Asked Questions
How do I prevent webhook duplicates from causing issues?
Implement idempotency in webhook handlers using unique webhook IDs. Store processed webhook IDs in database or cache, and skip processing if ID already seen. Most TMS platforms include unique ID in webhook payload. Also ensure handlers are safe to run multiple times (idempotent operations).
Should I use webhooks or polling for translation updates?
Use webhooks for real-time automation (notification, immediate deployment). Use polling for less time-sensitive operations (nightly sync, reporting). Webhooks reduce latency and server load compared to frequent polling. However, polling provides simpler implementation for teams without webhook infrastructure.
How can I test webhook handlers locally during development?
Use tools like ngrok or localtunnel to expose local development server to internet with temporary public URL. Configure TMS to send webhooks to ngrok URL. Test webhook handling locally before deploying to production. Many TMS platforms also provide webhook testing/replay features in their dashboard.
What happens if my webhook endpoint is down?
Most TMS platforms retry failed webhooks with exponential backoff for 24-48 hours. Monitor webhook delivery logs in TMS dashboard to detect failures. Implement health checks and alerting for webhook endpoints. For critical workflows, consider message queue backup (TMS publishes to queue, your workers process asynchronously).
Can I automate translator assignments based on workload?
Yes, use APIs to query current translator workloads (pending task counts) and assign new tasks to translators with capacity. Implement round-robin, least-busy-first, or language expertise-based assignment algorithms. IntlPull's API provides translator status and task metrics for building intelligent assignment logic.
How do I handle merge conflicts when automatically committing translations?
Use git strategies like rebase or merge, and configure automated conflict resolution for simple cases (e.g., accept incoming changes for translation files). For complex conflicts, create pull request instead of direct commit, allowing human review. Organize translations by namespace/feature to minimize conflicts.
Should automated translations be auto-approved or require review?
Machine-translated content should require human review before deployment. Auto-approve only trusted translator submissions that pass all quality checks. Configure approval rules based on translator seniority, key importance (critical UI vs. help docs), and QA check results. Balance speed with quality based on content type.
