How-to: translate the admin interface
Goal: run the admin in Italian, let users switch language from the topbar, and override the odd Mapo string that doesn't fit your domain.
Interface vs content
This guide is about the UI language. Translating your records (a model with per-language fields) is a different feature — see Translated fields.
1. Pick the default language
Mapo ships English and Italian and starts in English. To start in Italian:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@nuxt/ui", "mapomodule"],
mapo: {
i18n: {
defaultLocale: "it",
},
},
});Browser detection is on by default, so a visitor with an Italian browser lands on Italian and the choice sticks in a cookie. Turn it off with detectBrowserLanguage: false if you always want defaultLocale to win.
2. Add the language switcher
<!-- app/app.vue -->
<template>
<UApp>
<NuxtLayout>
<template #topbar:right>
<MapoLangSwitcher />
<MapoThemeToggle />
</template>
<NuxtPage />
</NuxtLayout>
</UApp>
</template>It lists the configured locales with their flag, switches on selection and persists the choice. Pass :flags="false" for names only.
3. Translate your own strings
Use useI18n() — it's auto-imported. Keep your keys in your own namespace and Mapo's under mapo.*:
<script setup lang="ts">
const { t } = useI18n();
</script>
<template>
<h1>{{ t("dashboard.title") }}</h1>
<p>{{ t("dashboard.welcome", { name: user.first_name }) }}</p>
</template>Your messages go in i18n/locales/<code>.json, declared through locales:
mapo: {
i18n: {
defaultLocale: 'it',
locales: [
{ code: 'en', language: 'en-US', name: 'English', file: 'en.json' },
{ code: 'it', language: 'it-IT', name: 'Italiano', file: 'it.json' },
],
},
}// i18n/locales/it.json
{
"dashboard": {
"title": "Pannello di controllo",
"welcome": "Bentornato, {name}!"
}
}4. Override a Mapo string
Same files: redefine the key under mapo. The merge is deep, so you only write what you're changing — everything else keeps the built-in text:
// i18n/locales/it.json
{
"dashboard": { "title": "Pannello di controllo" },
"mapo": {
"save": "Registra",
"listTable": {
"noItems": "Nessun risultato per questa ricerca"
}
}
}To find the key you want, look at en.json: keys are grouped by component (mapo.listTable.*, mapo.mediaUploader.*, mapo.menuTreeview.*), with the generic labels at the top level.
5. Add a language Mapo doesn't ship
Declare it with a file of your own. Mapo's own strings fall back to defaultLocale, so nothing breaks while you translate:
mapo: {
i18n: {
defaultLocale: 'en',
locales: [
{ code: 'en', language: 'en-US', name: 'English' },
{ code: 'it', language: 'it-IT', name: 'Italiano' },
{ code: 'fr', language: 'fr-FR', name: 'Français', file: 'fr.json' },
],
},
}To translate the interface as well, copy the mapo block from en.json into fr.json and translate the values.
Translating outside a component
useI18n() needs a component instance, so it throws in a Pinia store or a plain helper. Use useMapoT() there — it is auto-imported like the Mapo stores:
export const useOrdersStore = defineStore("orders", () => {
async function cancel(order: Order) {
const t = useMapoT();
const ok = await useConfirmStore().ask({
title: t("orders.cancelTitle"),
message: t("orders.cancelQuestion", { id: order.id }),
confirmText: t("mapo.confirm"),
});
// …
}
return { cancel };
});Gotchas
A literal @ breaks the catalog. vue-i18n reads @:key as a link to another message, so an email address or an npm scope in a value fails compilation with Invalid linked format (error code: 10) — and takes the whole file down with it, not just that key. Escape it:
{ "support": "Write to {'@'}example.com" }Plurals take the count twice. Define the forms with |, then pass the count as the third argument:
{ "nItems": "{n} item | {n} items" }t("mapo.repeater.nItems", { n: count }, count);Already using @nuxtjs/i18n? Declare it in modules[] as usual: Mapo detects it, contributes only its catalogs and leaves your config alone. @nuxt/ui still has to come before mapomodule.
See also
- @mapomodule/i18n reference — every option, the switcher API
- Translated fields — per-language content