コンテンツへスキップ
Nuxt i18n

Nuxt i18n 連携

自動インポート、SSR、Nitro 互換を備えた Nuxt 3 フル対応。

Setup

Wire Better i18n into Nuxt in three steps

No Nuxt module to install: a plugin, the zero-dependency core client, and the vue-i18n you already have.

Install the client

core delivers and caches the messages. Add @nuxtjs/i18n only if you also want it to own locale routing.

terminalbash
npm install @better-i18n/core vue-i18n

# Optional — only if you want @nuxtjs/i18n to own
# locale routing, prefixes and hreflang:
npm install @nuxtjs/i18n

Create the client at module scope

One instance per server process, so the 60-second cache is shared across requests instead of rebuilt per render.

lib/better-i18n.tsts
import { createI18nCore } from '@better-i18n/core'

// Module scope, not inside a composable: the TTL cache lives on the
// instance, so a per-request client would refetch on every render.
export const betterI18n = createI18nCore({
  projectId: 'your-org/your-project', // Settings → General → Project ID
  defaultLocale: 'en',
})

Register vue-i18n from a Nuxt plugin

The plugin awaits the messages before the app mounts, so SSR output and client hydration read the same strings.

plugins/better-i18n.tsts
import { createI18n } from 'vue-i18n'
import { betterI18n } from '~/lib/better-i18n'

export default defineNuxtPlugin(async (nuxtApp) => {
  const locale = useRoute().params.locale?.toString() ?? 'en'

  // Runs on the server during SSR, and in the browser on a cold client
  // navigation. The CDN shape is the shape vue-i18n expects.
  const messages = await betterI18n.getMessages(locale)

  nuxtApp.vueApp.use(
    createI18n({
      legacy: false,
      locale,
      fallbackLocale: 'en',
      messages: { [locale]: messages },
    })
  )
})

How it works

Where a Nuxt string comes from

No build step and no bundled locale files: the plugin resolves a locale at runtime, on the server first.

Read path — every locale load

Your Nuxt app

useI18n() reads the messages the plugin registered before mount.

0 network calls per render

@better-i18n/core

getMessages(locale) from the plugin — server-side during SSR.

60s TTL · 0 deps

CDN edge

Cloudflare worker answers from the nearest edge cache.

max-age=60 · always 200

R2 object store

The published translation files the sync worker wrote.

source of truth

Fallback chain — tried in order when a hop fails

1In-memory TTL cache
2CDN fetch, with timeout and one retry
3Persistent storage, if configured
4staticData bundled with the app
5Throw — after everything above missed

Write path — dashboard to app

AI or translator

Proposal reviewed in the dashboard, glossary enforced.

MCP · dashboard · CLI

Publish

Sync worker writes the locale files to R2.

better-i18n publish

CDN purge

Fire-and-forget purge of the affected keys and the manifest.

non-critical by design

Live in the app

The next getMessages() past the TTL returns the new copy.

~60s worst case

0dependencies in core
60scache TTL, client and edge
200CDN status, even on failure
5fallback layers before an error
In a page

クイックスタート

モジュールで Nuxt アプリに i18n を追加します。

pages/[locale]/index.vuevue
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { betterI18n } from '~/lib/better-i18n'

const { t, locale, setLocaleMessage } = useI18n()

// Locales come from the project manifest, not a hardcoded array
const { data: languages } = await useAsyncData('locales', () =>
  betterI18n.getLanguages()
)

async function switchTo(next: string) {
  setLocaleMessage(next, await betterI18n.getMessages(next))
  locale.value = next
}
</script>

<template>
  <h1>{{ t('home.title') }}</h1>
  <p>{{ t('home.greeting', { name: 'World' }) }}</p>
</template>
On the server

Translated Nitro server routes

The client has no browser-only dependency, so an API route can answer in the caller's language too.

server/api/greeting.get.tsts
// server/api/greeting.get.ts — Nitro route, same client
import { betterI18n } from '~/lib/better-i18n'

export default defineEventHandler(async (event) => {
  const locale = getQuery(event).locale?.toString() ?? 'en'

  // Shares the in-memory cache with the rest of the server process.
  const messages = await betterI18n.getMessages(locale)

  return { text: messages.api?.greeting ?? '', locale }
})
Capabilities

機能

Nuxt 3 ネイティブモジュール
composables の自動インポート
SEO ユーティリティ(useHead、useSeoMeta)
ロケールベースのルーティング
ロケールの遅延ロード
フル SSR 対応
Nuxt DevTools 連携
Nitro サーバー対応
ハイブリッドレンダリング対応
Works with

Better I18N and the Nuxt ecosystem

We handle translation management and delivery; the Nuxt pieces you already use keep their jobs.

Better I18N + @nuxtjs/i18n

The official Nuxt module for locale routing: path prefixes, redirects, hreflang and SEO head tags.Keeps routing, hands translation delivery to Better I18N

Better I18N + vue-i18n

The formatter underneath: t(), ICU plurals, date and number formatting, reactive locale state.Receives the CDN messages as-is — no adapter in between

Better I18N + Nitro

Nuxt's server engine, where API routes and server-side rendering actually execute.Runs the same zero-dependency client, sharing one cache

他のフレームワークガイドを見る

Get started

Nuxt i18n で開発を始める

無料プランあり。クレジットカード不要。