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.

SectionDescription
commonGeneral UI: Save, Create, Delete, Search, Cancel, Loading, Error, etc.
collectionsCollection and global management labels
documentsDocument editor: info panel, timestamps, references
authSign-in page and authentication errors
workflowStatus labels: Draft, Published, Scheduled, Trashed
dashboardDashboard page: quick actions, content overview, recent activity
alertsToast notifications and validation messages
sidebarNavigation: Access Control, Users, Roles, Media Library, etc.
actionsDocument toolbar actions: Versions, Duplicate, Preview, History
activityActivity log entries: Created, Updated, Deleted
mediaMedia Library: search, sort, filters, upload
seoSEO field labels and OpenGraph metadata
webhooksWebhook configuration and event management
localeSwitcherContent 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>;
};
Previous
Custom Components
Next
Plugins