Why We Built a Draft-to-Publish Translation Workflow
Imagine editing a translation and it goes live immediately.
Now imagine you have 50 translations to update. Each one goes live the moment you save. Your users see half-finished work. Your CDN regenerates 50 times.
That's why we built the Draft → Publish workflow.
The Problem in a Real Laravel App
Say you're reworking the checkout copy before a big campaign: 40 keys across 6 languages. Without a staging step, a live-editing tool creates two ugly outcomes.
First, the mixed state. You've updated checkout.cart.empty in English and Spanish, but French is still the old copy and German is half-typed. A user in Berlin refreshes mid-edit and sees the seams. Multiply by 40 keys and your app is in a visibly inconsistent state for hours.
Second, the infrastructure churn. Every save that goes live means regenerating translation files and pushing them to the CDN. Fifty edits, fifty regenerations, for changes that logically belong to one release.
The old-school Laravel answer was lang files plus deploys — consistent, but every typo fix costs a full deployment. The draft workflow keeps the consistency and drops the deploy.
The Lifecycle
Every translation in LangSyncer moves through a versioned lifecycle:
Edit -> Save (Draft) -> Edit Again -> Save (Draft) -> Review -> Publish (Active)
|
previous version -> Archived
The rules are strict and simple:
- Save always creates or updates a draft version. New translations start as drafts too.
- Drafts are never visible to your applications. Not via CDN, not via sync. There's no flag to accidentally flip.
- Publish promotes all drafts to active in one operation, and the previously active versions are archived, not deleted.
In the UI you see this as two badges: Active (green, published and live) and Pending (yellow, new or edited but not yet published). The status filter lets you list only pending translations, which is exactly the "what's about to ship?" review view.
What Publish Actually Changes in Production
"Publish" isn't just a status flip. When you click it, five things happen in order:
- All draft versions become active.
- The previous active versions are moved to archived (this is what makes rollback possible).
- CDN files are regenerated, with roughly a 30-second delay.
- Webhooks are dispatched, delayed 2-3 minutes — including the "Translations Published" event your Laravel app listens for in Live mode.
- A publication record is created, with your release notes attached.
On the application side, that means your users see the new copy without a deploy: the client package picks up the published files from the CDN, or you trigger php artisan translator:sync yourself. If a change isn't showing up, the checklist is almost always: did you publish, has the app synced, is a stale cache in the way (php artisan cache:clear)?
The Workflow in Practice
Here's the full loop for that 40-key checkout rework:
- Edit freely. Monday: a developer adds 20 new keys, a translator updates 15 existing ones, the content team fixes 5 typos. Every save is a draft. Production doesn't move.
- Review what's pending. Tuesday morning, filter by Pending status and read through the 40 changes. This is the moment you catch the machine-translated legal disclaimer before a customer does.
- Publish once. Click Publish at the top of the translations page, check the pending changes count, and add a release note: "Checkout copy rework for spring campaign". One publish, one CDN regeneration, one webhook.
- Multi-project publishing, if you manage several apps: click Publish without selecting a project and you'll see every project with pending changes. Select the ones to release, and each is published sequentially with progress indicators.
Note the permission line: publishing requires the Project Admin role. Collaborators can edit and save drafts all day, but the publish button stays disabled for them. That's a feature, not a bug — see how team roles fit around this workflow.
Rollback: The Other Half of the Safety Net
Because publishing archives instead of deleting, every publish is reversible:
- Open the translation's History — you'll see each version with its number, status (Draft/Active/Archived), when it was published, and who published it.
- Find the good version and click Rollback.
- LangSyncer creates a new draft with the old values. The current active version is untouched.
- Review the draft, then publish.
Rollback going through the same draft → publish gate is deliberate. There's no panic button that instantly mutates production; even the undo path gets reviewed. Full details in the translations documentation.
When Draft/Publish Gets in the Way
Honest limits:
- It's not instant. CDN regeneration takes ~30 seconds and webhooks fire after 2-3 minutes. For a genuinely urgent fix that's still dramatically faster than a deploy, but it's not a live-typing experience.
- You can't preview drafts inside your app. Drafts are invisible to applications by design, so there's no "staging flag" to render pending copy in your real UI. The documented pattern is a separate staging project with its own API key: test the changes there, then apply and publish in production.
- Publishing is a role bottleneck by design. Only Project Admins can publish. If your one admin is on vacation, drafts pile up. Make sure at least two people hold the role.
If you're comparing this against tools where edits flow straight to exports, the workflow difference is worth weighing explicitly — see LangSyncer vs POEditor.
Gotchas
- Publish button disabled? Either there are no pending changes, or you don't have the Project Admin role.
- Give the pipeline its 30 seconds (CDN) and 2-3 minutes (webhooks) before re-publishing "because nothing happened".
- Rollback creates a draft — nothing changes in production until you publish it.