# Quick start

Render a translated string and switch the locale in React, Next.js, Vue, Nuxt, Svelte, Solid or plain JavaScript, in five minutes and with no Comvi account.

Canonical: https://comvi.io/docs/i18n/quick-start

Install one package, render a translated heading and switch its locale. Pick the framework in
the header; the tabs follow it.

## Install
**React**

`Terminal`

```bash
npm install @comvi/react
```

Or open the [React example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/react) to see the switcher, plurals and lazy catalogs running.

**Next.js**

`Terminal`

```bash
npm install @comvi/next
```

Or open the [Next.js example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/next) to see server rendering, the locale in the URL and plurals running.

**Vue**

`Terminal`

```bash
npm install @comvi/vue
```

Or open the [Vue example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/vue) to see the switcher, plurals and lazy catalogs running.

**Nuxt**

`Terminal`

```bash
npm install @comvi/nuxt
```

Or open the [Nuxt example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/nuxt) to see server rendering, the locale in the URL and plurals running.

**Svelte**

`Terminal`

```bash
npm install @comvi/svelte
```

Or open the [Svelte example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/svelte) to see the switcher, plurals and lazy catalogs running.

**Solid**

`Terminal`

```bash
npm install @comvi/solid
```

Or open the [Solid example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/solid) to see the switcher, plurals and lazy catalogs running.

**Vanilla**

`Terminal`

```bash
npm install @comvi/core
```

Or open the [Vanilla example in StackBlitz](https://stackblitz.com/github/comvi-io/comvi-js/tree/main/examples/vanilla) to see the switcher, plurals and lazy catalogs running.

The binding depends on `@comvi/core`, so one install is the whole of it. `pnpm`, `yarn` and
`bun` take the same package names.

## Create the i18n instance
**React**

`src/main.tsx`

```tsx
import { createRoot } from 'react-dom/client';
import { createI18n, I18nProvider } from '@comvi/react';
import App from './App';

const i18n = createI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages: {
      en: { greeting: 'Hello, {name}!' },
      uk: { greeting: 'Привіт, {name}!' },
    },
});

createRoot(document.getElementById('root')!).render(

    </I18nProvider>,
);
```

**Next.js**

`next.config.ts`

```ts
import { withComvi } from '@comvi/next/plugin';

export default withComvi({ config: './src/i18n/config.ts' })({});
```

`src/i18n/routing.ts`

```ts
import { defineRouting } from '@comvi/next/routing';

export const routing = defineRouting({ locales: ['en', 'uk'], defaultLocale: 'en' });
```

`src/i18n/config.ts`

```ts
import { createNextI18n } from '@comvi/next';
import { routing } from './routing';

export const { i18n } = createNextI18n({
    ...routing,
    messages: { en: { greeting: 'Hello, {name}!' }, uk: { greeting: 'Привіт, {name}!' } },
});

export { routing };
```

`src/app/[locale]/layout.tsx`

```tsx
import { notFound } from 'next/navigation';
import { hasLocale } from '@comvi/next/routing';
import { ComviProvider } from '@comvi/next/server';
import { routing } from '@/i18n/routing';

type Props = { children: React.ReactNode; params: Promise<{ locale: string }> };

export default async function LocaleLayout({ children, params }: Props) {
    const { locale } = await params;
    if (!hasLocale(routing.locales, locale)) notFound();

    return (
      <html lang={locale}>
        <body>
          <ComviProvider locale={locale}>{children}</ComviProvider>
        </body>
      </html>
    );
}
```

`src/middleware.ts`

```ts
import { createMiddleware } from '@comvi/next/middleware';
import { routing } from './i18n/routing';

export default createMiddleware(routing);

export const config = {
    matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)'],
};
```

If your app still has a root `src/app/layout.tsx` and `src/app/page.tsx`, delete them: the `[locale]` layout is the root layout. The middleware sends `/` to the right locale and redirects when the URL and the locale disagree.

**Vue**

`src/main.ts`

```ts
import { createApp } from 'vue';
import { createI18n } from '@comvi/vue';
import App from './App.vue';

const i18n = createI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages: {
      en: { greeting: 'Hello, {name}!' },
      uk: { greeting: 'Привіт, {name}!' },
    },
});

createApp(App).use(i18n).mount('#app');
```

**Nuxt**

`nuxt.config.ts`

```ts
import { defineNuxtConfig } from 'nuxt/config';

export default defineNuxtConfig({
    modules: ['@comvi/nuxt'],
    comvi: {
      defaultLocale: 'en',
      locales: ['en', 'uk'],
      messages: {
        en: { greeting: 'Hello, {name}!' },
        uk: { greeting: 'Привіт, {name}!' },
      },
    },
});
```

**Svelte**

`src/routes/+layout.svelte`

```svelte
<script lang="ts">
    import { createI18n, setI18nContext } from '@comvi/svelte';
    import type { Snippet } from 'svelte';

    const { children }: { children: Snippet } = $props();

    const i18n = createI18n({
      locale: 'en',
      fallbackLocale: 'en',
      messages: {
        en: { greeting: 'Hello, {name}!' },
        uk: { greeting: 'Привіт, {name}!' },
      },
    });

    setI18nContext(i18n);
</script>

{@render children()}
```

**Solid**

`src/index.tsx`

```tsx
import { render } from 'solid-js/web';
import { createI18n, I18nProvider } from '@comvi/solid';
import App from './App';

const i18n = createI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages: {
      en: { greeting: 'Hello, {name}!' },
      uk: { greeting: 'Привіт, {name}!' },
    },
});

render(
    () => (

    ),
    document.getElementById('root')!,
);
```

**Vanilla**

`src/i18n.ts`

```ts
import { createI18n } from '@comvi/core';

export const i18n = createI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages: {
      en: { greeting: 'Hello, {name}!' },
      uk: { greeting: 'Привіт, {name}!' },
    },
});

await i18n.init();

i18n.t('greeting', { name: 'Alice' }); // "Hello, Alice!"
```

`locale` is the language that renders now, `fallbackLocale` is where `t()` looks for a key the
current locale lacks, and `messages` is the catalog, one object per locale.

## Translate and switch the locale
**React**

`src/App.tsx`

```tsx
import { useI18n } from '@comvi/react';

export default function App() {
    const { t, locale, setLocale } = useI18n();

    return (
      <>
        <h1>{t('greeting', { name: 'Alice' })}</h1>
        <select value={locale} onChange={(e) => setLocale(e.target.value)}>
          <option value="en">English</option>
          <option value="uk">Українська</option>
        </select>
      </>
    );
}
```

**Next.js**

`src/app/[locale]/page.tsx`

```tsx
import Link from 'next/link';
import { getI18n } from '@comvi/next/server';

export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
    const { locale } = await params;
    const { t } = await getI18n({ locale });

    return (
      <div>
        <h1>{t('greeting', { name: 'Alice' })}</h1>
        <Link href={locale === 'en' ? '/uk' : '/en'}>Switch</Link>
      </div>
    );
}
```

**Vue**

`src/App.vue`

```vue
<script setup lang="ts">
import { useI18n } from '@comvi/vue';

const { t, locale, setLocale } = useI18n();
</script>

<template>
    <h1>{{ t('greeting', { name: 'Alice' }) }} ({{ locale }})</h1>
    <button @click="setLocale('uk')">Українська</button>
    <button @click="locale = 'en'">English</button>
</template>
```

**Nuxt**

`app/app.vue`

```vue
<template>

</template>
```

`app/pages/index.vue`

```vue
<script setup lang="ts">
const { t, locale } = useI18n();
</script>

<template>
    <h1>{{ t('greeting', { name: 'Alice' }) }}</h1>
    <NuxtLinkLocale to="/" :locale="locale === 'en' ? 'uk' : 'en'">Switch</NuxtLinkLocale>
</template>
```

A fresh Nuxt app ships an `app/app.vue` that renders `<NuxtWelcome />`; replace it with this one so your pages render.

**Svelte**

`src/routes/+page.svelte`

```svelte
<script lang="ts">
    import { useI18n } from '@comvi/svelte';

    const { t, locale, setLocale } = useI18n();
</script>

<h1>{$t('greeting', { name: 'Alice' })}</h1>
<select value={$locale} onchange={(e) => setLocale(e.currentTarget.value)}>
    <option value="en">English</option>
    <option value="uk">Українська</option>
</select>
```

**Solid**

`src/App.tsx`

```tsx
import { useI18n } from '@comvi/solid';

export default function App() {
    const { t, locale, setLocale } = useI18n();

    return (
      <>
        <h1>{t('greeting', { name: 'Alice' })}</h1>
        <select value={locale()} onChange={(e) => setLocale(e.currentTarget.value)}>
          <option value="en">English</option>
          <option value="uk">Українська</option>
        </select>
      </>
    );
}
```

**Vanilla**

`src/switch.ts`

```ts
import { i18n } from './i18n';

await i18n.setLocale('uk');
console.log(i18n.t('greeting', { name: 'Аліса' })); // "Привіт, Аліса!"
```

`src/main.ts`

```ts
import './switch';
```

Put that one line at the top of your `src/main.ts` (or replace a scaffold's `main.ts` with it);
nothing runs until `main.ts` imports it. Top-level `await` needs a build target that has it:
on Vite 5 and 6 set `build.target: 'es2022'` in `vite.config.ts`, or wrap the calls in an
`async` function.

`t('greeting', { name: 'Alice' })` looks up `greeting` in the current locale and fills `{name}`
from the second argument. `setLocale()` switches the locale, and every component that read a
translation re-renders with the new text.

Switch the locale and the heading changes; on Vanilla the translated line is in the browser
console.

## Read next

- [Connect to Comvi](/docs/i18n/connect-comvi): move the catalog to the platform CDN so a wording change ships without a redeploy.
- Your framework page, [React](/docs/i18n/react), [Next.js](/docs/i18n/next), [Vue](/docs/i18n/vue), [Nuxt](/docs/i18n/nuxt), [Svelte](/docs/i18n/svelte), [Solid](/docs/i18n/solid) or [Vanilla JavaScript](/docs/i18n/vanilla): markup in translations, first paint and server rendering.
- [Important defaults](/docs/i18n/important-defaults): what a missing key renders, what `fallbackLocale` loads, and the other behaviours worth knowing before the first bug report.
