# How to Add and Configure Internationalization (i18n) with vue-i18n in Celeris Web

> Learn how to add and configure internationalization i18n with vue-i18n in Celeris Web. Discover seamless translation file loading localization persistence and component integration.

- Repository: [Kirk Lin/celeris-web](https://github.com/kirklin/celeris-web)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Celeris Web provides a modular i18n system built on vue-i18n that uses `LocalesEngine` and `LocalesConfiguration` to load JSON translation files from `apps/admin/locales`, persist the selected locale in local storage, and expose translation helpers via `useI18n()` in components.**

Celeris Web implements a thin, modular wrapper around **vue-i18n** that simplifies adding multilingual support to your Vue applications. The system lives in the `@celeris/locale` package under `packages/web/locale` and provides a persistent store, asynchronous message loading, and a clean API for runtime locale switching.

## Core Architecture and File Structure

The internationalization layer is organized into distinct modules that separate storage, configuration, and Vue plugin initialization:

| File | Responsibility |
|------|----------------|
| [`packages/web/locale/src/store.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/store.ts) | Persists the selected locale in **localStorage** using `LOCALES_STORE_KEY`. Exports `setLocale` and `getLocale`. |
| [`packages/web/locale/src/config.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/config.ts) | Defines **LocalesConfiguration** for mutable settings and **LocalesEngine** as the static API façade. |
| [`packages/web/locale/src/index.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/index.ts) | Creates the vue-i18n instance via `createI18n` and registers it on the Vue app through `setupI18n`. |
| [`apps/admin/src/AppConfiguration.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/AppConfiguration.ts) | Loads JSON files from `apps/admin/locales/*.json` using Vite’s `import.meta.glob` and feeds them to `LocalesEngine`. |
| [`apps/admin/src/main.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/main.ts) | Bootstraps the application and invokes `setupI18n(app)` after configuration initialization. |
| Component files (e.g., [`Menu.vue`](https://github.com/kirklin/celeris-web/blob/main/Menu.vue)) | Consume translations using `useI18n()` from vue-i18n. |

## Configuration Flow and Initialization

The system initializes in five distinct phases to ensure translations are available before components render:

1. **Locale Persistence** — [`store.ts`](https://github.com/kirklin/celeris-web/blob/main/store.ts) creates a reactive `getLocale` computed property and a `setLocale` helper that writes to `localStorage` at [`packages/web/locale/src/store.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/store.ts).

2. **Configuration State** — `LocalesConfiguration` holds the current locale, fallback language, message handler function, and additional vue-i18n options at [`packages/web/locale/src/config.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/config.ts).

3. **Static API** — `LocalesEngine` exposes static methods like `initLocales` and `setLocale` that delegate to the configuration object at [`packages/web/locale/src/config.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/config.ts).

4. **Instance Creation** — `setupI18n(app)` in [`packages/web/locale/src/index.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/index.ts) calls `createI18nOptions()`, which merges defaults (`legacy: false`, locale from configuration) with user options, creates the i18n instance, and installs it on the Vue app.

5. **Application Bootstrap** — `initializeI18n()` in [`apps/admin/src/AppConfiguration.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/AppConfiguration.ts) pre-loads all translation files, then [`main.ts`](https://github.com/kirklin/celeris-web/blob/main/main.ts) awaits this initialization before mounting the app at [`apps/admin/src/main.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/main.ts).

## Practical Implementation Examples

### Loading Translation Messages

In your application configuration file, load JSON files from the `locales` directory and initialize the engine:

```typescript
// apps/admin/src/AppConfiguration.ts
import { LocalesEngine } from "@celeris/locale";

function initializeI18n() {
  const { getLocale } = useAppSetting();

  // Load all JSON files from apps/admin/locales/*.json
  const messages = Object.fromEntries(
    Object.entries(
      import.meta.glob<{ default: any }>("./locales/*.json", { eager: true })
    ).map(([key, value]) => [key.slice(10, -5), value.default])
  );

  // Feed the data to the engine
  LocalesEngine.initLocales(() => ({
    locale: getLocale.value,
    fallbackLocale: "en",
    messagesHandler: () => messages,
    otherOptions: {
      sync: true,
      availableLocales: Object.keys(messages),
      silentTranslationWarn: true,
      missingWarn: false,
      silentFallbackWarn: true,
    },
  }));
}

```

### Initializing the i18n Instance

The `setupI18n` function creates the vue-i18n instance with merged options:

```typescript
// packages/web/locale/src/index.ts
import { createI18n } from "vue-i18n";
import { deepMerge } from "@celeris/utils";
import { LocalesConfiguration } from "./config";

export let i18n: ReturnType<typeof createI18n>;

async function createI18nOptions() {
  return deepMerge(
    {
      legacy: false,
      locale: LocalesConfiguration.locale,
      fallbackLocale: LocalesConfiguration.fallbackLocale,
      messages: await LocalesConfiguration.messagesHandler(),
    },
    LocalesConfiguration.otherOptions
  );
}

export async function setupI18n(app) {
  const options = await createI18nOptions();
  i18n = createI18n(options);
  app.use(i18n);
}

```

### Consuming Translations in Components

Any Vue component can access translations using the `useI18n` composable:

```vue
<script setup lang="ts">
import { useI18n } from "vue-i18n";

const { te, t } = useI18n();

function i18nRender(key: string) {
  // Return the translated string if it exists, otherwise the key itself
  return te(key) ? t(key) : key;
}
</script>

<template>
  <span>{{ i18nRender('menu.dashboard') }}</span>
</template>

```

### Runtime Locale Switching

Change the active language and persist the selection using the engine API:

```typescript
import { LocalesEngine } from "@celeris/locale";

// Switch to Chinese
LocalesEngine.setLocale("zh-CN");

// Updates localStorage and reconfigures the i18n instance automatically

```

## Summary

- **Persistence**: The locale store in [`store.ts`](https://github.com/kirklin/celeris-web/blob/main/store.ts) maintains the user's language preference in localStorage using `LOCALES_STORE_KEY`.
- **Configuration**: `LocalesConfiguration` and `LocalesEngine` provide a type-safe, centralized API for managing i18n settings and switching languages at runtime.
- **Loading**: Translation files are loaded via `import.meta.glob` in [`AppConfiguration.ts`](https://github.com/kirklin/celeris-web/blob/main/AppConfiguration.ts), making them available synchronously during app bootstrap.
- **Integration**: The `setupI18n` function registers the vue-i18n instance on the Vue app only after messages are fully loaded, preventing translation flashes.
- **Usage**: Components consume translations through standard `useI18n()` from vue-i18n, with helper patterns like `i18nRender` for fallback handling.

## Frequently Asked Questions

### How do I add a new language to a Celeris Web application?

Create a new JSON file in `apps/admin/locales` (e.g., [`fr.json`](https://github.com/kirklin/celeris-web/blob/main/fr.json) for French), add your translation key-value pairs, and restart the development server. The `import.meta.glob` pattern in [`AppConfiguration.ts`](https://github.com/kirklin/celeris-web/blob/main/AppConfiguration.ts) automatically picks up new files and adds them to `availableLocales`.

### Where does Celeris Web store the user's selected language?

The selected locale is persisted in the browser's **localStorage** under the key defined by `LOCALES_STORE_KEY` in [`packages/web/locale/src/store.ts`](https://github.com/kirklin/celeris-web/blob/main/packages/web/locale/src/store.ts). The `setLocale` function updates this value and the reactive `getLocale` computed property.

### How can I change the fallback locale from English to another language?

Modify the `fallbackLocale` property passed to `LocalesEngine.initLocales` in [`apps/admin/src/AppConfiguration.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/AppConfiguration.ts). Change the string `"en"` to your preferred fallback language code (e.g., `"zh-CN"`).

### What is the purpose of the `LocalesEngine` class?

`LocalesEngine` acts as a static façade that exposes high-level methods like `initLocales` and `setLocale`. It delegates to the mutable `LocalesConfiguration` object, allowing the application to configure and control the i18n system without directly manipulating vue-i18n internals.