{"id":20206501,"url":"https://github.com/useflyyer/next-rosetta","last_synced_at":"2025-04-10T12:33:02.229Z","repository":{"id":38440218,"uuid":"308970235","full_name":"useflyyer/next-rosetta","owner":"useflyyer","description":"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.","archived":false,"fork":false,"pushed_at":"2023-01-08T22:54:10.000Z","size":3008,"stargazers_count":64,"open_issues_count":5,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-17T16:06:41.010Z","etag":null,"topics":["flyyer","i18n","next","nextjs","rosetta","typescript","vercel"],"latest_commit_sha":null,"homepage":"https://next-rosetta.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/useflyyer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-31T20:55:01.000Z","updated_at":"2024-09-20T12:59:41.000Z","dependencies_parsed_at":"2023-02-08T07:45:51.544Z","dependency_job_id":null,"html_url":"https://github.com/useflyyer/next-rosetta","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/useflyyer%2Fnext-rosetta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/useflyyer%2Fnext-rosetta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/useflyyer%2Fnext-rosetta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/useflyyer%2Fnext-rosetta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/useflyyer","download_url":"https://codeload.github.com/useflyyer/next-rosetta/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217123,"owners_count":21066633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flyyer","i18n","next","nextjs","rosetta","typescript","vercel"],"created_at":"2024-11-14T05:24:30.008Z","updated_at":"2025-04-10T12:33:02.205Z","avatar_url":"https://github.com/useflyyer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# next-rosetta 🌎🌍🌏\n\n\u003e Add i18n in less than 5 minutes — **Built for Next.js 10**\n\n![demo](./.github/demo.gif)\n\nLightweight, simple, easy to integrate, extendable, no custom server required and efficient because it will only download the required translations for your current locale.\n\n[See live demo](https://next-rosetta.vercel.app)\n\nSupports typed locales via the Template literal and Recursive types. **Requires TypeScript \u003e=4.1.0**\n\nNote: Currently types is only supported using dot notation. Eg: `t(\"about.title.0.description\")`.\n\n![typescript intellisense example](./.github/typing.png)\n\n## Usage\n\n### Install\n\nFirst step: downloading this dependency.\n\n```sh\n# with npm\nnpm install next-rosetta\n\n# with yarn\nyarn add next-rosetta\n```\n\n### Update next.config.js\n\nUpdate your `next.config.js` by adding a `i18n` section:\n\n```ts\n// ./next.config.js\nmodule.exports = {\n  i18n: {\n    locales: [\"en\", \"es\"],\n    defaultLocale: \"en\",\n  },\n};\n```\n\nFor more info refer to: https://nextjs.org/docs/advanced-features/i18n-routing\n\n### Create locales\n\nMake 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!**\n\n```ts\n// ./i18n/index.tsx\nexport interface MyLocale {\n  locale: string;\n  title: string;\n  subtitle: string;\n  profile: {\n    button: string;\n  };\n  welcome: string;\n}\n```\n\n```ts\n// ./i18n/en.tsx\nimport type { MyLocale } from \".\";\n\nexport const table: MyLocale = {\n  locale: \"English\",\n  title: \"Next.js 10 + Rosetta with native i18n integration\",\n  subtitle: \"Click below to update your current locale 👇\",\n  profile: {\n    button: \"Press me!\",\n  },\n  welcome: \"Welcome {{name}}! 😃\", // with variable replacement\n};\n```\n\n```ts\n// ./i18n/es.tsx\nimport type { MyLocale } from \".\";\n\nexport const table: MyLocale = {\n  locale: \"Español\",\n  title: \"Next.js 10 + Rosetta con integración nativa de i18n\",\n  subtitle: \"Presiona aquí abajo para cambiar tu lenguaje 👇\",\n  profile: {\n    button: \"Presióname!\",\n  },\n  welcome: \"Bienvenido {{name}}! 👋\", // with variable replacement\n};\n```\n\nDealing with long texts? You can use [`endent`](https://github.com/indentjs/endent) or similar libraries.\n\n```ts\nimport endent from \"endent\";\n\nimport type { MyLocale } from \".\";\n\nexport const table: MyLocale = {\n  markdown: endent`\n    # Title\n\n    This string will have a correct right indentation.\n  `,\n}\n```\n\n### Add the i18n provider\n\nImport `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`.\n\n```tsx\n// ./pages/_app.tsx\nimport type { AppProps } from \"next/app\";\nimport { I18nProvider } from \"next-rosetta\";\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  return (\n    \u003cI18nProvider table={pageProps.table}\u003e\n      \u003cComponent {...pageProps} /\u003e\n    \u003c/I18nProvider\u003e\n  );\n}\n\nexport default MyApp;\n```\n\n## Load and render\n\nTo import locales you must call this on the server side code (or on the static render):\n\n```ts\nconst locale = \"en\";\nconst { table = {} } = await import(`../i18n/${locale}`);\n```\n\nHere is an example if you are using `getStaticProps`:\n\n```tsx\n// ./pages/index.tsx\nimport type { GetStaticProps } from \"next\";\nimport { useI18n, I18nProps } from \"next-rosetta\";\n\n// Import typing\nimport type { MyLocale } from \"../i18n\";\n\nfunction HomePage() {\n  const { t } = useI18n\u003cMyLocale\u003e();\n  return (\n    \u003cdiv\u003e\n      \u003ch3\u003e\n        {t(\"title\")}\n      \u003c/h3\u003e\n      \u003cp\u003e\n        {t(\"welcome\", { name: \"John\" })}\n      \u003c/p\u003e\n      \u003cbutton\u003e\n        {t(\"profile.button\")}\n      \u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n\n// You can use I18nProps\u003cT\u003e for type-safety (optional)\nexport const getStaticProps: GetStaticProps\u003cI18nProps\u003cMyLocale\u003e\u003e = async (context) =\u003e {\n  const locale = context.locale || context.defaultLocale;\n  const { table = {} } = await import(`../i18n/${locale}`); // Import locale\n  return { props: { table } }; // Passed to `/pages/_app.tsx`\n};\n```\n\nAny component can access the locale translations by using the `useI18n` hook.\n\n```tsx\n// ./pages/index.tsx\nimport Link from \"next/link\";\nimport { useRouter } from \"next/router\";\nimport { useI18n } from \"next-rosetta\";\n\n// Import typing\nimport type { MyLocale } from \"../i18n\";\n\nfunction LocaleSelector() {\n  const { locale, locales, asPath } = useRouter(); // Get current locale and locale list\n  const { t } = useI18n\u003cMyLocale\u003e();\n  // ...\n}\n```\n\nFor more info regarding `rosetta` API please refer to: https://github.com/lukeed/rosetta\n\n## Example\n\nHere is a more complete example of page inside the `/page` directory:\n\n```tsx\n// ./pages/index.tsx\nimport { useI18n, I18nProps } from \"next-rosetta\";\nimport { useRouter } from \"next/router\";\nimport Head from \"next/head\";\nimport Link from \"next/link\";\n\nimport type { MyLocale } from \"../i18n\"; // Import typing\n\nexport default function Home() {\n  const { locale, locales, asPath } = useRouter();\n  const i18n = useI18n\u003cMyLocale\u003e();\n  const { t } = i18n;\n\n  return (\n    \u003cdiv\u003e\n      \u003cHead\u003e\n        \u003ctitle\u003e{t(\"locale\")}\u003c/title\u003e\n      \u003c/Head\u003e\n      \u003cmain\u003e\n        \u003ch1\u003e{t(\"title\")}\u003c/h1\u003e\n        \u003cp\u003e{t(\"subtitle\")}\u003c/p\u003e\n        \u003cp\u003e{t(\"welcome\", { name: \"John\" })}\u003c/p\u003e\n        \u003cul\u003e\n          {locales?.map((loc) =\u003e (\n            \u003cli key={loc}\u003e\n              \u003cLink href={asPath} locale={loc}\u003e\n                \u003ca className={loc === locale ? \"is-active\" : \"\"}\u003e{loc}\u003c/a\u003e\n              \u003c/Link\u003e\n            \u003c/li\u003e\n          ))}\n        \u003c/ul\u003e\n      \u003c/main\u003e\n    \u003c/div\u003e\n  );\n}\n\n// Server-side code\n\nimport type { GetStaticProps } from \"next\";\n\nexport const getStaticProps: GetStaticProps\u003cI18nProps\u003cMyLocale\u003e\u003e = async (context) =\u003e {\n  const locale = context.locale || context.defaultLocale;\n  const { table = {} } = await import(`../i18n/${locale}`); // Import locale\n  return { props: { table } }; // Passed to `/pages/_app.tsx`\n};\n```\n\n### Example with getServerSideProps\n\nThis is compatible with your current server side logic. Here is an example:\n\n```tsx\n// ./pages/posts/[id].tsx\nimport type { GetServerSideProps } from \"next\";\nimport { useI18n, I18nProps } from \"next-rosetta\";\n\n// Import typing\nimport type { MyLocale } from \"../i18n\";\n\ntype Props = { post: any };\n\nexport default function PostPage({ post, ...props }: Props) {\n  const { t } = useI18n\u003cMyLocale\u003e();\n  // ...\n}\n\nexport const getServerSideProps: GetServerSideProps\u003cProps \u0026 I18nProps\u003e = async (context) =\u003e {\n  const locale = context.locale || context.defaultLocale;\n\n  const data = await fetch(`/posts/${context.params[\"id\"]}`).then(res =\u003e res.json());\n\n  const { table = {} } = await import(`../../i18n/${locale}`);\n  return { props: { table, post: data } };\n};\n```\n\n## FAQ\n\n### Is a JSON locale table supported?\n\nYes. Just import is as \u003ccode\u003eawait import(`../../i18n/${locale}.json`);\u003c/code\u003e\n\n### React complains about `unknown` is not a valid children type\n\nIf you have this error:\n\n```txt\nType 'unknown' is not assignable to type 'ReactNode'.ts\n```\n\nYou are probably using a wrong path, you have a typo or you are using arrays as path (`t([\"foo\", \"bar\"])` won't infer type).\n\nTo force a type:\n\n```tsx\nconst en = {\n  title: \"Hello\",\n}\nconst { t } = useI18n\u003ctypeof en\u003e();\n```\n\n```tsx\n// type is 'unknown'\nconst text = t(\"foo\") // note 'foo' doesn't exist in locale definition.\n// React error\n\u003cspan\u003e{text}\u003cspan\u003e\n```\n\n```tsx\n// type is 'string'\nconst text = t\u003cstring\u003e(\"foo\")\n// ok\n\u003cspan\u003e{text}\u003cspan\u003e\n```\n\n### How to add a button to change locale?\n\nCreate some `\u003cLink /\u003e` 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`.\n\nThe 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.\n\n```tsx\nexport default function ChangeLocale() {\n  const { locale, locales, asPath } = useRouter();\n  const i18n = useI18n\u003cMyLocale\u003e();\n\n  return (\n    \u003cdiv\u003e\n      {locales?.map((loc) =\u003e {\n        const isActive = loc === locale;\n        return (\n          \u003cLink key={loc} href={asPath} locale={loc}\u003e\n            \u003ca\u003e{loc}\u003c/a\u003e\n          \u003c/Link\u003e\n        );\n      })}\n    \u003c/div\u003e\n  );\n}\n```\n\n### IDE Autocomplete\n\nIDEs won't autocomplete while typing, only after the path is written you can see the types.\n\nThis is a limitation of Typescript, we would require a pre-compilation steps of each possible path to allow this.\n\n## TODO\n\n- Support pluralization.\n- Support function definitions with arguments. Only serializable locales are possible right now.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuseflyyer%2Fnext-rosetta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fuseflyyer%2Fnext-rosetta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuseflyyer%2Fnext-rosetta/lists"}