GithubHelp home page GithubHelp logo

next-export-i18n's Introduction

next-export-i18n v3 written over a night scene of a New York City'S street covered in multi-language neon signs

next-export-i18n v3 Build Status

TL;DR: This npm module provides a simple, reactive client-side javascript solution for project internationalization (i18n) using next export/config: export for Next.js' app router.

Use v3 with the app router

Version 3 of the module supports the app router exclusively. Because next-export-i18n works with actual static exported pages, it operates exclusively on the client side. Therefore, you can only use it in client components. You must add the "use client" directive on top of the file.

Install it on your command line by running npm install --save-dev next-export-i18n.

If you're migrating from the pages directory to the approuter, look at the `migration guide.

Use v2 with the pages directory

To use next-export-i18n with Next.js' traditional pages directory, use the latest version of the major version 2. We'll update this branch with new features. To install it, use the semver range ^2. The complete npm command would be: npm install --save-dev next-export-i18n@^2

Quick Start

To add the next-export-i18n, follow a few basic steps.

  1. Run yarn add next-export-i18n or npm install next-export-i18n.
  2. Create a top-level-folder i18n and Add your JSON translation files similar to the listings below
{
  "headline": "City: Paris",
  "link": {
    "text": "Top 10 Bistros in Paris ",
    "href": "/paris-bistros"
  }
}

File: ./i18n/translations.en.json

{
  "headline": "Stadt: Paris",
  "link": {
    "text": "Die 10 besten Bistros in Paris ",
    "href": "/paris-bistros"
  }
}

File: ./i18n/translations.de.json

  1. Create the i18n/index.js, and use require to add your translation files. Use the configuration object to customize next-export-i18n to your liking.
var en = require("./translations.en.json");
var de = require("./translations.de.json");

const i18n = {
  translations: {
    en,
    de,
  },
  defaultLang: "en",
  useBrowserDefault: true,
  // optional property will default to "query" if not set
  languageDataStore: "query" || "localStorage",
};

module.exports = i18n;

File: ./i18n/index.js

  1. Add the translation hook useTranslation and the LanguageSwitchercomponent to your code. Then, add the reactive translations using the t() function. Next-export-i18n updates them automatically immediately when you change the language through the LanguageSwitcher component.

If you choose the query param (the default method) to store your language selection, remember that every internal link requires the search parameter lang on the href' attribute. By adding this, we tell the destination which language to render. The linked page would fall back to the default language without the search parameter. To simplify this, you can use the LinkWithLocalecomponent which automatically adds thelang-parameter to each link while preserving all search parameters you've added to the URL (see ?share=social` in the example listing).

Look at the listing below for an example implementation.

"use client"

import {
  useTranslation, 
  LanguageSwitcher, 
  LinkWithLocale
} from "next-export-i18n";

export default function Component({}) {
  const { t } = useTranslation();
  
  return (
    <div>
    <header>
      <nav>
        <LanguageSwitcher lang="de">Deutsch</LanguageSwitcher>
        <LanguageSwitcher lang="en">English</LanguageSwitcher>
      </nav>
      </header>
      <main>
      <h1>t('headline')</h1>
      <LinkWithLocale href={t("link.href")}>
        {t("link.text")}
      </LinkWithLocale>
    </main>
    </div>
  );

File: ./component.js

The Usecase for next-export-i18n

Since v10.0.0 Next.js already has support for internationalized (i18n) routing out-of-the-box. You can provide a list of locales, a default and domain-specific locales, and Next.js automatically handles the routing. It streamlines the touring and locale parsing for nearly all existing l18n library solutions available for Next.js such as react-intl, react-i18next, lingui, rosetta, next-intl.

Unfortunately, Next.js i18n-routing does not supports next export.

Note that Internationalized Routing does not integrate with next export as next export does not leverage the Next.js routing layer. Hybrid Next.js applications that do not use next export are fully supported.

This means that none of the i18n-libraries (utilizing the built-in i18n-routing) can support fully static sites generated with next export.

Wait, what is happening here? They explicitly mention support for server-side rendering!

react-i18next is optimally suited for server-side rendering

https://react.i18next.com

To complement this, next-i18next provides the remaining functionality – management of translation content and components/hooks to translate your React components – while fully supporting SSG/SSR, multiple namespaces, code-splitting, etc.

https://github.com/isaachinman/next-i18next

They all support pre-rendered sites which are served with Next.js - whereas next export creates a truly static page which can be served with any webserver (e.g. nginx, apache, etc.).

For the different types of pre-rendering in Next.js, take a look at my article The two and a half + one flavours of Next.js's pre-rendering , which explains and summarizes the different options.

next-export-i18n overview

With next-export-i18n, you can add true reactive client-side internationalization to your static-generated projects.

Documentation

You can configure next-export-i18n to match the needs of your project.

The useTranslation hook

The interface for the i18n-content is similar to react-i18next/next-i18next; identical to them, we add the translated content through the t(key.to.translation) function that we receive from the useTranslation-hook.

Let's look at a simple example:

"use client"
import {useTranslation} from "next-export-i18n";

export default function Component({}) {
  const { t } = useTranslation();
  const translatedHeadline = t('headline');
  return (
    <h1>{translatedHeadline}</h1>
  );
}

File: component.js

Translation Files

You must provide a JSON file in the ./i18n subfolder for each language. Below is an example listing of how they could look like.

{
  "headline": "City: Paris",
  "link": {
    "text": "Top 10 Bistros in Paris ",
    "href": "/paris-bistros"
  } 
}

_File: ./i18n/translations.en.json

If you prefer a more readable format, you can use yaml files and convert them to the required JSON format during the build step. A common library for that would be yamljs.

Please remember not to use dots in your JSON property names. The module uses the dots to determine link keys; for example: t("link.headline") refers to the translated content string "City: Paris"

The module renders the key back to the site in case the key is not part of the language files to indicate and remind you of missing translations.

The Configuration File i18n/index.js

Let's look at an example configuration file i18n/index.js.

// First, we load all translation files.
var en = require("./translations.en.json");
var de = require("./translations.de.json");

// Then we need to set up our configuration;
// Here, we add the translations under the particular 
// language key and then configuring the module.
const i18n = {
  translations: {
    en: en.i18n,
    de: de.i18n,
  },
  defaultLang: "de",
  languageDataStore: "localStorage",
  useBrowserDefault: true,
};

File: ./i18n/index.js

The Configuration Options

Next-export-i18n has only a few important configuration options. Let's look at them in detail.

defaultLang

A string, for Example: "en" We use the defaultLang property to set the default language. Remember, this language key needs to be available in your translation configuration.

languageDataStore

Either "localStorage" or "query" With the configuration property languageDataStore, you tell next-export-i18n to either add a query parameter (default) lang to your URLs or store the selected language in the browser's localStorage.

useBrowserDefault

Either true or false If you use true, we use the browser's language instead of the configuration's defaultLang to determine the default language setting. Remember that next-export-i18n considers only the primary subtag, e.g., en-US from the will be read as en and will use the translations you added under ènin the i18n/index.js`file.

The LinkWithLocale Component

When you use the query param (default) to store your language selection, every internal link requires the search parameter lang on the `href' attribute. Otherwise, the destination will not show the content in the selected language; instead, the application will fall back to the default language.

You can use the LinkWithLocale component to automatically add the lang-parameter to each link while preserving all search parameters you've added to the URL (see ?share=social in the example listing). Look at the listing below for an example implementation.

"use client"

import {LinkWithLocale} from 'next-export-i18n';

export default function Component({ }) {
  return (
    <LinkWithLocale href="/paris-sights?share=social">
      Link to /paris-sights
    </LinkWithLocale>
  );
}

File: component.js

The LanguageSwitcher Component

Next-export-i18n provides a convenient out-of-the-box to switch between the available languages. It preserves an existing search parameter on the current URL, and you can use the [data-language-switcher] and [data-is-current="true"] to style the component.

Look at the listing below for an example implementation.

"use client"

import {LanguageSwitcher} from 'next-export-i18n';

export default function Component({ }) {
  return (
    <nav>
      <LanguageSwitcher lang="de">Deutsch</LanguageSwitcher>
      <LanguageSwitcher lang="en">English</LanguageSwitcher>
    </nav>
  );
}

File: component.js

Working With Template Strings

Let's say we want to display a username or set the number of top locations depending on the number of locations we receive from an API. For those kinds of dynamic text, you can add a moustache template in the translation.json strings and update them dynamically.

Let's look at an example implementation where we replace the fixed number 10 in the string Top 10 Bistros in Paris with a dynamic number.

{
  "headline": "City: Paris",
  "link": {
    "text": "Top {{count}} Bistros in Paris ",
    "href": "/paris-bistros"
  }
}

File: translation.json

"use client"
import {useTranslation} from "next-export-i18n";

export default function Component({}) {
  const { t } = useTranslation();
  const numberOfItems = 10;
  const translatedContent = t('link.text', { count: numberOfItems }))
  // translatedContent will be "Top 10 Bistros in Paris"
  return (
    <h1>{translatedContent}</h1>
  );
}

File: component.js

Example page

We have an example implementation at next-export-i18n-example.vercel.app and its source code at github: https://github.com/martinkr/next-export-i18n-example to showcase next-export-i18n and to give you an example of how to use the module.

Statically Exporting Your Project

Next.js v14.0.0 replaces the next export command with the configuration setting "output": "export". Add this to the next.config.js in your application's root directory. The listing below shows a stripped down minimal example.

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export'
}

File: ./next.config.js Run the export command below:

npm run build
# or
yarn build

Then, you can use npx serve ./out to see your exported project in your web browser or deploy the ./out directory to your existing web server.

Tech Stack

  • next.js: >= 13.0.0
  • react.js: >=18.0.0
  • jest: ^27.5.1
  • typescript: ^4.9.5

License

It is licensed under the MIT license. MIT - http://www.opensource.org/licenses/mit-license.php

next-export-i18n's People

Contributors

chhunneng avatar chrblabla avatar christoph-bittmann avatar cyntler avatar ecolman avatar hexi1997 avatar jimmyrleung avatar kigawas avatar m-ermolaev avatar martinkr avatar renatoruk avatar vincentrohde avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

next-export-i18n's Issues

autodetection build error

Hi

Many thanks for creating this!

I have an issue with on CI build since v1.3 I think related to auto-detection. any ideas?

11:45:41.442 | ERR! \|     let browserLang = "";
-- | --
11:45:41.442 | ERR! \|     if (typeof navigator !== "undefined") {
11:45:41.442 | ERR! >         browserLang = ((navigator?.languages && navigator?.languages[0]) \|\|
11:45:41.442 | ERR! \|             navigator?.language)
11:45:41.442 | ERR! \|             .split("-")[0]

How can next-export-i18n detect language based on domain?

How can I detect browser language based on a specific domain url ? Something likes this:

module.exports = {
  i18n: {
    localeDetection: false,
    locales: ['en', 'ja', 'vi'],
    defaultLocale: 'en',
    localePath: './locales',
    domains: [
      {
        domain: 'website.net',
        defaultLocale: 'en',
        http: true
      },
      {
        domain: 'website.jp',
        defaultLocale: 'ja',
        http: true
      }
    ]
  }
}

Please support me to resolve this. Thank you for your help

Support SSG

As far as I can tell this library works entirely client side, which is a bad thing if someone is visiting and has JavaScript disabled. This is also bad because it means their initial render will be in a foreign language (I am exporting statically). It would be nice if there was an option to render to different subroutes instead like /en/ or /fr/

I'm not sure if this is already possible but it's been confusing trying to figure out how to do this on next.js in general

Testing with Jest

Whenever testing components tax with Jest, it fails because it cannot use the useRouter hook... is there a proper way to mock it or configure the package properly with jest?

So far, assuming that TestComponent does contain only a Component that shows a translated text, what should I do

import { render } from '@testing-library/react';

import TestComponent from '.';


describe('<TestComponent />', () => {
  it('should render the heading', () => {
    const { container } = render(<TestComponent />);
    expect(container).toBeDefined();
  });
});

As of now it fails with the following error:

Error: Uncaught [Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted]

Thanks in advance.

Support for defaultVariables

Is there a way to specify defaultVariables in the configuration?

interpolation: {
defaultVariables: {price: 100, product_name:"The product"}
}

So that they will be substituted for all values in the language file list so:

{
"product": {
"title":"{{product_name}} is the best",
"description":"The {{product_name}} is know for efficiency."
}
}

t('title') would still work without providing product name

and not have to do this:

t('title', {product_name: "The Product"})

Dual license at npm

Hi!

In the github repository, your library is licensed under MIT, but in npm your project is licensed under a dual MIT and GNU 3.0 license.

Please, check the npm repository README.

how to change "dir" in <body> element in the "_document" file

hey,
i am trying to change the direction of the html based on the changing language.
in the _document.js file i have placed this:

import useLanguageQuery from "next-export-i18n";
const [query] = useLanguageQuery();

but then i get:

1 of 1 unhandled error
Server Error
TypeError: next_export_i18n__WEBPACK_IMPORTED_MODULE_2___default is not iterable (cannot read property Symbol(Symbol.iterator))

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source

pages/_document.js (3:16) @ eval

  1 | import Document, { Html, Head, Main, NextScript } from 'next/document';
  2 | import useLanguageQuery from "next-export-i18n";
> 3 | const [query] = useLanguageQuery();
    |                ^
  4 | 
  5 | 
  6 | class CustomDocument extends Document {

do i miss something?

using html tags in nested translation

Is there a way to use HTML tags in nested translation keys?
We used to work with another i18n package and it had the feature to put HTML tags in the nested translations, like this:

translation.json =>
"nameTitle": "This is your name",
"userMessagesUnread_one": "Hello <1>{{name}}</1>, you have {{count}} unread message. <5>Go to message</5>.",
"userMessagesUnread_other": "Hello <1>{{name}}</1>, you have {{count}} unread messages. <5>Go to messages</5>.",

file.tsx =>
`import { Trans } from 'react-i18next';

function MyComponent({ person, messages }) {
const { name } = person;
const count = messages.length;

return (

Hello <strong title={t('nameTitle')}>{{name}}, you have {{count}} unread message. Go to messages.

);
}`

Not compatible with PNPM

I'm encountering this issue when switching from yarn to pnpm:

https://nextjs.org/docs/messages/module-not-found
wait  - compiling...
error - ./node_modules/.pnpm/[email protected]_c0abbb53577d424fe320649841fdc7d4/node_modules/next-export-i18n/index.js:5:0
Module not found: Can't resolve './../../i18n/index.js'

By reading the source code, the issue is that next-export-i18n assumed it would always be located as <project_folder>/node_modules/next-export-i18n. This is no longer true with pnpm, as it utilized the hard link feature so next-export-18n would be in another location.

I'm still investigating pnpm whether there is solution to make certain package as an exception. No clue yet...

Multiple nested translation files support

Thanks for putting this package together!

The readme seems to assume that translation files will be flat and singular. I'm wondering how I could support nested translation files like:

/locales
    /subfolder1
        en.yaml
        de.yaml
    /subfolder2
        en.yaml
        de.yaml

I've considered recursively sorting and combining them before adding each locale to the i18n.translations object, but I'd like to retain some sort of granular control when using the translations with, as mentioned in another issue, setting a default namespace in const { t } = useTranslation('subfolder1'); to the subfolder, and then getting the text with t('keys.to.text').

Is this something that can be supported? Thanks!

Translation fails silently when the key contains a dot '.' character

Hey,
firstly thanks so much for this package, saved me a headache.

As to the issue i can elaborate, add tests, reproducement scenarios and contribute later, but I want to point out that it seems to be when i tried using your package for some longer texts having them as keys, like full sentences, whenver there was a . in the key it would fail.

Example:

index.ts:

<p>{t('A long sentence with some words, divided with a comma')}</p>

<p>{t('A long sentence with some words. Divided with a dot')}</p>

gb.json:

{
  "A long sentence with some words, divided with a comma": "Comma translation",
  "A long sentence with some words. Divided with a dot": "Dot translation"
}

Outputs:

Comma translation
A long sentence with some words. Divided with a dot

While I expected:

Comma translation
Dot translation

The problem is especially concerning that there is no error message, not in the console, not anywhere about not finding the key, or that the key in the JSON contains what I might assume are disallowed characters. I also guess that the dot is reserved for drilling down the objects in the JSON, right? I didn't find that mentioned in the documentation and it left me confused.

Could not resolve dependency for React 18.2.0

Trying to use this package with the latest version of React (18.2.0) but cannot successfully run npm install.

package.json

"next-export-i18n": "^2.0.3", 
"react": "18.2.0", 
"react-dom": "18.2.0", 

This is the error output in npm:

npm ERR! Could not resolve dependency:  
npm ERR! peer react@"18.0.0" from [email protected]  
npm ERR! node_modules/next-export-i18n
npm ERR!   next-export-i18n@"^2.0.3" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/react
npm ERR!   peer react@"18.0.0" from [email protected]
npm ERR!   node_modules/next-export-i18n
npm ERR!     next-export-i18n@"^2.0.3" from the root project

Looks like this is an issue with the dependencies of the project, but it's completely stopping the ability to run a simple npm install without --force .

[Question] How to share the translation file with translators

Thank you for the great library.

I have a question about how to share the translation file with translators.
For example, export the translation file to a CSV file and share it with translators in Google Sheets, or using a localization management tool ( e.g. accent ) and so on....

Do you have any suggestions on how to do this?

TypeError: Cannot convert undefined or null to object

Received this error while trying to implement the library, any idea?

TypeError: Cannot convert undefined or null to object

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
pages/index.tsx (12:30) @ Home

  10 | 
  11 | const Home: NextPage = () => {
> 12 |   const { t } = useTranslation();
     |                              ^

Is there a way to get the translation from method param and force the translation in that language ?

something like this

const useTranslation = (language?: string) => {
	const router = useRouter();
	let i18nObj: I18N;

	i18nObj = i18n() as I18N;

	const translations: Dictionary = i18nObj.translations;
	const defaultLang: string = i18nObj.defaultLang;
	const lang = language || useSelectedLanguage().lang || defaultLang;

The method also needs to wrapped inside a callback as it is not taking updated useSelectedLanguage.

import { useRouter } from "next/router";
import { useCallback } from "react";
import i18n from "./../index";
import { Dictionary, I18N } from "../types";
import useSelectedLanguage from "./use-selected-language";
import Mustache from "mustache";

/**
 * Provides the t() function which returns the value stored for this given key (e.g. "i18n.ui.headline")
 * in the translation file.
 * The return value can be a string, a number, an array or an object.
 * In case there is no entry for this key, it returns the key.
 * @returns t(key: string): any function
 */
const useTranslation = (lang?: string) => {
  const router = useRouter();
  let i18nObj: I18N;

  i18nObj = i18n() as I18N;

  const translations: Dictionary = i18nObj.translations;
  const defaultLang: string = i18nObj.defaultLang;
  const language = lang || useSelectedLanguage().lang || defaultLang;

  /**
   * Returns the value stored for this given key (e.g. "i18n.ui.headline")  in the translation file.
   * The return value can be a string, a number, an array or an object.
   * In case there is no entry for this key, it returns the key.
   * @param key the key for looking up the translation
   * @param view the mustache view for interpolating the template string
   * @returns the value stored for this key, could be a string, a number, an array or an object
   */
  const translation = useCallback(
    (key: string, view?: object): any => {
      let value: any = key
        .split(".")
        .reduce(
          (previous: any, current: string) =>
            (previous && previous[current]) || null,
          translations[language]
        );
      let translation: any = value || key;
      try {
        return Mustache.render(translation, view);
      } catch (e) {
        return translation;
      }
    },
    [language]
  );

  return {
    t: translation,
  };
};

export { useTranslation };

Storybook example

Hey,

When using this package alongside Storybook I get the following error:

TypeError: Cannot read properties of null (reading 'query')
    at useSelectedLanguage

The component loaded into my story uses the useSelectedLanguage hook. I get the same error using the useTranslation hook and the useSelectedLanguage hook.

I'm assuming there is an extra step needed to wrap storybook in the translation context. Have you tried this / experienced the same?

Any help would be really appreciated

Support for Trans Component

Trans component from i18next allows listing the custom component.

This would be really useful and facilitate migrating from next-translate or react-i18next to next-export-i18n.

Example:

<Trans
  i18nKey="description"
  components={{
    privacyPolicyLink: (
      <PrivacyLink lang={lang} type={'privacy-policy'} />
    ),
    generalTermsLink: (
      <PrivacyLink lang={lang} type={'general-terms'} />
    ),
    cookiePolicyLink: (
      <PrivacyLink lang={lang} type={'cookie-policy'} />
    )
  }}
/>

Cannot read properties of null (reading 'query') when testing with Jest

Title is self explanatory, I receive this error when testing the file.

Relevant code from: Notification.js (component being tested)

import React from "react";
import {Box, Button, Card, CardActions, CardContent, List, ListItem, Typography, FormControlLabel, ButtonGroup,} from "@mui/material";
import SwitchIos from "./SwitchIos";
import { useTranslation } from "next-export-i18n";
import Image from "next/image";
import { useEffect, useState } from "react";
import nookies from "nookies";
import axios from "axios";
import ApiRunner from "../../plugins/api";
import { useAuthentication } from "../../hooks/useAuthentication";

let summaryNotifs = [{ id: "day" }, { id: "week" }, { id: "month" }];

const styleSummarySelectButton = { backgroundColor: "gray", color: "white" };
const styleSummaryDefault = { color: "black" };

const Notification = (props) => {
  const { summaryType, setSummaryType } = props;
  const { t } = useTranslation();

Relevant code from: notification.test.js

  import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/react'
import Notification from '../../../components/user/Notification'

describe('Page renders', () => {
    it('renders', () => {
        render(<Notification />)
    })
})

describe('User sets notification preferences', () => {

    it('renders notification, then clicks one of the switches', async () => {
        render(<Notification />)

        await userEvent.click(screen.getByTestId('postScheduled'))

        expect(screen.getByTestId('postScheduled')).toBeTruthy()
    })
})

Full text of error in console:

● User sets notification preferences › renders notification, then clicks one of the switches

    TypeError: Cannot read properties of null (reading 'query')

      17 | const Notification = (props) => {
      18 |   const { summaryType, setSummaryType } = props;
    > 19 |   const { t } = useTranslation();
         |                               ^
      20 |   // console.log(t, useTranslation(''))
      21 |   const userData = useAuthentication();
      22 |   const [notifSettings, setNotifSettings] = useState({});

      at useSelectedLanguage (node_modules/next-export-i18n/index.js:80:24)
      at useTranslation (node_modules/next-export-i18n/index.js:155:22)
      at Notification (components/user/Notification.js:19:31)
      at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18)
      at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17811:13)
      at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16)
      at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14)
      at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:340:25)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:276:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:223:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34)
      at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:3994:16)
      at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:4056:31)
      at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:23964:7)
      at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22779:12)
      at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22707:5)
      at renderRootSync (node_modules/react-dom/cjs/react-dom.development.js:22670:7)
      at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:22293:18)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21881:7)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:25482:3)
      at node_modules/react-dom/cjs/react-dom.development.js:26021:7
      at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:22431:12)
      at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:26020:5)
      at Object.render (node_modules/react-dom/cjs/react-dom.development.js:26103:10)
      at node_modules/@testing-library/react/dist/pure.js:101:25
      at batchedUpdates$1 (node_modules/react-dom/cjs/react-dom.development.js:22380:12)
      at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:1042:14)
      at render (node_modules/@testing-library/react/dist/pure.js:97:26)
      at Object.<anonymous> (__tests__/components/user/information.test.js:43:15)
  I've tried playing around with some of the solutions suggested over at the i18next/react-i18next github but got nowhere with it. Console logs of useTranslation and {t} both return function t(key, view). Thanks for any help/suggestions.

Set default namespace when calling useTranslation

I'm writing my nested JSON strings to organize better and keep keys shorter.
The problem is that every time I use the translations, I have to write the full path of the nested key

const {t} = useTranslation();

const title = t("hero.title");
const description = t("hero.description")

My proposal: add optional parameter to useTranslation that lets you define a prefix path to all the keys you use

Example:

const {t} = useTranslation("hero");

const title = t("title");
const description = t("description")

How to handle hydration errors? SEO?

Hi,

when useBrowserDefault is set to true and the users browser language is different as set in defaultLang there will be fired hydrations errors ("Text content does not match server-rendered HTML."). This is expected as the client site renders a different language than generated on the server.

You can reproduce the issue on the example page (https://next-export-i18n-example.vercel.app/) if you change the browser language to something other than german (as here defaultLang is set to de).

Bildschirmfoto 2022-11-11 um 15 17 33

This is my first static next.js project so I'm wondering how to handle this?

I'm also wondering how SEO works for languages that are not set as defaultLang as this languages are not created as static files.

Support for html inside text

First of all, I just wanted to say thanks for putting this together!

I think it would be great if there was support for html inside text. In other words something like the Trans component in next-translate.

My use case is a long paragraph with different styling for different parts of the text. The alternative would be to just have a bunch entries in my .json translations file, instead of one for the whole paragraph.

Template string support

Thanks for your work. This library is a lifesaver for building static next sites, but it lacks template string support, which is quite useful in many cases.

Users would probably want to translate like:

t('key', {count: 2})
{
   "key": "You clicked {{count}} times"
}

How about implement this feature by integrating mustache?

Initial Build Translations

Hi, great work on this package. I have a question (not an issue). I initially thought that I would have to call my translation files in the getStaticProps() function so that data would be compiled on build. But it seems that isn't necessary.

Are the initial static html files (created during next build) referencing whatever translations are set in the defaultLang json file?

404 on reload

I'm having some trouble trying to fix a problem with exported sites, if you reload a page, you get a 404 from the server. Maybe I missed something during the development?

EDIT2: Removed the hydration issue, solved it on production, just trying to figure out the 404 on exported sites.

Thanks in advance!

Syntax Error: Unexpected Token '.' when deploying on DigitalOcean

I'm trying to deploy on DigitalOcean as a static site.
Locally, the npm run export works fine.

But the build on DO fails with the following error:

/workspace/node_modules/next-export-i18n/index.js:26
        window?.navigator) {
                    ^

 SyntaxError: Unexpected token '.'
     at wrapSafe (internal/modules/cjs/loader.js:915:16)
    at Module._compile (internal/modules/cjs/loader.js:963:27)
     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.341 (/workspace/.next/server/pages/index.js:163:51)
    at __webpack_require__ (/workspace/.next/server/webpack-runtime.js:25:42)
    at __webpack_exec__ (/workspace/.next/server/pages/index.js:307:39) {
   type: 'SyntaxError'
 }

The app was created with npx create-next-app -ts
I'm using versions:

    "next": "12.0.10",
    "next-export-i18n": "^1.4.1",

Do you have an idea on what is causing the error?

Invaild default language 'en'.

Hello, thank you for this library. However, I keep getting Invalid default language 'en1 error message. My index.js in i18n folder is

  const i18n = {
   translations: {
    en: en.i18n
   },
  defaultLang: "en"
}

Please, help

Language autodétection

Hello, looking at the code and implementing it, and I wonder if there is a language autodetection feature because it always takes my defaultLang as language despite the French language on the browser for example.
Thanks

Error: Invalid default language 'en'. Check your 'defaultLang' in 'i18n/index.js'?

Hello, I just implement this package in my project and I got an unexpected error.

Error: Invalid default language 'en'. Check your 'defaultLang' in 'i18n/index.js'?

From

const { t } = useTranslation();

Here is my i18n/index.js

var en = require("./translations.en.json");
var km = require("./translations.km.json");
var zh = require("./translations.zh.json");

const i18n = {
  translations: {
    en: en.i18n,
    km: km.i18n,
    "zh-Hans": zh.i18n,
  },
  defaultLang: "en",
  useBrowserDefault: true,
};

module.exports = i18n;

Make Language autodétection optional

Thanks for the explanation! I understand how it works, but what happens if:

  1. I provide translations for English and Balinese (just for the sake of this example).
  2. The website is mostly targeted towards people who speak Balinese, so I set the default language to Balinese.
  3. There's no Balinese language support in most browsers, so users are likely to use another language for the browser (e.g. English).
  4. Now the default language of the website for the users becomes English, which is not what I'm aiming for.

This also happens if the user just happens to be using the browser in English (e.g. the browser comes preinstalled with the laptop, and it's set to English) and they're not tech-savvy enough to know how to change it.

Originally posted by @laymonage in #9 (comment)

Get the default languages in getStaticProps

How can I get the default languages in getStaticProps with next-export-i18n like this:

export const getStaticProps: GetStaticProps = async context => {
  console.log('context: ', context)
  if (locale === 'de') {
     return {
      notFound: true
    }
  }

  return {
    props: {}
  }
}

Currently the context return undefined. My problem is that when a page has a default lang is de then it should be redirect to 404 page
Please help me to help this issue. Thank you.

Hiding Default-Language Query

I would really like an option to remove the default language key from the query string - so that the language parameter will only be set if the language differs from the default selection.

Fallback language options is missing

When translation value is missing from an additional language, key is returned by the useTranslation hook. It's better to add an additional check for a fallback language and let the useTranslation return fallback value instead.

This can be done with the following modification:

const useTranslation = () => {
    console.log('using translation')
    router.useRouter();
    let i18nObj;
    i18nObj = i18n();
    const translations = i18nObj.translations;
    **// Add the following constant (gets value from i18nObj)**
    const fallbackLng = i18nObj.fallbackLng;
    i18nObj.defaultLang;
    const { lang } = useSelectedLanguage();
    // const [lang] = useSelectedLanguage();
    **// Add the following helper function to reuse the functionality
    /**
     * Helper function to return stored values from translation file
     */
    const getLanguageValue = (key, lang) => {
        return key.split('.').reduce((previous, current) => (previous && previous[current]) || null, translations[lang]);
    }**

    return {
        /**
         * Returns the value stored for this given key (e.g. "i18n.ui.headline")  in the translation file.
         * The return value can be a string, a number, an array or an object.
         * In case there is no entry for this key, it returns the key.
         * @param key the key for looking up the translation
         * @param view the mustache view for interpolating the template string
         * @returns the value stored for this key, could be a string, a number, an array or an object
         */
        t: (key, view) => {
            **// Conditionally assign value to translation variable
            let translation = getLanguageValue(key, lang) || getLanguageValue(key, fallbackLng) || key;**
            try {
                return Mustache__default["default"].render(translation, view);
            }
            catch (e) {
                return translation;
            }
        },
    };
};

Support per-page JSON files

It would be really nifty if there was a mechanism to import the translations on a per-page basis. I would like to have one translation file associated with each page rather than one giant translation file for each language.

Invalid default language 'en'

Hi Martin
Does it work with Next 12, because I have just got this error? I don't know my mistake but all I did was exactly the same as the steps you mentioned in the library.
Capture

Manually changing the lang query param causes no translations

If we have the config like that:

const en = require('./translations.en.json');
const pl = require('./translations.pl.json');

const translations = {
  en,
  pl,
};

const defaultLang = Object.keys(translations)[0];

module.exports = {
  translations,
  defaultLang,
};

and then when we add a query param lang with a value of i.e. test (?lang=test) then we get a problem with the translation. It means that useTranslation returns the key instead of translation of default lang.

Module not found?

info  - Creating an optimized production build
Failed to compile.

./node_modules/next-export-i18n/index.js
Module not found: Can't resolve './../../i18n/index.js' in 'C:\Users\user\Desktop\folder\node_modules\next-export-i18n'

Import trace for requested module:
./pages/gallery.js


> Build failed because of webpack errors

edit: needed to create the i18n folder

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.