I18n

Adding internationalization after a product has already grown is often unnecessarily difficult.

User-facing text becomes scattered throughout the application, translation keys are introduced inconsistently, and existing interfaces must be reworked before another language can be supported.

Vuesion includes internationalization from the beginning.

All user-facing messages use translation keys, supported locales are configured centrally, and a custom extraction tool keeps translation files synchronized with the application.

Supported locales

Vuesion includes two locales by default:

  • English (en-US)
  • German (de-DE)

The supported locales and default locale are configured through the Nuxt i18n configuration.

Additional locales can be added as the product grows.

Each locale has its own translation file:

Text
i18n/locales
├── en-US.json
└── de-DE.json

English is used as the default source language throughout the project.

Translation messages

User-facing text is referenced through translation keys:

Vue
<vue-text>
  {{ $t('common.Save' /* Save */) }}
</vue-text>

The first argument is the stable translation key:

Text
common.Save

The comment contains the default English message:

Text
Save

Keeping the default message next to its usage makes the template understandable without requiring developers to switch constantly between the component and a translation file.

It also gives the extraction tool the information required to maintain the locale files automatically.

Extracting translations

Vuesion includes a custom extraction tool that scans the codebase for translation keys and their default messages.

Run it after adding or changing user-facing text:

Shell
npm run extract-i18n-messages

For the following usage:

Vue
{{ $t('workspace.create.title' /* Create workspace */) }}

the extractor adds the message to the default locale:

i18n/en-US.json
{
  "workspace.create.title": "Create workspace"
}

The same key is added to every other configured locale so that missing translations remain visible:

i18n/de-DE.json
{
  "workspace.create.title": "Create workspace"
}

The translated value can then be changed manually:

i18n/de-DE.json
{
  "workspace.create.title": "Workspace erstellen"
}

Running the extractor again updates the default English message when its source comment changes, while preserving translations in all other locale files.

Naming translation keys

Translation keys should describe where a message belongs and what it represents.

Examples include:

Text
common.Save
common.Cancel
LoginForm.cta
WorkspaceForm.title
pages.settings.title
input.email.label

Shared messages belong under common.

Messages that are specific to a page, component, or domain should use a corresponding namespace.

Prefer stable semantic keys over keys derived directly from the complete English sentence.

Good:

Text
PasswordResetForm.cta

Avoid:

Text
ResetYourPasswordNow

A stable key can remain unchanged when the wording of the message evolves.

Locale preferences

A user's preferred locale is stored in their user settings.

After signing in, Vuesion applies that preference to the application. Changing the language updates the interface and persists the selection for future sessions.

Text
User settings
      │
      ▼
Preferred locale
      │
      ▼
Application language

Keeping the locale as part of the user settings makes language preference independent from a particular browser or device.

Unauthenticated users use the configured default locale until they select or persist another supported language.

Formatting dynamic values

Translations should contain the complete sentence rather than assembling it from several translated fragments.

Use interpolation for dynamic values:

Vue
{{ $t('workspace.memberCount' /* {count} member|members */, { count: members.length }) }}

This allows each language to control its own sentence structure.

The same principle applies to:

  • Names
  • Dates
  • Numbers
  • Currency values
  • Counts
  • Product-specific values

Avoid concatenating translated strings in templates, because word order and grammar differ between languages.

Pluralization

Messages whose wording depends on a quantity should use the pluralization features provided by the i18n layer.

The translation should define the complete singular and plural forms rather than relying on conditions inside the component.

This keeps language rules inside the locale files and presentation logic inside the component.

Messages in TypeScript

Translations are not limited to Vue templates.

Use the i18n composable when translated messages are required in setup code or action composables:

TypeScript
const { t } = useI18n();

eventBus.emit('toast', {
  title: t('workspace.create.success' /* Workspace created */),
});

The same extraction convention applies: provide the key followed by the default English message in a comment.

Keep business data language-independent

Translation belongs at the presentation boundary.

Persist stable values such as enums, identifiers, and status codes rather than translated labels.

For example, store:

Text
ACTIVE

and translate it when displayed:

Vue
{{ $t('UserStatus.ACTIVE' /* Active */) }}

Do not store the translated value:

Text
Active

This keeps database records independent from the current locale and allows the same value to be presented in every supported language.

Adding another locale

To add another language:

  • Add the locale to the Nuxt i18n configuration and to tools/extract-i18n-messages.ts.
  • Create the corresponding locale file.
  • Run the extraction tool.
  • Translate the generated default messages.
  • Add the locale to the supported user-setting options.

For example, adding French may introduce:

Text
i18n/fr-FR.json

The extraction tool then ensures that the new file receives the same set of keys as the existing locales.

Why this approach?

Translation files often become difficult to maintain when keys and source messages are managed entirely by hand.

Vuesion keeps the translation key and its default message close to the code that uses them, while generated locale files provide a central place for translation.

This approach provides:

  • Internationalization from the beginning
  • Readable templates
  • Automatically synchronized locale files
  • Preserved translations
  • Persistent user preferences
  • Stable, language-independent business data
  • A straightforward path for adding new locales

The result is an internationalization workflow that remains practical as both the product and the number of supported languages grow.

Next steps

Continue with Quality to learn how Vuesion uses testing, static analysis, and automation to maintain a reliable foundation.