Internationalization
The plugin's admin UI ships in 12 languages, merges into Payload's i18n, and lets you override any string or add your own language.
Every user-facing string the plugin renders in the Payload admin — collection and field labels, field descriptions, select options, the calendar / dashboard / availability components, the booking-time picker, and the booking validation errors — goes through Payload's i18n layer. The plugin ships translations for 12 languages out of the box and lets you override any string or add your own language.
i18n vs. field localization. This page is about the admin UI language (the labels and messages an editor sees). It is unrelated to localizing the content stored in fields (e.g. a service name in English and French), which is Payload's field localization and is controlled separately. The plugin's collection fields support field localization when Payload localization is enabled.
Bundled languages
| Code | Language | Ships in Payload core? |
|---|---|---|
en | English (source) | ✅ |
fr | French | ✅ |
de | German | ✅ |
es | Spanish | ✅ |
ru | Russian | ✅ |
pl | Polish | ✅ |
tr | Turkish | ✅ |
ar | Arabic (RTL) | ✅ |
zh | Chinese (Simplified) | ✅ |
id | Indonesian | ✅ |
fa | Persian / Farsi (RTL) | ✅ |
hi | Hindi | ❌ — see Hindi |
The plugin merges these into config.i18n.translations under the reservation namespace. For the 11 languages that Payload core also ships, the admin language switcher lists them automatically — no extra setup. The active admin language drives which translation is shown; RTL languages (ar, fa) get right-to-left layout from the admin itself.
How merging works
At init, the plugin merges its translations into your config, and your translations take precedence:
// inside the plugin (simplified)
config.i18n = {
...config.i18n,
translations: deepMergeSimple(pluginTranslations, yourTranslations),
}So you never lose your own keys, and you can override any plugin string.
Overriding a string
Override any plugin key by supplying the same reservation:* key for a locale in your Payload config. Your value wins:
import { buildConfig } from 'payload'
import { payloadReserve } from 'payload-reserve'
export default buildConfig({
i18n: {
translations: {
en: {
reservation: {
// Re-label the "Lanes" calendar view as "Timeline" in English
calendarLanes: 'Timeline',
// Re-word a validation error
errorConflict: 'That slot is already taken — pick another time.',
},
},
fr: {
reservation: {
calendarLanes: 'Chronologie',
},
},
},
},
plugins: [payloadReserve()],
})Keys you don't override fall back to the plugin's bundled translation, and any key missing for the active language falls back to English.
Adding a language the plugin doesn't ship
Provide a full reservation block for the locale yourself. Use the bundled en.json as the canonical key list, and keep every {{placeholder}} token (e.g. {{count}}, {{from}}, {{allowed}}) intact:
import it from './my-translations/reservation.it.json'
payloadReserve()
// and in buildConfig:
i18n: {
// make Italian selectable in the admin (Payload core ships `it`)
supportedLanguages: { en, it },
translations: {
it: { reservation: it },
},
}Hindi (hi)
Hindi is not bundled in @payloadcms/translations, so Payload's admin language switcher does not list it by default — even though this plugin ships a complete hi translation. To make Hindi selectable you must register it as a custom language in your Payload config; once it is supported, the plugin's hi strings appear automatically:
import { buildConfig } from 'payload'
import { en } from '@payloadcms/translations/languages/en'
import { payloadReserve } from 'payload-reserve'
// Provide (or import) a Hindi translation object for Payload's own core keys.
const hi = {
// ...Payload core translations for Hindi (general:*, fields:*, etc.)
}
export default buildConfig({
i18n: {
fallbackLanguage: 'en',
supportedLanguages: { en, hi },
},
plugins: [payloadReserve()],
})The plugin's hi reservation strings merge in regardless; this step only makes the language available for editors to select.
Translation key reference
All keys live in src/translations/en.json (the source of truth). Every bundled locale has full key parity with it. Keys are grouped by area:
collection*/field*— collection and field labelsstatus*/scheduleType*/duration*/capacity*/guestBooking*— select-option labelsday*/dayShort*— weekday names (long and abbreviated)error*— validation / business-rule messages (several use{{…}}interpolation)calendar*/availability*/dashboard*/pending*/tooltip*/pick*/slot*/lane*/filter*— admin component strings
When adding a new translatable string to the plugin, add the key to en.json first, then to each locale file, keeping placeholders identical.