# General Translation Knowledge Base: General Translation vs. Intlayer: which should you use?
URL: https://generaltranslation.com/en-US/kb/general-translation-vs-intlayer.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Intlayer keeps each component's content in a typed declaration file next to it, with every language written side by side, and can fill missing languages with an AI provider you configure. General Translation keeps copy inline in your components in one language and generates the other languages with the context of your code.
Category: Internationalization
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

Intlayer is an [open-source i18n library](https://github.com/aymericzip/intlayer) that puts content in a declaration file next to each component, such as `inbox.content.ts`, where each string lists its translations in one `t({ en, es, pl })` call. A hook like `useIntlayer('inbox')` returns that content, typed, in the current locale. Intlayer fills missing languages with an AI provider you configure and ships a free visual editor and an optional hosted CMS, but every language of every string still lives in your source files, for you to write, fill, and review. General Translation takes the other path: you write copy once, inline, in the source language, and General Translation generates the other languages with context from your code.

## What Intlayer does well

- **Content next to the component.** One declaration file per component keeps text close to the code that renders it.
- **Type safety.** `useIntlayer` keys are typed: in version 9.7.0, `useIntlayer('inbx')` is a TypeScript error. A `t()` call missing one of your configured locales is flagged too (a type error in a 9.7.0 test, with `strictMode` setting how strict the check is), so a gap shows up before you ship.
- **Rich content types.** Besides per-locale text, declarations support [plurals by CLDR category](https://intlayer.org/doc/concept/content/plural) (`plural({ one, few, many, other })`), enumerations, conditions, gender, Markdown, HTML, nesting, and insertion.
- **Bring your own model.** `intlayer fill` translates missing locales with OpenAI, Anthropic, Mistral, Gemini, DeepSeek, Ollama, OpenRouter, and other providers, set under [`ai` in `intlayer.config.ts`](https://intlayer.org/doc/concept/configuration) or with `--provider` and `--api-key`. With your own key and the `@intlayer/ai` package installed, [it runs locally](https://intlayer.org/doc/concept/cli/fill) and you pay the provider directly.
- **Visual editing.** The `intlayer-editor` package loads your running app in an editor where writers select and edit text, then writes the change back to your declaration files. [Intlayer's docs say](https://intlayer.org/doc/concept/editor) the editor is Apache 2.0, free for commercial use, and meant for development and staging.
- **Framework reach.** [Guides](https://intlayer.org/doc/get-started) cover Next.js, React with Vite, React Router, TanStack Start, React Native, Vue and Nuxt, Svelte and SvelteKit, Solid, Preact, Astro, Angular, and server frameworks such as Express, NestJS, Fastify, and Hono.

## What Intlayer leaves to you

Here's an inbox's content file with three locales:

```ts title="inbox.content.ts"
import { t, plural, type Dictionary } from 'intlayer';

const inboxContent = {
  key: 'inbox',
  content: {
    title: t({
      en: 'Inbox',
      es: 'Bandeja de entrada',
      pl: 'Skrzynka odbiorcza',
    }),
    unread: t({
      en: plural({
        one: 'You have {{count}} unread message.',
        other: 'You have {{count}} unread messages.',
      }),
      es: plural({
        one: 'Tienes {{count}} mensaje sin leer.',
        other: 'Tienes {{count}} mensajes sin leer.',
      }),
      pl: plural({
        one: 'Masz {{count}} nieprzeczytaną wiadomość.',
        few: 'Masz {{count}} nieprzeczytane wiadomości.',
        many: 'Masz {{count}} nieprzeczytanych wiadomości.',
        other: 'Masz {{count}} nieprzeczytanej wiadomości.',
      }),
    }),
  },
} satisfies Dictionary;

export default inboxContent;
```

```tsx title="Inbox.tsx"
import { useIntlayer } from 'react-intlayer';

export function Inbox({ count }: { count: number }) {
  const { title, unread } = useIntlayer('inbox');
  return (
    <section>
      <h1>{title}</h1>
      <p>{unread({ count })}</p>
    </section>
  );
}
```

That renders correctly in every locale, and the file grows with each one you add: at 20 locales, each string carries 20 entries. A new string needs all of them too. Adding `archive: t({ en: 'Archive' })` produced a type error naming the locales still missing:

```text title="Output"
Type '{ en: string; }' is missing the following properties from type
'Required<Record<keyof __RequiredLocalesRegistry, unknown>>': es, pl
```

At run time, the missing locales fell back to English. To close the gap, you write the translations or run `intlayer fill`. Fill sends the dictionary's content, [its description](https://intlayer.org/doc/concept/content), and an `applicationContext` string from your config to the model. It doesn't see the component or the JSX around a string. [Intlayer's compiler](https://intlayer.org/doc/compiler) can extract plain JSX text into content files at build time, so you can skip writing declarations by hand, but translations still end up in those files.

## General Translation keeps one language in the code

General Translation's libraries render translations the same way, picking each locale's messages and applying its plural rules. The difference is that only the source language lives in your components. Here's the same inbox with `gt-react`:

```tsx title="Inbox.tsx"
import { T, Plural, Num } from 'gt-react';

export function Inbox({ count }: { count: number }) {
  return (
    <T>
      <section>
        <h1>Inbox</h1>
        <Plural
          n={count}
          singular={<p>You have <Num>{count}</Num> unread message.</p>}
          plural={<p>You have <Num>{count}</Num> unread messages.</p>}
        />
      </section>
    </T>
  );
}
```

There's no content file and no key. [`npx gt translate`](/docs/cli/reference/commands/translate) sends each [`<T>`](/docs/react/reference/components/t) block, with its element structure, to the General Translation API and writes translations for every target locale to separate files, so adding a locale changes `gt.config.json`, not your components. You write the plural forms English uses, translation generates the forms each target language needs, and [`<Plural>`](/docs/react/reference/components/plural) picks a translated branch with the reader's locale's rules. [`npx gt validate`](/docs/cli/reference/commands/validate) checks the project without calling the API, and [`gt generate`](/docs/cli/reference/commands/generate) writes templates for teams that translate by hand.

The context is what changes the result. General Translation translates the text and JSX structure inside each marked block together, with developer notes to clarify meaning. A “Save” button for a document means “speichern” in German; an offer to save money means “sparen.” [Context Groups](/docs/platform/dashboard/guides/defining-context-for-translations) apply your Glossary and Custom Prompts to the projects you assign. Reviewers edit in the Dashboard, and the CLI or [Locadex](/docs/platform/locadex/quickstart) handles updates as the code changes. Teams that want to mark existing JSX automatically can turn on [automatic JSX injection](/docs/cli/guides/using-auto-jsx).

The JavaScript libraries are MIT-licensed and can render supplied translations without an account. Keep translations in your build, or publish them to General Translation's CDN.

Cursor uses General Translation for 14+ locales across web and docs. (See [production deployments](https://generaltranslation.com/enterprise).)

## Intlayer vs. General Translation at a glance

| Feature | Intlayer | General Translation |
| --- | --- | --- |
| How you write copy | `t({ en, es, ... })` in a declaration file per component, read with `useIntlayer` | JSX inside [`<T>`](/docs/react/reference/components/t), strings through [`useGT`](/docs/react/reference/hooks/use-gt), or keyed dictionaries |
| Where translations live | In the declaration files, or per-locale content files | Separate per-locale files, or General Translation's CDN |
| AI translation | `intlayer fill` with your provider key, or through Intlayer's service | [`npx gt translate`](/docs/cli/reference/commands/translate) through the General Translation API |
| Context the model gets | Dictionary content, description, and an application context string | Marked JSX structure, developer notes, and assigned Glossary and Custom Prompts |
| Missing locales | Flagged, as a type error in a 9.7.0 test; [`intlayer test`](https://intlayer.org/doc/concept/cli/test) reports them | Generated by [`npx gt translate`](/docs/cli/reference/commands/translate); untranslated content renders in the source language |
| Editing | Free local visual editor; optional hosted CMS | Translation editing and review in the Dashboard |
| Frameworks | React, Next.js, Vue, Svelte, Solid, Angular, Astro, React Native, and Node.js servers | React, Next.js, Vue, Node.js, and [additional SDKs](/docs/overview/get-started) |
| License and cost | Apache 2.0; hosted CMS plans scale by projects, seats, and storage ([pricing](https://app.intlayer.org/pricing), checked October 2026) | MIT JavaScript libraries; [usage-based translation](https://generaltranslation.com/pricing) |

Choose Intlayer when you want every language visible next to the component, type errors for missing locales, an on-page editor for writers, or its Svelte, Solid, or Angular integration. Use General Translation when you'd rather write copy once in the source language and have every other language generated with the context of your code, your Glossary, and Custom Prompts.

## Moving from Intlayer to General Translation

Move one component at a time, keeping its Intlayer translations in use until its General Translation version is ready. In a server render with `react-intlayer` 9.7.0 and `gt-react` 11.4.12, an Intlayer component and a [`<T>`](/docs/react/reference/components/t) component rendered side by side under `IntlayerProvider` and [`GTProvider`](/docs/react/reference/components/gt-provider). Copy the component's `en` values into JSX inside [`<T>`](/docs/react/reference/components/t), generate translations with [`npx gt translate`](/docs/cli/reference/commands/translate), and review the result before switching the component. Start with the [Next.js Quickstart](/docs/react/nextjs-quickstart) or the [React Quickstart](/docs/react/react-quickstart), and see [Migrating i18n libraries](/docs/react/guides/migrating-i18n-libraries) for running two libraries during a move.

## Key takeaways

- Intlayer keeps each component's content in a typed declaration file with every language side by side, catches missing locales as type errors, and fills them with an AI provider you configure.
- General Translation keeps only the source language in your components and generates every other language with context from your code, your Glossary, and Custom Prompts, with review in the Dashboard.
- Intlayer covers more frameworks and offers on-page visual editing; you can move to General Translation one component at a time, since both libraries render side by side.

## Related questions

- [General Translation vs. i18next, react-intl, and next-intl: which i18n library should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-i18n-libraries.mdx): Open-source libraries such as i18next, react-intl, and next-intl don't translate anything. They display translations you supply in a catalog file for each language, written by you, your translators, or a separate service. General Translation's libraries are open source too and do the same job, and General Translation also does the translating, with your code as context.
- [General Translation vs. Paraglide JS: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-paraglide.mdx): Choose General Translation to keep copy inline and generate translations with the structure of your React components, shared terminology, and Dashboard review. Choose Paraglide JS for compiled, typed message functions and per-message tree-shaking. Paraglide renders your catalogs; General Translation connects source copy to translation generation and ongoing updates.
- [Can I do i18n without translation files?](https://generaltranslation.com/en-US/kb/i18n-without-translation-files.mdx): Yes. With gt-react or gt-next, content stays inline in your components inside a `<T>` component, and translations are generated at build time as output, so there are no keys or per-locale JSON files to write and keep in sync.
- [How do I handle plurals in React?](https://generaltranslation.com/en-US/kb/how-to-handle-plurals-in-react.mdx): Don't hard-code plural logic with checks like n === 1. A small helper is enough for English-only apps, but multilingual apps need locale-aware plural rules, which an i18n component such as `<Plural>` from gt-react or gt-next handles for every language.
- [Why use General Translation instead of a TMS (Smartling, Lokalise, Phrase, Crowdin, or Transifex)?](https://generaltranslation.com/en-US/kb/general-translation-vs-tms.mdx): A translation management system (TMS) such as Smartling, Lokalise, Phrase, Crowdin, or Transifex coordinates translators, but engineers still extract strings, send them out, wait for translations, and wire them back into the product. General Translation translates content in your code with its context, ships every language with each release, and runs on open-source libraries with no lock-in.

## Previous and next questions

- Previous question: [General Translation vs. i18next: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-i18next.mdx)
- Next question: [General Translation vs. Lingui: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-lingui.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/kb/sitemap.md) for every article.
