{"id":16594025,"url":"https://github.com/atilafassina/next-g11n","last_synced_at":"2025-03-23T14:31:10.185Z","repository":{"id":46933117,"uuid":"405718666","full_name":"atilafassina/next-g11n","owner":"atilafassina","description":"translate and localize your Next.js app smoothly","archived":false,"fork":false,"pushed_at":"2021-10-10T13:19:19.000Z","size":757,"stargazers_count":27,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T17:10:16.423Z","etag":null,"topics":["i18n","jamstack","nextjs","typescript"],"latest_commit_sha":null,"homepage":"","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/atilafassina.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":"2021-09-12T18:14:11.000Z","updated_at":"2023-05-12T17:05:16.000Z","dependencies_parsed_at":"2022-09-24T18:34:39.417Z","dependency_job_id":null,"html_url":"https://github.com/atilafassina/next-g11n","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atilafassina%2Fnext-g11n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atilafassina%2Fnext-g11n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atilafassina%2Fnext-g11n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atilafassina%2Fnext-g11n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atilafassina","download_url":"https://codeload.github.com/atilafassina/next-g11n/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245115741,"owners_count":20563223,"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":["i18n","jamstack","nextjs","typescript"],"created_at":"2024-10-11T23:44:50.125Z","updated_at":"2025-03-23T14:31:09.594Z","avatar_url":"https://github.com/atilafassina.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"light-logo.png\" alt=\"next-g11n logo\" /\u003e\n\n## next-g11n 🌐\n\nYour toolbelt for translating Next.js apps\n\n![npm badge](https://img.shields.io/npm/v/next-g11n?color=%23df0000\u0026style=flat-square)\n![typescript badge](https://img.shields.io/npm/types/next-g11n?style=flat-square)\n\n\u003c/div\u003e\n\n🪡 tailor-made for [Next.js i18n Routing](https://nextjs.org/docs/advanced-features/i18n-routing)\n\n👮 Type-safe dictionaries\n\n☁️ optimized for Server-Side Rendering\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"screenshot-vscode.png\" alt=\"vscode screenshot of missing translation yielding warning\" /\u003e\n\u003c/div\u003e\n\n## Getting started 🏗\n\n1. Install the dependency\n   `yarn add next-g11n` or `npm i next-g11n`\n\n2. Create your `dictionary.ts` (or any other name, actually)\n\n| type     | default |\n| -------- | ------- |\n| `object` | -       |\n\nThe translation object/map. It can either carry one locale, or all. The object format is as follows:\n\n```ts\n{\n  'locale-key': {\n    'translation-key': 'value - number or string'\n  }\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eCopy/paste example\u003c/summary\u003e\n\n```ts\n// example dictionary.ts\nconst dictionary = {\n  // top level are locales (de) or (de-at), for example\n  en: {\n    // these are the translation keys\n    hello: 'Hi',\n  },\n  'de-de': {\n    hello: 'Hallo',\n  },\n  'de-at': {\n    hello: 'Grüß Gott',\n  },\n}\n```\n\n\u003c/details\u003e\n\n3. Choose between Client-Side-only or Server-Side translations\n\n## Helper methods 🧠\n\n### getLocale\n\nReceives a `router` instance from Next.js `useRouter` and returns the current locale if supported by your app, or the `defaultLocale`.\n\n```tsx\nconst router = useRouter()\nconst g11nLocale = getLocale(router) as Locales\n```\n\n## Client-side only 🪝\n\nWhen you call your hook, establish the types for the dictionary\n\n```tsx\nimport type { NextPage } from 'next'\nimport { useG11n } from 'next-g11n'\nimport { DICTIONARY } from '../dictionary'\n\nconst Home: NextPage = () =\u003e {\n  const { translate: t } = useG11n\u003ctypeof DICTIONARY\u003e(DICTIONARY)\n\n  return (\n    \u003ch1\u003e{t('hello')}\u003c/h1\u003e\n  )\n}\n\n```\n\n\u003e ⚠️ this strategy will bring in the entire Dictionary to your bundle\n\n### useFallback\n\nWhen `true`, if the translation does not exist in dictionary, the `key` will show. When set to false, it will throw an error.\n\n| type      | default |\n| --------- | ------- |\n| `boolean` | `false` |\n\n† recomended for apps without TypeScript\n\n## Server-Side Rendering 🌤\n\n### Static Translations\n\nTranslations which do not require interpolation, can be completely rendered on the Server-Side within `getStaticProps`. An object with all translated terms will be injected in your page `props`.\n\n```tsx\nconst Home = ({ hello }: InferGetStaticPropsType\u003ctypeof getStaticProps\u003e) =\u003e {\n  const router = useRouter()\n  const g11nLocale = getLocale(router) as Locales\n\n  return \u003ch1 className={styles.title}\u003e{hello[g11nLocale]}\u003c/h1\u003e\n}\n\nexport const getStaticProps = async () =\u003e {\n  const hello = createStaticTerm\u003cKeys, Locales\u003e('hello', DICTIONARY)\n\n  return {\n    props: {\n      hello,\n    },\n  }\n}\n```\n\n### Interpolated Terms\n\nTo interpolate terms there are 2 methods required. On the server-side it’s required to map all possible **translation terms** (still with template variables) by locale with `createFunctionTerm`. That `object` can then be passed down to each page `props`. Once arriving on the client-side, it’s required to use `clientSideTranslate` to map each **translation term** to its method to interpolate the term with each variable and assign the proper types.\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhy 2 methods?\u003c/summary\u003e\n\nFunctions are not serializable out-of-the-box in JavaScript, and `getStaticProps` only passes down serialized object as its returned `props`. It is possible to call both methods on the server if you handle serializing the output functions. But then again, it will be required to deserializing them on the client-side.\n\n\u003c/details\u003e\n\n#### createFunctionTerm (server-side)\n\nThis method will receive a translation key, and the whole dictionary. It will output a _raw_ translation term (with uninterpolated variables). This is an object literal ready to be serialized by `getStaticProps` on its way to the client-side.\n\n```tsx\nconst rawTerm = createFunctionTerm('term', DICTIONARY)\n```\n\n#### clientSideTranslate (client-side)\n\nOnce on the client-side, this method will receive the _raw term_ from `createFunctionTerm` and ouput one method for each supported locale. This method is a **TypeScript generic**, first parameter is your `Locales` exported from your whole dictionary, and the second is a `string union` with each available parameter for that translation term.\n\n```tsx\nexport type Locales = keyof typeof DICTIONARY\n\nconst Page = ({ rawTerm }: InferGetStaticPropsType\u003ctypeof getStaticProps\u003e) =\u003e {\n  const router = useRouter()\n  const g11nLocale = getLocale(router) as Locales\n  const term = clientSideTranslate\u003cLocales, 'var1' | 'var2'\u003e(rawTerm)\n\n  return \u003ch1\u003e{term[g11nLocale]({ var1: 'foo', var2: 'bar' })}\u003c/h1\u003e\n}\n```\n\n## Examples 🍱\n\n| directory                                                  | description                                 |\n| ---------------------------------------------------------- | ------------------------------------------- |\n| [client-side](tree/main/example/client-side)               | minimal setup for Client-side translations  |\n| [server-side-render](tree/main/example/server-side-render) | minimal setup for SSR-friendly translations |\n\n## Additional info ❓\n\n- Support starts with Next.js i18n Routing, so `v10+`\n- Next.js is a `peerDependency`\n- React is a `peerDependency`\n\n## Glossary 📖\n\n| abbreviation | full word            | meaning                                              |\n| ------------ | -------------------- | ---------------------------------------------------- |\n| i18n         | internationalization | enable adaptation of a product to multiple languages |\n| L10n         | localization         | translating and customizing to a specific place      |\n| g11n         | globalization        | addressing requirements to launch globally           |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatilafassina%2Fnext-g11n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatilafassina%2Fnext-g11n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatilafassina%2Fnext-g11n/lists"}