Skip to content

add-locale and remove-locale ​

Adding a language touches five files and a manifest. These two commands do all of it, and the manifest is what makes the removal exact.

add-locale ​

Usage: smart-admin add-locale <code> [--from <src>] [--label "<native name>"] [--dry-run]
  e.g. smart-admin add-locale fr --label "Français"
       smart-admin add-locale ru --from ar   # copy from Arabic, not the base
OptionWhat it does
--labelThe native name the switcher shows.
--fromCopy the starting values from this locale instead of the base. Use it when the new language is closer to another one than to English.
--dry-runPrint the plan; write nothing.

What it does ​

bash
npm run smart-admin -- add-locale nl --label "Nederlands"
Adding locale "nl"
  copying from en (declared base)
  created   src/locales/nl.json
  edited    src/core/i18n/index.ts (AvailableLocale + lazyLocales)
  edited    src/core/i18n/localeMeta.ts (Intl tag + label + date-fns importer)
             switcher label: Nederlands
  edited    src/core/config/defaultConfig.ts (config.locale union)
  created   .smart-admin/locales/nl.json

✔ Locale "nl" wired.
Next steps:
  1. Translate src/locales/nl.json — one file, every namespace, "en" values as placeholders.
  2. Set the region in src/core/i18n/localeMeta.ts:
     intlTag 'nl' → e.g. 'nl-XX'   ·   label is already 'Nederlands'
     No flag is set, by design — a flag is a country and a locale is a
     language. The switcher shows the code; add /flags/xx.svg by hand
     on that row if you use flags at all.
     The date-fns importer points at date-fns/locale/nl — if that
     subpath does not exist the build fails loudly, by design.
  3. smart-admin analyze --locales   # shows what is still untranslated
  4. Restart dev if it is running.

One new file, three edits, one manifest. The type union in three separate places is updated together, which is the part that is easy to get wrong by hand.

Two deliberate choices ​

No flag is set. A flag is a country and a locale is a language, and the two do not map. The switcher falls back to the code. Add /flags/xx.svg to that row yourself if you use flags at all — the nine shipped locales do.

The date-fns importer is not checked. It points at date-fns/locale/{code}, and if that subpath does not exist the build fails loudly rather than the calendar quietly falling back to English.

The manifest ​

Every added locale writes .smart-admin/locales/{code}.json:

json
{
  "code": "nl",
  "addedAt": "2026-09-24T16:32:21.042Z",
  "source": "en",
  "createdFiles": [
    "src/locales/nl.json"
  ],
  "editedFiles": [
    "src/core/i18n/index.ts",
    "src/core/i18n/localeMeta.ts",
    "src/core/config/defaultConfig.ts"
  ]
}

That is what remove-locale reads, which is why removal is exact rather than a search-and-hope. The directory also holds manifests for the shipped locales that were added this way.

remove-locale ​

Usage: smart-admin remove-locale <code> [--force] [--backup|--no-backup]
  e.g. smart-admin remove-locale ru

  --backup / --no-backup answer the backup prompt up front, so a
  script or CI run never blocks waiting for a terminal.

  To keep backups out of non-development runs entirely, create
  .smart-admin/config.json with:
    { "backups": { "devOnly": true } }
  Backups are then skipped whenever NODE_ENV=production or CI is set.
  An explicit --backup still wins over it.

It refuses while anything still names the code ​

"nl" is still referenced in 1 file(s):
  src/modules/forms/pages/selects.vue

  Remove those references first, or re-run with --force.

That is the check worth having: a locale removed from the registry but still named in a page leaves a control pointing at a language that does not exist.

--force warns rather than staying quiet ​

 ⚠  FORCED PAST STRAY REFERENCES
  1 file(s) still name "nl" and were NOT edited:
    src/modules/forms/pages/selects.vue
  Each one now points at a locale that no longer exists.
  Fix them by hand, then re-run `npm run build` before you trust this tree.

  backed up to .smart-admin/backups/1790267589377/
  .gitignore already covers .smart-admin/backups/

Removing locale "nl"
  deleted   src/locales/nl.json
  edited    src/core/i18n/index.ts (AvailableLocale + lazyLocales)
  edited    src/core/i18n/localeMeta.ts (registry row)
  edited    src/core/config/defaultConfig.ts (config.locale union)
  deleted   .smart-admin/locales/nl.json

✔ Locale "nl" removed.
  Backup: .smart-admin/backups/1790267589377/  ·  restore with `smart-admin reset`
  Delete it later with `smart-admin remove-backups`.

It names the files it did not touch and tells you to build before trusting the tree. The manifest goes with the locale, so the record and the code stay in step.

The base locale ​

en is the base. Every other file is measured against it, and add-locale copies from it unless --from says otherwise. A locale file always holds every key — untranslated values are the base's own words, not blanks, so a gap reads as English rather than a raw nav.foo.

Right-to-left locales ​

Direction is not a flag on the locale. ar, he, fa and ur are right-to-left, and useDirection derives it from the code. Add one of those and the layout mirrors with no extra setting up.

Checking the result ​

bash
npm run smart-admin -- analyze --locales

analyze reports key coverage per locale and how many values are still identical to the base.