Customization
Internationalization
Overview
All text displayed in the Vextro admin UI -- button labels, field placeholders, toast messages, navigation items -- comes from a locale object. The i18n system lets you override any subset of those strings without replacing the entire locale.
English is the built-in default. Partial overrides are merged with the English base at startup, so you only need to provide the strings that differ from the defaults.
Quick Start
Pass locale overrides to your VextroConfig:
// convex/vextro.config.ts
import { defineVextroConfig, defineLocale } from 'vextro';
export default defineVextroConfig({
brandName: 'Acme CMS',
locale: defineLocale({
common: {
save: 'Publish',
saveChanges: 'Publish Changes',
},
workflow: {
draft: 'Unpublished',
published: 'Live',
},
}),
}); API
All i18n utilities are exported from the main vextro package.
defineLocale
Type-safe helper for defining locale overrides with full IDE autocomplete. Returns the input unchanged -- its only purpose is key validation.
import { defineLocale } from 'vextro';
const overrides = defineLocale({
common: {
save: 'Publish',
},
}); createI18n
Creates a complete VextroLocale by merging a base locale with partial overrides. Defaults to English when no base is specified.
import { createI18n, en } from 'vextro';
const locale = createI18n({
locale: en, // optional base locale (defaults to English)
overrides: {
common: { save: 'Publish' },
},
}); t
String interpolation utility. Replaces {key} placeholders with values from a params object.
import { t } from 'vextro';
t('Used in {count} documents', { count: 5 });
// → "Used in 5 documents" getLocale
Resolves the active locale from a VextroConfig. If config.locale is set, merges it with the English default. Otherwise returns English as-is.
import { getLocale } from 'vextro';
const locale = getLocale(vextroConfig); en
The complete English locale object. Import it when you want to build a fully custom locale from scratch.
import { en } from 'vextro'; Locale Sections
The locale is organized into 14 sections. All sections are required for a complete locale; partial overrides via defineLocale only require the keys you want to change.
| Section | Description |
|---|---|
common | General UI: Save, Create, Delete, Search, Cancel, Loading, Error, etc. |
collections | Collection and global management labels |
documents | Document editor: info panel, timestamps, references |
auth | Sign-in page and authentication errors |
workflow | Status labels: Draft, Published, Scheduled, Trashed |
dashboard | Dashboard page: quick actions, content overview, recent activity |
alerts | Toast notifications and validation messages |
sidebar | Navigation: Access Control, Users, Roles, Media Library, etc. |
actions | Document toolbar actions: Versions, Duplicate, Preview, History |
activity | Activity log entries: Created, Updated, Deleted |
media | Media Library: search, sort, filters, upload |
seo | SEO field labels and OpenGraph metadata |
webhooks | Webhook configuration and event management |
localeSwitcher | Content localization UI: default locale, copy-from |
Content Localization vs. UI Localization
The i18n system described here controls admin UI strings -- button labels, headings, messages. It is separate from per-document content localization (storing multiple translated versions of a document's fields). Content localization is handled by the localeSwitcher UI that appears when a collection has locale-aware fields configured.
Full Locale Override Example
The following shows overrides for the sections most commonly customized:
import { defineVextroConfig, defineLocale } from 'vextro';
export default defineVextroConfig({
brandName: 'Acme',
locale: defineLocale({
// Rename save/create actions
common: {
save: 'Publish',
saveChanges: 'Publish Changes',
create: 'New Entry',
},
// Rename workflow statuses
workflow: {
draft: 'Unpublished',
published: 'Live',
trashed: 'Archived',
},
// Customize alert messages
alerts: {
changesSaved: 'Published successfully',
unableToSave: 'Could not publish -- please try again',
},
// Rename collections section
collections: {
noCollections: 'No content types defined yet',
},
}),
}); Types
// Partial overrides -- only supply what you want to change
type VextroLocaleOverrides = {
[K in keyof VextroLocale]?: Partial<VextroLocale[K]>
};
// Complete locale -- all keys required
type VextroLocale = {
common: Record<string, string>;
collections: Record<string, string>;
documents: Record<string, string>;
auth: Record<string, string>;
workflow: Record<string, string>;
dashboard: Record<string, string>;
alerts: Record<string, string>;
sidebar: Record<string, string>;
actions: Record<string, string>;
activity: Record<string, string>;
media: Record<string, string>;
seo: Record<string, string>;
webhooks: Record<string, string>;
localeSwitcher: Record<string, string>;
};