@samuelmabonga/payload-translate
v0.1.0
Published
Payload CMS plugin that auto-translates localized fields via DeepL or Google Cloud Translate, with an admin 'Translate now' button.
Maintainers
Readme
@samuelmabonga/payload-translate
Payload CMS 3 plugin that auto-translates localized fields via DeepL or Google Cloud Translation v2, and renders a "Translate now" button in the document sidebar next to Publish.
- Schema-aware walker — translates
text,textarea,richText(Lexical, including inline blocks), groups, arrays, blocks, named tabs. - Preserves URLs, slugs, ObjectIds, media references, CSS classes, enum values — no clobbering.
- Auto-translates on every publish (configurable) and lets editors trigger it manually from the admin.
- Endpoint also accepts a shared secret for CI / bulk scripts.
- Works with any Payload localization config — derives target locales from
config.localization.localesminus the source.
Install
pnpm add @samuelmabonga/payload-translate
# or
npm i @samuelmabonga/payload-translatePeer deps: payload@^3, @payloadcms/ui@^3, react@^18 || ^19.
Configure
// payload.config.ts
import { buildConfig } from 'payload'
import { translatePlugin } from '@samuelmabonga/payload-translate'
export default buildConfig({
localization: {
locales: [
{ code: 'en', label: 'English' },
{ code: 'fr', label: 'French' },
{ code: 'pt', label: 'Portuguese' },
],
defaultLocale: 'en',
fallback: true,
},
plugins: [
translatePlugin({
provider: 'deepl', // 'deepl' (default) or 'google'
deeplApiKey: process.env.DEEPL_API_KEY,
// googleApiKey: process.env.GOOGLE_TRANSLATE_API_KEY,
sourceLocale: 'en', // optional — default 'en'
// targetLocales: ['fr', 'pt'], // optional — derives from config
collections: ['pages', 'posts', 'team'],
globals: ['header', 'footer'],
autoTranslateOnPublish: true, // hook on every publish
showButton: true, // sidebar Translate-now button
secret: process.env.REVALIDATION_KEY, // optional shared secret
endpointPath: '/translate-doc', // default
}),
],
})Then regenerate the admin importMap so it picks up the button component:
pnpm payload generate:importmapHow it works
| Concern | Where |
|---|---|
| Auto-translate on publish | afterChange hook added to every configured collection / global. Only fires when req.locale === sourceLocale so editing a translated doc never overwrites manual fixes. |
| Manual "Translate now" button | Sidebar UI field appended to every configured collection / global. POSTs to /api/translate-doc (path configurable), auths via Payload session cookie. |
| External / CI calls | Same endpoint, { secret } in the body. Secret comes from pluginOptions.secret. |
| Provider | setTranslatorConfig() is called at plugin init with the provider + keys. Fallback to TRANSLATION_PROVIDER / DEEPL_API_KEY / GOOGLE_TRANSLATE_API_KEY env. |
| Locale code mapping | Bundled defaults for ~20 common languages. Extend with deeplTargets / googleTargets. |
Manual API
If you want to drive translation programmatically (e.g. a CLI):
import { translatePageDoc } from '@samuelmabonga/payload-translate'
await translatePageDoc(srcDoc, docId, collectionConfig.fields, payload, 'pages', {
sourceLocale: 'en',
targetLocales: ['fr', 'pt'],
})License
MIT
