UNPKG

next-translations

Version:
398 lines (305 loc) 25 kB
# next-translations Thanks to this package you will be able to add to your website written in **NextJS** to **download/manage** translations on your website! Thanks to this package you will be able to build a very efficient website that will have generated **pages WITH nested translations!**. Powerful package also for **STATIC** and **SERVER SIDE RENDERING** sites in **NextJS**! Works **without i18n**! #### install translations ```bash npm i next-translations ``` #### translations.config.js - you need to add this config file to your project | Parameter | Type | Default | Description | | :------------------------------- | :----------------------------------------------------------------- | :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `defaultLocale` | `string` | `'en'` | **Required**. The default language on your site | | `locales` | `string[]` | `['en']` | **Required**. All available languages on your website. | | `sitesForLoggedUser` | `string[]` | `[]` | All paths available ONLY for the active session | | `sitedForLoggedAndNotLoggedUser` | `string[]` | `[]` | All paths available for the all sessions | | `redirectForLoggedUser` | `string` | `'/'` | Redirects the user when on a route for non-logged in users | | `redirectForNotLoggedUser` | `string` | `'/'` | Redirects the user when in the route for logged in users | | `errorPagePath` | `string` | `'/404'` | Showing if we have a custom link to the error page so that unnecessary redirects are not made | | `outputFolderTranslations` | `string` | `'/public/locales'` | The path to your translations. **NOTE**: If you download translations using next-translations, they will be saved to the given address. For the site to work properly, they **must be** in the `/public` folder. | | `defaultLocaleWithMultirouting` | `boolean` | `undefined` | The language to be excluded from multi routing. For example, we want /index.js to have the language from defaultLocale prop, then it should be substituted into this variable. Other languages (if any) will be available in `/pages/[locale]` | | `constNamespaces` | `string[]` | `['common']` | These are all the namespaces we use throughout the project so as not to define them on every page. | | `componentNameToReplaced` | `string` | `'TComponent'` | The name of the component in translations that will be captured and replaced in tComponent. | | `namespacesToFetch` | `string[]` | `['common']` | All the namespaces you want to fetch during link Fetch Translations | | `linkFetchTranslations` | `(version: string, language: string, namespace: string) => string` | `undefined` | A function to download our translations **from the api**. It is called every time it wants to load a given translation in a given language and namespace. The function returns single values that we entered in the fields: `locales`, `namespaces`. To return, we need to return a link to our api, e.g. **return** `https://your-api-to-download-translations/${version}/${language}/${namespace}` Note: if you add NEXT_PUBLIC_NEXT_TRANSLATIONS_APP_ENV to your .env file then you can access the version of the page, e.g.: dev, int, prd etc.. | ## configuration **Attention!** To avoid a translation error when building a page, add getTranslationsProps to each page! If you don't have these pages, you may get messages about no translation! - /404 - /500 - /offline **example translations in /public/locales/en/common.json** ```bash { "section":{ "title": "Example title in your site!" } } ``` **/pages/\_app.tsx** for **STATIC SITES** and **SERVER SIDE RENDERING** ```bash import { initializeTranslations } from "next-translations/hooks"; import { InitializeRedirectsTranslations, validLink } from "next-translations/redirects"; import type { AppProps } from "next/app"; import { useEffect } from "react"; import { useRouter } from "next/router"; export default function App({ Component, pageProps }: AppProps) { // add this line initializeTranslations(pageProps?.translations); // add this lint if you want to use redirects: sitesForLoggedUser. // You can add this in user context for example. InitializeRedirectsTranslations({ isLoggedUser: true, // true or false for STATIC PAGES. Attention! if isLoggedUser is undefined then no redirects are performed! isLoggedUser: pageProps?.isLoggedUser || false, // for SERVER SIDE RENDERING pages. Attention! if isLoggedUser is undefined then no redirects are performed! enable: true, // default is true. Checks if routing can be done on the page. If it's false, it doesn't do routing. withQuery: true, // default is true. Listens for query during redirects withHash: true, // default is true. Listens for hash during redirects }); //example InitializeRedirectsTranslations({ isLoggedUser: !!user, enable: user !== undefined, withQuery: true, withHash: false, }); //checking routes and return valid route useEffect(() => { if (!router.isReady) { return; } let queryValue = ""; if (window.location.search.length > 0) { const splitQuery = window.location.search.split("?"); const getQueryValue = splitQuery.at(1); if (getQueryValue) { queryValue = getQueryValue; } } let hashValue = ""; if (window.location.hash.length > 0) { const splitHash = window.location.hash.split("#"); const getHashValue = splitHash.at(1); if (getHashValue) { hashValue = getHashValue; } } // works in the same way as page redirects. If the user does not have access to a given subpage, he will return a link to the subpage to which he has based on the config const result = validLink({ isLoggedUser: false, // checking if the user is logged in locale: "en", // the locale we want to change to. If is undefined, the locale is selected based on the currently used one path: "/user", // path where we want to generate the link. If is undefined, the locale is selected based on the currently used one router: router, // need to add router from nextjs query: queryValue, // query to your link. hash: hashValue, // hash to your link }); }, [router, user]); return ( <main> <Component {...pageProps} /> </main> ); } ``` **/pages/yourPath.tsx** - for **STATIC SITES** `Note: if you don't have defaultLocaleWithMultirouting defined, then you MUST keep content in /pages/[locale]/yourPath.tsx, otherwise you will only have the language that was set as defaultLocale!` ```bash import { getTranslationsProps } from "next-translations"; import { useTranslation } from "next-translations/hooks"; import { GetStaticProps } from "next"; function Home() { const { t, pageTranslations } = useTranslation("common"); // enter the given namespace that you use in the given section const { t, pageTranslations } = useTranslation("common:section"); // if you want you can also refer to namespace, along with nested elements - 1 example const { t, pageTranslations } = useTranslation("common.section"); // if you want you can also refer to namespace, along with nested elements - 2 example // t -> thanks to this function, you can download a given text/object/array at your discretion - just like you have downloaded/added in translations // pageTranslations -> all translations that are available on this subpage return ( <div> t("section.title") // downloading translation - without nested elements t("title") // downloading translation - if you using nested elements </div> ); } export const getStaticProps: GetStaticProps = async ctx => { const translationsProps = await getTranslationsProps(ctx, ["common"]); // add here all translations in string[] that you use on this subpage // you have access to: // ctx.locale - current locale // ctx.locales - all locales // ctx.defaultLocale - default locale return { props: { ...translationsProps, }, }; } export default Home; ``` **/pages/yourPath.tsx** - for **SERVER SIDE RENDERING** `Note: if you don't have defaultLocaleWithMultirouting defined, then you MUST keep content in /pages/[locale]/yourPath.tsx, otherwise you will only have the language that was set as defaultLocale!` ```bash import { getTranslationsPropsServer } from "next-translations/server"; import { useTranslation } from "next-translations/hooks"; import { GetServerSideProps } from "next"; function Home() { const { t, pageTranslations } = useTranslation("common"); // enter the given namespace that you use in the given section const { t, pageTranslations } = useTranslation("common:section"); // if you want you can also refer to namespace, along with nested elements - 1 example const { t, pageTranslations } = useTranslation("common.section"); // if you want you can also refer to namespace, along with nested elements - 2 example // t -> thanks to this function, you can download a given text/object/array at your discretion - just like you have downloaded/added in translations // pageTranslations -> all translations that are available on this subpage return ( <div> t("section.title") // downloading translation - without nested elements t("title") // downloading translation - if you using nested elements </div> ); } export const getServerSideProps: GetServerSideProps = async ctx => { let translatesProps = null; const translationsNamespaces = [namespaces.account]; if (process.env.NODE_ENV === "development") { translatesProps = await getTranslationsProps(ctx, translationsNamespaces); // add here all translations in string[] that you use on this subpage } else { translatesProps = await getTranslationsPropsServer( // add here all translations in string[] that you use on this subpage ctx, translationsNamespaces, ); } // you have access to: // ctx.locale - current locale // ctx.locales - all locales // ctx.defaultLocale - default locale return { props: { ...translatesProps, isLoggedUser: true, // or false, add this prop if you want to use redirects: sitesForLoggedUser }, }; }; export default Home; ``` **/pages/[locale]/yourPath.tsx** - for **STATIC SITES** ```bash import { getTranslationsProps, getStaticPaths, getPaths } from "next-translations"; // add getStaticPaths only if you using: export { getStaticPaths }, if you using getStaticPaths from next - dont add this import! import { useTranslation } from "next-translations/hooks"; import { GetStaticProps, GetStaticPaths } from "next"; function Home() { const { t, pageTranslations } = useTranslation("common"); // enter the given namespace that you use in the given section const { t, pageTranslations } = useTranslation("common:section"); // if you want you can also refer to namespace, along with nested elements - 1 example const { t, pageTranslations } = useTranslation("common.section"); // if you want you can also refer to namespace, along with nested elements - 2 example // t -> thanks to this function, you can download a given text/object/array at your discretion - just like you have downloaded/added in translations // pageTranslations -> all translations that are available on this subpage return ( <div> t("section.title") // downloading translation - without nested elements t("title") // downloading translation - if you using nested elements </div> ); } export const getStaticProps: GetStaticProps = async ctx => { const translationsProps = await getTranslationsProps(ctx, ["common"]); // add here all translations in string[] that you use on this subpage // you have access to: // ctx.locale - current locale // ctx.locales - all locales // ctx.defaultLocale - default locale return { props: { ...translationsProps, }, }; } export { getStaticPaths }; // IMPORTANT ADD THIS LINE TO ENABLE MULTI ROUTING //ALTERNATIVE with Nextjs getStaticPaths - if you using this, don't impoty getStaticPaths from next-translations export const getStaticPaths: GetStaticPaths = async () => { // IMPORTANT ADD THIS LINE TO ENABLE MULTI ROUTING (alternative) return { fallback: false, paths: getPaths(), }; } export default Home; ``` **/pages/[locale]/yourPath.tsx** - for **SERVER SIDE RENDERING** `Attention! You manage the site's languages via slug! eg: /en/home - page with en language, /pl/home - page with pl language` ```bash import { getTranslationsPropsServer } from "next-translations/server"; import { useTranslation } from "next-translations/hooks"; import { GetServerSideProps } from "next"; function Home() { const { t, pageTranslations } = useTranslation("common"); // enter the given namespace that you use in the given section const { t, pageTranslations } = useTranslation("common", true); // translations without log errors const { t, pageTranslations } = useTranslation("common:section"); // if you want you can also refer to namespace, along with nested elements - 1 example const { t, pageTranslations } = useTranslation("common.section"); // if you want you can also refer to namespace, along with nested elements - 2 example // t -> thanks to this function, you can download a given text/object/array at your discretion - just like you have downloaded/added in translations // pageTranslations -> all translations that are available on this subpage return ( <div> t("section.title") // downloading translation - without nested elements t("title") // downloading translation - if you using nested elements </div> ); } export const getServerSideProps: GetServerSideProps = async ctx => { let translatesProps = null; const translationsNamespaces = [namespaces.account]; if (process.env.NODE_ENV === "development") { translatesProps = await getTranslationsProps(ctx, translationsNamespaces); // add here all translations in string[] that you use on this subpage } else { translatesProps = await getTranslationsPropsServer( // add here all translations in string[] that you use on this subpage ctx, translationsNamespaces, ); } // you have access to: // ctx.locale - current locale // ctx.locales - all locales // ctx.defaultLocale - default locale return { props: { ...translatesProps, isLoggedUser: true, // or false, add this prop if you want to use redirects: sitesForLoggedUser }, }; }; export default Home; ``` **useTranslation - all functions** ```bash const { t, tString, tNumber, tArray, tObject, tComponent, pageTranslations } = useTranslation("common"); // enter the given namespace that you use in the given section const { t, pageTranslations } = useTranslation("common:section"); // if you want you can also refer to namespace, along with nested elements - 1 example const { t, pageTranslations } = useTranslation("common.section"); // if you want you can also refer to namespace, along with nested elements - 2 example pageTranslations // all translations that are available on this subpage t("section.text1"); // if there is a translation, it returns it as any, if not, it returns undefined tString("section.text2"); // if there is a translation and it has a string type, it returns it as string, if it doesn't find it, or it has the wrong type, it returns undefined. tNumber("section.text3"); // if there is a translation and it has a number type, it returns it as number, if it doesn't find it, or it has the wrong type, it returns undefined. tArray("section.text4"); // if there is a translation and it has a any[] type, it returns it as any[], if it doesn't find it, or it has the wrong type, it returns undefined. tObject("section.text5"); // if there is a translation and it has a object type, it returns it as object, if it doesn't find it, or it has the wrong type, it returns undefined. //if there is a translation and it has type string, if it doesn't find it or it has wrong type it returns undefined. If it contains <TComponent> value </TComponent> or <TComponent/>, you can create your own component based on the values returned from the callback. **Note** the text inside <TComponent> must be separated by a space between <TComponent> and </TComponent>!!!!! Example of correct implementation: { "title": "example paragraph <TComponent> xxxx sad </TComponent> paragraph" } tComponent( "section.textLink", ({ textBefore, textComponent, textAfter, text }) => { // value from callback to create your own component. text is returned when it doesn't find a TComponent inside the text return ( <div> <p>{textBefore}</p> <Link href="/">{textComponent}</Link> <p>{textAfter}</p> </div> ); }, ); ``` **Avaible types** ```bash // import type {T_t, T_tString, T_tNumber, T_tArray, T_tObject, T_tComponent} from "next-translations/hooks" - T_t - T_tString - T_tNumber - T_tArray - T_tObject - T_tComponent ``` **package.json** ```bash "scripts": { "getTranslations": "node node_modules/next-translations/getTranslations", // script to fetch all translations from your api **linkFetchTranslations** "dev": "npm run getTranslations && next dev", "build": "npm run getTranslations && next build", } ```