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.
Vuesion includes two locales by default:
en-US)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:
i18n/locales
├── en-US.json
└── de-DE.json
English is used as the default source language throughout the project.
User-facing text is referenced through translation keys:
<vue-text>
{{ $t('common.Save' /* Save */) }}
</vue-text>
The first argument is the stable translation key:
common.Save
The comment contains the default English message:
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.
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:
npm run extract-i18n-messages
For the following usage:
{{ $t('workspace.create.title' /* Create workspace */) }}
the extractor adds the message to the default locale:
{
"workspace.create.title": "Create workspace"
}
The same key is added to every other configured locale so that missing translations remain visible:
{
"workspace.create.title": "Create workspace"
}
The translated value can then be changed manually:
{
"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.
Translation keys should describe where a message belongs and what it represents.
Examples include:
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:
PasswordResetForm.cta
Avoid:
ResetYourPasswordNow
A stable key can remain unchanged when the wording of the message evolves.
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.
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.
Translations should contain the complete sentence rather than assembling it from several translated fragments.
Use interpolation for dynamic values:
{{ $t('workspace.memberCount' /* {count} member|members */, { count: members.length }) }}
This allows each language to control its own sentence structure.
The same principle applies to:
Avoid concatenating translated strings in templates, because word order and grammar differ between languages.
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.
Translations are not limited to Vue templates.
Use the i18n composable when translated messages are required in setup code or action composables:
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.
Translation belongs at the presentation boundary.
Persist stable values such as enums, identifiers, and status codes rather than translated labels.
For example, store:
ACTIVE
and translate it when displayed:
{{ $t('UserStatus.ACTIVE' /* Active */) }}
Do not store the translated value:
Active
This keeps database records independent from the current locale and allows the same value to be presented in every supported language.
To add another language:
tools/extract-i18n-messages.ts.For example, adding French may introduce:
i18n/fr-FR.json
The extraction tool then ensures that the new file receives the same set of keys as the existing locales.
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:
The result is an internationalization workflow that remains practical as both the product and the number of supported languages grow.
Continue with Quality to learn how Vuesion uses testing, static analysis, and automation to maintain a reliable foundation.