Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/en-US/next/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ By the end of this guide, your Next.js app will display content in multiple lang
**Want automatic setup?** Run `npx gt@latest` to configure everything with the [Setup Wizard](/docs/cli/init). This guide covers manual setup.
</Callout>

<Callout type='info'>
**Using the Pages Router?** Follow the [Pages Router quickstart](/docs/next/tutorials/quickstart-pages-router) instead.
</Callout>

---

## Step 1: Install the packages
Expand Down
1 change: 1 addition & 0 deletions docs/en-US/next/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"./api/types",
"---Tutorials---",
"./tutorials/quickstart",
"./tutorials/quickstart-pages-router",
"./tutorials/quickdeploy",
"./tutorials/examples/currency-converter",
"./tutorials/examples"
Expand Down
369 changes: 369 additions & 0 deletions docs/en-US/next/tutorials/quickstart-pages-router.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
---
title: Pages Router Quickstart
description: Add multiple languages to your Next.js Pages Router app in under 10 minutes
---

By the end of this guide, your Next.js Pages Router app will display content in multiple languages, with a language switcher your users can interact with.

In the Pages Router, `gt-next` works through `getServerSideProps`: on each request, the server resolves the user's locale, loads a translations snapshot, and passes both to a `<GTProvider>` in `_app.tsx` so the first render is already translated.

**Prerequisites:**
- A Next.js app using the **Pages Router**
- Node.js 18+

<Callout type='info'>
**Using the App Router?** Follow the [Next.js Quickstart](/docs/next) instead — it uses server components and needs no `getServerSideProps` wiring.
</Callout>

---

## Step 1: Install the packages

`gt-next` is the library that powers translations in your app. `gt` is the CLI tool that prepares translations for production.

<Tabs items={['npm', 'yarn', 'bun', 'pnpm']}>
<Tab value="npm">
```bash
npm i gt-next
npm i -D gt
```
</Tab>
<Tab value="yarn">
```bash
yarn add gt-next
yarn add --dev gt
```
</Tab>
<Tab value="bun">
```bash
bun add gt-next
bun add --dev gt
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add gt-next
pnpm add --save-dev gt
```
</Tab>
</Tabs>

---

## Step 2: Configure your Next.js config

`gt-next` uses a Next.js plugin called **`withGTConfig`** to set up internationalization at build time. Wrap your existing Next.js config with it:

```ts title="next.config.ts"
import { withGTConfig } from 'gt-next/config';

const nextConfig = {};

export default withGTConfig(nextConfig);
```

This plugin reads your translation settings and wires everything together behind the scenes. The config is identical for the App Router and the Pages Router — no router-specific flags are needed.

---

## Step 3: Create a translation config file

Create a **`gt.config.json`** file in your project root. This tells the library which languages you support:

```json title="gt.config.json"
{
"defaultLocale": "en",
"locales": ["es", "fr", "ja"],
"files": {
"gt": {
"output": "public/_gt/[locale].json"
}
}
}
```

- **`defaultLocale`** — the language your app is written in (your source language).
- **`locales`** — the languages you want to translate into. Pick any from the [supported locales list](/docs/platform/supported-locales).
- **`files.gt.output`** — where the CLI saves translation files. `[locale]` is replaced with each language code (e.g., `public/_gt/es.json`).

Add `public/_gt/` to your **`.gitignore`** — these files are generated, not hand-written:

```txt title=".gitignore"
public/_gt/
```

---

## Step 4: Add a load function for local translations

Create a **`loadTranslations`** file in your project root (or `src/` directory). This tells `gt-next` how to load the translation files generated by the CLI:

```ts title="loadTranslations.ts"
export default async function loadTranslations(locale: string) {
try {
const translations = await import(`./public/_gt/${locale}.json`);
return translations.default;
} catch {
return {};
}
}
```

`withGTConfig` automatically detects a `loadTranslations.[js|ts]` file in your project root or `src/` directory — no additional configuration needed.

<Callout type='info'>
**Why local translations?** Local translations are bundled with your app, so they load instantly with no reliance on external services. See the [Local Translation Storage guide](/docs/next/guides/local-tx) for more details and trade-offs.
</Callout>

---

## Step 5: Wrap getServerSideProps on your pages

Wrap each page's `getServerSideProps` with **`withGTServerSideProps`**. On every request, it resolves the user's locale — from the `generaltranslation.locale` cookie if set, otherwise from the `Accept-Language` header — loads a translations snapshot for that locale, and injects both into your page props:

```tsx title="pages/index.tsx"
import type { GetServerSideProps } from 'next';
import { withGTServerSideProps } from 'gt-next';

export const getServerSideProps: GetServerSideProps = withGTServerSideProps(
async (context) => {
return {
props: {
// your own props
},
};
}
);
```

If a page doesn't need server-side props of its own, call it with no arguments:

```tsx title="pages/about.tsx"
import { withGTServerSideProps } from 'gt-next';

export const getServerSideProps = withGTServerSideProps();
```

`withGTServerSideProps` adds `locale` and `translations` to your props (plus an internal `enableI18n` flag). If your inner function returns a `redirect` or `notFound`, it passes the result through untouched without loading translations.

---

## Step 6: Add the GTProvider to your app

The **`GTProvider`** component gives your entire app access to translations. In `_app.tsx`, pull the injected props out of `pageProps` and pass them to the provider. The **`WithGTServerSideProps`** type describes the injected shape:

```tsx title="pages/_app.tsx"
import type { AppProps } from 'next/app';
import { GTProvider, WithGTServerSideProps } from 'gt-next';

export default function App({
Component,
pageProps,
}: AppProps<WithGTServerSideProps>) {
const { locale, translations, ...restPageProps } = pageProps;

return (
<GTProvider locale={locale} translations={translations}>
<Component {...restPageProps} />
</GTProvider>
);
}
```

Because the locale and translations arrive with the server response, the first render is already in the user's language — no client-side loading state.

---

## Step 7: Mark content for translation

Now, wrap any text you want translated with the **`<T>`** component. `<T>` stands for "translate":

```tsx title="pages/index.tsx"
import { T } from 'gt-next';

export default function Home() {
return (
<main>
<T>
<h1>Welcome to my app</h1>
<p>This content will be translated automatically.</p>
</T>
</main>
);
}
```

You can wrap as much or as little JSX as you want inside `<T>`. Everything inside it — text, nested elements, even formatting — gets translated as a unit.

---

## Step 8: Add a language switcher

Drop in a **`<LocaleSelector>`** so users can change languages:

```tsx title="pages/index.tsx"
import { T, LocaleSelector } from 'gt-next';

export default function Home() {
return (
<main>
<LocaleSelector />
<T>
<h1>Welcome to my app</h1>
<p>This content will be translated automatically.</p>
</T>
</main>
);
}
```

`LocaleSelector` renders a dropdown populated with the languages from your `gt.config.json`. When the user picks a language, `gt-next` saves the choice to the `generaltranslation.locale` cookie and reloads the page, so the server re-renders everything in the new locale.

---

## Step 9: Set up environment variables (optional)

To see translations in development, you need API keys from General Translation. These enable **on-demand translation** — your app translates content in real time as you develop.

Create a **`.env.local`** file:

```bash title=".env.local"
GT_API_KEY="your-api-key"
GT_PROJECT_ID="your-project-id"
```

Get your free keys at [dash.generaltranslation.com](https://dash.generaltranslation.com/signup) or by running:

```bash
npx gt auth
```

<Callout type="warn">
For development, use a key starting with `gtx-dev-`. Production keys (`gtx-api-`) are for CI/CD only.

Never expose `GT_API_KEY` to the browser or commit it to source control.
</Callout>

---

## Step 10: See it working

Start your dev server:

<Tabs items={['npm', 'yarn', 'bun', 'pnpm']}>
<Tab value="npm">
```bash
npm run dev
```
</Tab>
<Tab value="yarn">
```bash
yarn dev
```
</Tab>
<Tab value="bun">
```bash
bun dev
```
</Tab>
<Tab value="pnpm">
```bash
pnpm dev
```
</Tab>
</Tabs>

Open [http://localhost:3000](http://localhost:3000) and use the language dropdown to switch languages. You should see your content translated.

<Callout type="info">
In development, translations happen on-demand — you may see a brief loading state the first time you switch to a new language. In production, translations are pre-generated and load instantly.
</Callout>

---

## Step 11: Translate strings (not just JSX)

For plain strings — like `placeholder` attributes, `aria-label` values, or `alt` text — use the **`useGT`** hook:

```tsx title="pages/contact.tsx"
import { useGT } from 'gt-next';

export default function ContactPage() {
const gt = useGT();

return (
<form>
<input
placeholder={gt('Enter your email')}
aria-label={gt('Email input field')}
/>
<button type="submit">{gt('Send')}</button>
</form>
);
}
```

---

## Step 12: Deploy to production

In production, translations are pre-generated at build time (no real-time API calls). Add the translate command to your build script:

```json title="package.json"
{
"scripts": {
"build": "npx gt translate && next build"
}
}
```

Set your **production** environment variables in your hosting provider (Vercel, Netlify, etc.):

```bash
GT_PROJECT_ID=your-project-id
GT_API_KEY=gtx-api-your-production-key
```

<Callout type="warn">
Production keys start with `gtx-api-` (not `gtx-dev-`). Get one from [dash.generaltranslation.com](https://dash.generaltranslation.com). Never prefix it with `NEXT_PUBLIC_`.
</Callout>

That's it — your app is now multilingual. 🎉

---

## Troubleshooting

<Accordions>
<Accordion title="Do I need withGTServerSideProps on every page?">
Yes — `<GTProvider>` requires the `locale` and `translations` props, and they only exist on pages whose `getServerSideProps` is wrapped. For pages that don't fetch their own data, export the no-argument form:

```tsx
export const getServerSideProps = withGTServerSideProps();
```
</Accordion>
<Accordion title="Can I use getStaticProps instead?">
No. Resolving the locale requires per-request data (the locale cookie and `Accept-Language` header), which isn't available during static generation. `gt-next` provides no `getStaticProps` helper for the Pages Router.
</Accordion>
<Accordion title="The language isn't changing when I use the dropdown">
`gt-next` stores the user's language preference in a cookie called `generaltranslation.locale`. If you previously tested with a different language, this cookie may override your selection. Clear your cookies and try again.

- [Chrome](https://support.google.com/chrome/answer/95647)
- [Firefox](https://support.mozilla.org/en-US/kb/delete-cookies-remove-info-websites-stored)
- [Safari](https://support.apple.com/en-mn/guide/safari/sfri11471/16.0/mac/11.0)
</Accordion>
<Accordion title="Translations are slow in development">
This is expected. In development, translations happen on-demand (your content is translated in real time via the API). This delay **does not exist in production** — all translations are pre-generated by `npx gt translate`.
</Accordion>
</Accordions>

---

## Next steps

- [**`<T>` Component Guide**](/docs/next/guides/t) — Learn about variables, plurals, and advanced translation patterns
- [**String Translation Guide**](/docs/next/guides/strings) — Deep dive into `useGT`
- [**Variable Components**](/docs/next/guides/variables) — Handle dynamic content with `<Var>`, `<Num>`, `<Currency>`, and `<DateTime>`
- [**Pluralization**](/docs/next/api/components/plural) — Handle plural forms with the `<Plural>` component
- [**Deploying to Production**](/docs/next/tutorials/quickdeploy) — CI/CD setup, caching, and performance optimization
- [**Shared Strings**](/docs/next/guides/shared-strings) — Translate text in arrays, config objects, and shared data with `msg()`
Loading