Skip to content

Fix: Paraglide Not Working — Messages Not Loading, Compiler Errors, or Framework Integration Issues

FixDevs ·

Quick Answer

How to fix Paraglide.js i18n issues — message compilation, type-safe translations, SvelteKit and Next.js integration, language switching, and message extraction from existing code.

The Problem

Paraglide messages are undefined:

import * as m from '$lib/paraglide/messages';

m.welcome();
// TypeError: m.welcome is not a function

Or the compiler fails:

npx @inlang/paraglide-js compile
# Error: No messages found in project

Or language switching doesn’t update the page:

Language changes in the dropdown but text stays in English

Why This Happens

Paraglide.js is a compile-time i18n library that generates type-safe message functions from translation files. Key differences from runtime i18n:

  • Messages are compiled to JavaScript functions — each translation becomes a function like welcome() that returns the translated string for the current locale. If compilation hasn’t run, the functions don’t exist.
  • An inlang project file is required — Paraglide reads messages from a project.inlang/ directory containing settings.json and message files. Without proper project setup, the compiler finds nothing.
  • Language state must be managed per framework — Paraglide doesn’t manage routing or language detection. Framework adapters (SvelteKit, Next.js) handle URL-based language routing and provide the current locale to Paraglide.
  • Tree-shaking removes unused translations — only messages actually imported and called end up in the bundle. This is a feature, but it means unused messages aren’t just “available at runtime.”

Fix 1: Basic Setup

npx @inlang/paraglide-js init
# Creates project.inlang/ directory
// project.inlang/settings.json
{
  "$schema": "https://inlang.com/schema/project-settings",
  "sourceLanguageTag": "en",
  "languageTags": ["en", "ja", "es", "fr"],
  "modules": [
    "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
    "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
    "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
    "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
  ],
  "plugin.inlang.messageFormat": {
    "pathPattern": "./messages/{languageTag}.json"
  }
}
// messages/en.json
{
  "$schema": "https://inlang.com/schema/inlang-message-format",
  "welcome": "Welcome to our app",
  "greeting": "Hello, {name}!",
  "items_count": "{count, plural, =0 {No items} one {# item} other {# items}}",
  "nav_home": "Home",
  "nav_about": "About",
  "nav_contact": "Contact"
}

// messages/ja.json
{
  "$schema": "https://inlang.com/schema/inlang-message-format",
  "welcome": "アプリへようこそ",
  "greeting": "こんにちは、{name}さん!",
  "items_count": "{count, plural, other {{count}個のアイテム}}",
  "nav_home": "ホーム",
  "nav_about": "概要",
  "nav_contact": "お問い合わせ"
}
# Compile messages to JavaScript functions
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide
// Generated: src/lib/paraglide/messages.ts
// Each message becomes a type-safe function:
// export function welcome(): string
// export function greeting(params: { name: string }): string
// export function items_count(params: { count: number }): string

Fix 2: SvelteKit Integration

npm install @inlang/paraglide-sveltekit
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { paraglide } from '@inlang/paraglide-sveltekit/vite';

export default defineConfig({
  plugins: [
    paraglide({
      project: './project.inlang',
      outdir: './src/lib/paraglide',
    }),
    sveltekit(),
  ],
});
// src/lib/i18n.ts
import { createI18n } from '@inlang/paraglide-sveltekit';
import * as runtime from '$lib/paraglide/runtime';

export const i18n = createI18n(runtime, {
  pathnames: {
    '/about': {
      en: '/about',
      ja: '/about',
      es: '/acerca-de',
    },
  },
  exclude: ['/api'],
});
// src/hooks.ts
import { i18n } from '$lib/i18n';
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = i18n.handle();
// src/routes/+layout.ts
import { i18n } from '$lib/i18n';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = ({ url }) => {
  return i18n.layoutLoad({ url });
};
<!-- src/routes/+page.svelte -->
<script>
  import * as m from '$lib/paraglide/messages';
  import { i18n } from '$lib/i18n';
  import { languageTag } from '$lib/paraglide/runtime';
</script>

<h1>{m.welcome()}</h1>
<p>{m.greeting({ name: 'Alice' })}</p>
<p>{m.items_count({ count: 5 })}</p>

<!-- Language switcher -->
<nav>
  <a href={i18n.route($page.url.pathname)} hreflang="en">English</a>
  <a href={i18n.route($page.url.pathname)} hreflang="ja">日本語</a>
  <a href={i18n.route($page.url.pathname)} hreflang="es">Español</a>
</nav>

<p>Current: {languageTag()}</p>

Fix 3: Next.js Integration

npm install @inlang/paraglide-next
// next.config.mjs
import { paraglide } from '@inlang/paraglide-next/plugin';

export default paraglide({
  paraglide: {
    project: './project.inlang',
    outdir: './src/lib/paraglide',
  },
  // Next.js config
});
// src/lib/i18n.ts
import { createI18n } from '@inlang/paraglide-next';
import * as runtime from '@/lib/paraglide/runtime';

export const { middleware, Link, useRouter, usePathname, redirect, permanentRedirect } =
  createI18n(runtime);
// middleware.ts
export { middleware } from '@/lib/i18n';

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
};
// app/[lang]/layout.tsx
import { LanguageProvider } from '@inlang/paraglide-next';
import { languageTag } from '@/lib/paraglide/runtime';

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <LanguageProvider>
      <html lang={languageTag()}>
        <body>{children}</body>
      </html>
    </LanguageProvider>
  );
}
// app/[lang]/page.tsx — use translations
import * as m from '@/lib/paraglide/messages';
import { Link } from '@/lib/i18n';

export default function Home() {
  return (
    <div>
      <h1>{m.welcome()}</h1>
      <p>{m.greeting({ name: 'Alice' })}</p>

      {/* Type-safe Link with locale handling */}
      <Link href="/about">
        {m.nav_about()}
      </Link>
    </div>
  );
}

Fix 4: Type-Safe Message Parameters

// messages/en.json
{
  "order_status": "Order #{orderId} is {status}",
  "price": "Total: {amount, number, ::currency/USD}",
  "last_login": "Last login: {date, date, medium}",
  "file_size": "{size, number, ::compact-short} bytes",
  "progress": "{percent, number, ::percent} complete"
}
// Generated functions are fully typed:
import * as m from '$lib/paraglide/messages';

m.order_status({ orderId: '12345', status: 'shipped' });
// "Order #12345 is shipped"

m.greeting({ name: 'Alice' });
// TypeScript error if name is missing:
// m.greeting({})  // Error: Property 'name' is missing

// Unused messages are tree-shaken from the bundle
// Only imported messages end up in production code

Fix 5: Development Workflow

# Compile messages (run after editing .json files)
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide

# Watch mode — recompile on message changes
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide --watch

# With the Vite plugin, compilation is automatic during dev
// package.json
{
  "scripts": {
    "i18n:compile": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide",
    "dev": "npm run i18n:compile && vite dev",
    "build": "npm run i18n:compile && vite build",
    "i18n:lint": "npx @inlang/cli lint --project ./project.inlang"
  }
}
# Lint translations — find missing or empty messages
npx @inlang/cli lint --project ./project.inlang

# Output:
# ⚠ Missing translation for "nav_contact" in "ja"
# ⚠ Empty pattern for "description" in "es"

Fix 6: Using the Inlang Editor

# Visual editor for translations (VS Code extension or web app)
# Install: Sherlock — i18n inspector (VS Code extension)

# Or use the Fink web editor:
# Open: https://fink.inlang.com
# Load your project.inlang/ directory
# Translators can edit messages in a web UI

Still Not Working?

m.welcome is not a function — the paraglide output hasn’t been compiled. Run npx @inlang/paraglide-js compile or ensure the Vite/Next.js plugin is configured. The outdir must match your import path ($lib/paraglide for SvelteKit, @/lib/paraglide for Next.js).

Compiler finds no messagesproject.inlang/settings.json must point to the correct message files. Check plugin.inlang.messageFormat.pathPattern matches where your .json files are. The default is ./messages/{languageTag}.json.

Language switching doesn’t update text — Paraglide needs the framework adapter to manage the current locale. In SvelteKit, use i18n.handle() in hooks. In Next.js, use the Paraglide middleware. Without the adapter, languageTag() always returns the default.

Bundle still includes all languages — Paraglide tree-shakes unused messages but includes all languages for used messages. This is by design — the current locale is determined at runtime. The per-language overhead is minimal (just the translated strings, no library code).

For related i18n issues, see Fix: i18next Not Working and Fix: Lingui Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles