Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/useflyyer/next-rosetta
Next.js + Rosetta + TypeScript with native i18n support | Lightweight, simple, easy to integrate, no custom server required and efficient because will only download the locale you need.
https://github.com/useflyyer/next-rosetta
flyyer i18n next nextjs rosetta typescript vercel
Last synced: about 17 hours ago
JSON representation
Next.js + Rosetta + TypeScript with native i18n support | Lightweight, simple, easy to integrate, no custom server required and efficient because will only download the locale you need.
- Host: GitHub
- URL: https://github.com/useflyyer/next-rosetta
- Owner: useflyyer
- License: mit
- Created: 2020-10-31T20:55:01.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-01-08T22:54:10.000Z (almost 2 years ago)
- Last Synced: 2024-11-11T20:13:25.347Z (3 days ago)
- Topics: flyyer, i18n, next, nextjs, rosetta, typescript, vercel
- Language: TypeScript
- Homepage: https://next-rosetta.vercel.app
- Size: 2.87 MB
- Stars: 64
- Watchers: 4
- Forks: 4
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# next-rosetta 🌎🌍🌏
> Add i18n in less than 5 minutes — **Built for Next.js 10**
![demo](./.github/demo.gif)
Lightweight, simple, easy to integrate, extendable, no custom server required and efficient because it will only download the required translations for your current locale.
[See live demo](https://next-rosetta.vercel.app)
Supports typed locales via the Template literal and Recursive types. **Requires TypeScript >=4.1.0**
Note: Currently types is only supported using dot notation. Eg: `t("about.title.0.description")`.
![typescript intellisense example](./.github/typing.png)
## Usage
### Install
First step: downloading this dependency.
```sh
# with npm
npm install next-rosetta# with yarn
yarn add next-rosetta
```### Update next.config.js
Update your `next.config.js` by adding a `i18n` section:
```ts
// ./next.config.js
module.exports = {
i18n: {
locales: ["en", "es"],
defaultLocale: "en",
},
};
```For more info refer to: https://nextjs.org/docs/advanced-features/i18n-routing
### Create locales
Make a directory named `i18n` on the root of your project. If you are using TypeScript you can define the type schema and create every locale based on that interface. **Type safety! Excelente!**
```ts
// ./i18n/index.tsx
export interface MyLocale {
locale: string;
title: string;
subtitle: string;
profile: {
button: string;
};
welcome: string;
}
``````ts
// ./i18n/en.tsx
import type { MyLocale } from ".";export const table: MyLocale = {
locale: "English",
title: "Next.js 10 + Rosetta with native i18n integration",
subtitle: "Click below to update your current locale 👇",
profile: {
button: "Press me!",
},
welcome: "Welcome {{name}}! 😃", // with variable replacement
};
``````ts
// ./i18n/es.tsx
import type { MyLocale } from ".";export const table: MyLocale = {
locale: "Español",
title: "Next.js 10 + Rosetta con integración nativa de i18n",
subtitle: "Presiona aquí abajo para cambiar tu lenguaje 👇",
profile: {
button: "Presióname!",
},
welcome: "Bienvenido {{name}}! 👋", // with variable replacement
};
```Dealing with long texts? You can use [`endent`](https://github.com/indentjs/endent) or similar libraries.
```ts
import endent from "endent";import type { MyLocale } from ".";
export const table: MyLocale = {
markdown: endent`
# TitleThis string will have a correct right indentation.
`,
}
```### Add the i18n provider
Import `I18nProvider` from `"next-rosetta"` and wrap your app in it. From `pageProps` take `table` which is the current locale object and pass it to `I18nProvider`.
```tsx
// ./pages/_app.tsx
import type { AppProps } from "next/app";
import { I18nProvider } from "next-rosetta";function MyApp({ Component, pageProps }: AppProps) {
return (
);
}export default MyApp;
```## Load and render
To import locales you must call this on the server side code (or on the static render):
```ts
const locale = "en";
const { table = {} } = await import(`../i18n/${locale}`);
```Here is an example if you are using `getStaticProps`:
```tsx
// ./pages/index.tsx
import type { GetStaticProps } from "next";
import { useI18n, I18nProps } from "next-rosetta";// Import typing
import type { MyLocale } from "../i18n";function HomePage() {
const { t } = useI18n();
return (
{t("title")}
{t("welcome", { name: "John" })}
{t("profile.button")}
)
}// You can use I18nProps for type-safety (optional)
export const getStaticProps: GetStaticProps> = async (context) => {
const locale = context.locale || context.defaultLocale;
const { table = {} } = await import(`../i18n/${locale}`); // Import locale
return { props: { table } }; // Passed to `/pages/_app.tsx`
};
```Any component can access the locale translations by using the `useI18n` hook.
```tsx
// ./pages/index.tsx
import Link from "next/link";
import { useRouter } from "next/router";
import { useI18n } from "next-rosetta";// Import typing
import type { MyLocale } from "../i18n";function LocaleSelector() {
const { locale, locales, asPath } = useRouter(); // Get current locale and locale list
const { t } = useI18n();
// ...
}
```For more info regarding `rosetta` API please refer to: https://github.com/lukeed/rosetta
## Example
Here is a more complete example of page inside the `/page` directory:
```tsx
// ./pages/index.tsx
import { useI18n, I18nProps } from "next-rosetta";
import { useRouter } from "next/router";
import Head from "next/head";
import Link from "next/link";import type { MyLocale } from "../i18n"; // Import typing
export default function Home() {
const { locale, locales, asPath } = useRouter();
const i18n = useI18n();
const { t } = i18n;return (
{t("locale")}
{t("title")}
{t("subtitle")}
{t("welcome", { name: "John" })}
{locales?.map((loc) => (
{loc}
))}
);
}// Server-side code
import type { GetStaticProps } from "next";
export const getStaticProps: GetStaticProps> = async (context) => {
const locale = context.locale || context.defaultLocale;
const { table = {} } = await import(`../i18n/${locale}`); // Import locale
return { props: { table } }; // Passed to `/pages/_app.tsx`
};
```### Example with getServerSideProps
This is compatible with your current server side logic. Here is an example:
```tsx
// ./pages/posts/[id].tsx
import type { GetServerSideProps } from "next";
import { useI18n, I18nProps } from "next-rosetta";// Import typing
import type { MyLocale } from "../i18n";type Props = { post: any };
export default function PostPage({ post, ...props }: Props) {
const { t } = useI18n();
// ...
}export const getServerSideProps: GetServerSideProps = async (context) => {
const locale = context.locale || context.defaultLocale;const data = await fetch(`/posts/${context.params["id"]}`).then(res => res.json());
const { table = {} } = await import(`../../i18n/${locale}`);
return { props: { table, post: data } };
};
```## FAQ
### Is a JSON locale table supported?
Yes. Just import is as
await import(`../../i18n/${locale}.json`);
### React complains about `unknown` is not a valid children type
If you have this error:
```txt
Type 'unknown' is not assignable to type 'ReactNode'.ts
```You are probably using a wrong path, you have a typo or you are using arrays as path (`t(["foo", "bar"])` won't infer type).
To force a type:
```tsx
const en = {
title: "Hello",
}
const { t } = useI18n();
``````tsx
// type is 'unknown'
const text = t("foo") // note 'foo' doesn't exist in locale definition.
// React error
{text}
``````tsx
// type is 'string'
const text = t("foo")
// ok
{text}
```### How to add a button to change locale?
Create some `` and set the `locale` prop to change locale. It is important to note you should set the `href` variable to the current `asPath` from `useRouter`.
The difference between `router.route` and `router.asPath` is that the first has path value with params (eg: `/products/[id]`) and `asPath` has the replaced values.
```tsx
export default function ChangeLocale() {
const { locale, locales, asPath } = useRouter();
const i18n = useI18n();return (
);
}
```### IDE Autocomplete
IDEs won't autocomplete while typing, only after the path is written you can see the types.
This is a limitation of Typescript, we would require a pre-compilation steps of each possible path to allow this.
## TODO
- Support pluralization.
- Support function definitions with arguments. Only serializable locales are possible right now.