Overview
Lingo Pulse is an i18n observability dashboard. Like Datadog, but for your translation coverage. Connect a GitHub repository, and Lingo Pulse will scan every locale file on every push, score translation quality via the Lingo.dev SDK, and alert you when a language breaks.
Built on Next.js, Supabase, and the @lingo.dev/_sdk. All open source.
Quick start
1. Clone and install
git clone https://github.com/your-org/lingo-pulse cd lingo-pulse npm install
2. Configure environment variables
Copy the example file and fill in your credentials:
cp .env.example .env.local
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key SUPABASE_SERVICE_ROLE_KEY=your_service_role_key LINGO_DEV_API_KEY=your_lingo_dev_key GITHUB_WEBHOOK_SECRET=any_random_secret
3. Run the database migrations
Create a Supabase project, open the SQL editor, and run all 4 migration files in order:
supabase/migrations/001_initial.sql supabase/migrations/002_owner_scoping.sql supabase/migrations/003_pr_checks_repo_pr_unique.sql supabase/migrations/004_translation_incidents.sql
4. Start the dev server
npm run dev # → http://localhost:3000
5. Connect a repository
Visit /connect, sign in with GitHub, paste your repository URL, and optionally add a GitHub PAT with admin:repo_hook scope to enable automatic webhook registration.
How analysis works
When a repository is connected (or a webhook push event fires), Lingo Pulse fetches the full file tree via the GitHub API and finds every locale file matching **/*.json or **/*.yaml under common i18n directories.
Coverage score
For each locale, the engine compares keys against the source locale (usually en). Coverage = translated keys ÷ total source keys × 100.
Quality score
Translated strings are passed to the Lingo.dev SDK for quality analysis. The SDK detects placeholder leaks, fallback copy left in source language, empty values, and format mismatches. Each file receives a 0–100 quality score.
Incident detection
A locale is flagged as an incident when its coverage drops below the threshold (default 80%) or its quality score drops more than 10 points between two consecutive analysis runs.
PR Checks
When you sync PRs, Lingo Pulse analyzes any open pull requests that touch locale files. It posts a comment on each PR with:
- Coverage before and after the changes
- Number of missing keys introduced or resolved
- Status: passing, warning, or failing
The comment updates automatically when you re-sync PRs. This helps reviewers catch translation regressions before merging.
Webhooks
Lingo Pulse registers a GitHub webhook on your repository pointing to /api/webhooks/github. Push and pull-request events trigger a fresh analysis run automatically.
Requirements
Local testing with ngrok
npx ngrok http 3000 # copy the https://xxxx.ngrok.io URL # → update NEXT_PUBLIC_APP_URL in .env.local # → reconnect the repo to register the webhook
Production Incident SDK
Lingo Pulse includes a browser SDK to catch broken translations in production. Get your credentials from the SDK page in your dashboard.
What it detects
- Raw keys rendered to users
- Placeholder leaks like
{user_name} - Empty translations
- Fallback-locale renders (when source language shows instead of translated)
Plain HTML / JS
<script src="https://your-deploy-url/lingopulse-browser.js"></script>
<script>
const pulse = new window.LingoPulse({
repoId: 'your-repo-id',
ingestKey: 'your-ingest-key',
apiBase: 'https://your-deploy-url',
appVersion: 'web@1.0.0',
});
pulse.inspect('checkout.pay_now', {
locale: 'ja',
route: '/checkout',
translationKey: 'checkout.pay_now',
});
</script>React / Module
import { LingoPulse } from '@/lib/sdk/lingopulse';
const pulse = new LingoPulse({
repoId: 'your-repo-id',
ingestKey: 'your-ingest-key',
apiBase: 'https://your-deploy-url',
appVersion: 'web@1.0.0',
});Wrap your translator
const t = pulse.wrapTranslator(i18n.t.bind(i18n), (key) => ({
locale: i18n.language,
route: window.location.pathname,
translationKey: key,
}))Direct inspect
const label = pulse.inspect(i18n.t('checkout.pay_now'), {
locale: i18n.language,
route: window.location.pathname,
translationKey: 'checkout.pay_now',
});Raw HTTP payload
Send incidents directly over HTTP without the SDK:
POST https://your-deploy-url/api/incidents/report
{
"repoId": "your-repo-id",
"ingestKey": "your-ingest-key",
"issueType": "fallback",
"locale": "ja",
"route": "/checkout",
"translationKey": "checkout.pay_now",
"sampleText": "Pay now",
"appVersion": "web@1.0.0"
}Credentials
Get your credentials from the SDK page in your dashboard:
- repoId - identifies your project
- ingestKey - public key for incident ingestion
- apiBase - your Lingo Pulse deployment URL
Set a distinct appVersion per app or surface (e.g., web@1.4.2, marketing@2026-03-13) so incident feeds show which SDK source reported each issue.
API reference
/api/reposConnect a new repository. Body: { repoUrl, githubPat?, lingoApiKey? }
/api/reposList all repos for the authenticated user.
/api/repos/[id]Fetch full dashboard data for a repo (locales, coverage, quality, activity).
/api/analyzeManually trigger a re-analysis run. Body: { repoId }
/api/webhooks/githubGitHub webhook receiver. Validates X-Hub-Signature-256 and triggers analysis.