From 319c21e305014a54cdd19f393fd7756026e982fa Mon Sep 17 00:00:00 2001
From: Adriano Raiano
Date: Mon, 19 Dec 2022 12:48:51 +0100
Subject: [PATCH] first commit
---
.eslintrc.json | 6 +
.gitignore | 5 +
@types/i18next.d.ts | 30 +
README.md | 14 +
app/[lng]/client-page/head.tsx | 15 +
app/[lng]/client-page/page.tsx | 37 +
app/[lng]/components/Footer/FooterBase.tsx | 46 +
app/[lng]/components/Footer/client.tsx | 9 +
app/[lng]/components/Footer/index.tsx | 10 +
app/[lng]/components/Header.tsx | 12 +
app/[lng]/global.css | 99 +
app/[lng]/head.tsx | 17 +
app/[lng]/layout.tsx | 24 +
app/[lng]/page.tsx | 53 +
app/[lng]/second-client-page/head.tsx | 15 +
app/[lng]/second-client-page/page.tsx | 27 +
app/[lng]/second-page/head.tsx | 15 +
app/[lng]/second-page/page.tsx | 28 +
app/i18n/client.ts | 34 +
app/i18n/index.ts | 31 +
app/i18n/locales/de/client-page.json | 9 +
app/i18n/locales/de/footer.json | 5 +
app/i18n/locales/de/second-client-page.json | 5 +
app/i18n/locales/de/second-page.json | 5 +
app/i18n/locales/de/translation.json | 11 +
app/i18n/locales/en/client-page.json | 9 +
app/i18n/locales/en/footer.json | 5 +
app/i18n/locales/en/second-client-page.json | 5 +
app/i18n/locales/en/second-page.json | 5 +
app/i18n/locales/en/translation.json | 11 +
app/i18n/locales/it/client-page.json | 10 +
app/i18n/locales/it/footer.json | 5 +
app/i18n/locales/it/second-client-page.json | 5 +
app/i18n/locales/it/second-page.json | 5 +
app/i18n/locales/it/translation.json | 11 +
app/i18n/settings.ts | 19 +
middleware.ts | 37 +
next-env.d.ts | 5 +
next.config.js | 11 +
package-lock.json | 7141 +++++++++++++++++++
package.json | 32 +
public/android-chrome-192x192.png | Bin 0 -> 11985 bytes
public/android-chrome-512x512.png | Bin 0 -> 45062 bytes
public/apple-touch-icon.png | Bin 0 -> 10749 bytes
public/favicon-16x16.png | Bin 0 -> 645 bytes
public/favicon-32x32.png | Bin 0 -> 1411 bytes
public/favicon.ico | Bin 0 -> 15406 bytes
public/site.webmanifest | 1 +
tsconfig.json | 36 +
49 files changed, 7915 insertions(+)
create mode 100755 .eslintrc.json
create mode 100644 .gitignore
create mode 100644 @types/i18next.d.ts
create mode 100644 README.md
create mode 100644 app/[lng]/client-page/head.tsx
create mode 100644 app/[lng]/client-page/page.tsx
create mode 100644 app/[lng]/components/Footer/FooterBase.tsx
create mode 100644 app/[lng]/components/Footer/client.tsx
create mode 100644 app/[lng]/components/Footer/index.tsx
create mode 100644 app/[lng]/components/Header.tsx
create mode 100644 app/[lng]/global.css
create mode 100644 app/[lng]/head.tsx
create mode 100644 app/[lng]/layout.tsx
create mode 100644 app/[lng]/page.tsx
create mode 100644 app/[lng]/second-client-page/head.tsx
create mode 100644 app/[lng]/second-client-page/page.tsx
create mode 100644 app/[lng]/second-page/head.tsx
create mode 100644 app/[lng]/second-page/page.tsx
create mode 100644 app/i18n/client.ts
create mode 100644 app/i18n/index.ts
create mode 100644 app/i18n/locales/de/client-page.json
create mode 100644 app/i18n/locales/de/footer.json
create mode 100644 app/i18n/locales/de/second-client-page.json
create mode 100644 app/i18n/locales/de/second-page.json
create mode 100644 app/i18n/locales/de/translation.json
create mode 100644 app/i18n/locales/en/client-page.json
create mode 100644 app/i18n/locales/en/footer.json
create mode 100644 app/i18n/locales/en/second-client-page.json
create mode 100644 app/i18n/locales/en/second-page.json
create mode 100644 app/i18n/locales/en/translation.json
create mode 100644 app/i18n/locales/it/client-page.json
create mode 100644 app/i18n/locales/it/footer.json
create mode 100644 app/i18n/locales/it/second-client-page.json
create mode 100644 app/i18n/locales/it/second-page.json
create mode 100644 app/i18n/locales/it/translation.json
create mode 100644 app/i18n/settings.ts
create mode 100644 middleware.ts
create mode 100644 next-env.d.ts
create mode 100644 next.config.js
create mode 100644 package-lock.json
create mode 100644 package.json
create mode 100644 public/android-chrome-192x192.png
create mode 100644 public/android-chrome-512x512.png
create mode 100644 public/apple-touch-icon.png
create mode 100644 public/favicon-16x16.png
create mode 100644 public/favicon-32x32.png
create mode 100644 public/favicon.ico
create mode 100644 public/site.webmanifest
create mode 100644 tsconfig.json
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100755
index 0000000..506f373
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+ "extends": "next/core-web-vitals",
+ "rules": {
+ "@next/next/no-img-element": 0
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..642a0ec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.next
+node_modules
+.vscode
+.next
+.DS_Store
\ No newline at end of file
diff --git a/@types/i18next.d.ts b/@types/i18next.d.ts
new file mode 100644
index 0000000..f14b275
--- /dev/null
+++ b/@types/i18next.d.ts
@@ -0,0 +1,30 @@
+/**
+ * If you want to enable locale keys typechecking and enhance IDE experience.
+ *
+ * Requires `resolveJsonModule:true` in your tsconfig.json.
+ *
+ * @link https://www.i18next.com/overview/typescript
+ */
+import 'i18next'
+
+import type translation from '../app/i18n/locales/en/translation.json'
+import type secondPage from '../app/i18n/locales/en/second-page.json'
+import type footer from '../app/i18n/locales/en/footer.json'
+import type clientPage from '../app/i18n/locales/en/client-page.json'
+import type secondClientPage from '../app/i18n/locales/en/second-client-page.json'
+
+interface I18nNamespaces {
+ translation: typeof translation
+ 'second-page': typeof secondPage
+ footer: typeof footer
+ 'client-page': typeof clientPage
+ 'second-client-page': typeof secondClientPage
+}
+
+declare module 'i18next' {
+ interface CustomTypeOptions {
+ // returnNull: false
+ // defaultNS: 'translation'
+ resources: I18nNamespaces
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f18984f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# Next.js 13 app directory feature in combination with i18next
+
+This example shows a basic way to use [i18next](https://www.i18next.com) (and [react-i18next](https://react.i18next.com)) in a [Next.js 13](https://beta.nextjs.org/) app with the new app directory features.
+[next-i18next](https://next.i18next.com) is not needed anymore for this setup.
+
+It shows i18next integration on some server side pages and some client side pages.
+
+There is also an example middleware with language detection and persistence via cookie.
+
+*This example has been created out of [this discussion](https://github.com/i18next/next-i18next/discussions/1993).*
+
+## There's also a [blog post](https://locize.com/blog/next-13-app-dir-i18n) describing this with more detail information.
+
+[](https://locize.com/blog/next-13-app-dir-i18n)
diff --git a/app/[lng]/client-page/head.tsx b/app/[lng]/client-page/head.tsx
new file mode 100644
index 0000000..1eb9ada
--- /dev/null
+++ b/app/[lng]/client-page/head.tsx
@@ -0,0 +1,15 @@
+import { useTranslation } from '../../i18n'
+
+export default async function Head({ params: { lng } }) {
+ const { t } = await useTranslation(lng, 'client-page')
+
+ return (
+ <>
+ {t('title')}
+
+ >
+ )
+}
diff --git a/app/[lng]/client-page/page.tsx b/app/[lng]/client-page/page.tsx
new file mode 100644
index 0000000..18baaea
--- /dev/null
+++ b/app/[lng]/client-page/page.tsx
@@ -0,0 +1,37 @@
+'use client'
+
+import Link from 'next/link'
+import { useTranslation } from '../../i18n/client'
+import { Header } from '../components/Header'
+import { Footer } from '../components/Footer/client'
+import { useState } from 'react'
+
+export default function Page({ params: { lng } }: {
+ params: {
+ lng: string;
+ };
+}) {
+ const { t } = useTranslation(lng, 'client-page')
+ const [counter, setCounter] = useState(0)
+ return (
+ <>
+
+
+ {t('counter', { count: counter })}
+
+
+
+
+
+ {t('to-second-client-page')}
+
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/[lng]/components/Footer/FooterBase.tsx b/app/[lng]/components/Footer/FooterBase.tsx
new file mode 100644
index 0000000..94ae83f
--- /dev/null
+++ b/app/[lng]/components/Footer/FooterBase.tsx
@@ -0,0 +1,46 @@
+import { i18n } from 'i18next'
+import Link from 'next/link'
+import { Trans } from 'react-i18next/TransWithoutContext'
+import { languages } from '../../../i18n/settings'
+
+export const FooterBase = ({ i18n, lng, path = '' }: { i18n: i18n, lng: string, path: string }) => {
+ const t = i18n.getFixedT(lng, 'footer')
+ return (
+
+ )
+}
diff --git a/app/[lng]/components/Footer/client.tsx b/app/[lng]/components/Footer/client.tsx
new file mode 100644
index 0000000..f40943f
--- /dev/null
+++ b/app/[lng]/components/Footer/client.tsx
@@ -0,0 +1,9 @@
+'use client'
+
+import { FooterBase } from './FooterBase'
+import { useTranslation } from '../../../i18n/client'
+
+export const Footer = ({ lng, path }) => {
+ const { i18n } = useTranslation(lng, 'footer')
+ return
+}
diff --git a/app/[lng]/components/Footer/index.tsx b/app/[lng]/components/Footer/index.tsx
new file mode 100644
index 0000000..08cf3ab
--- /dev/null
+++ b/app/[lng]/components/Footer/index.tsx
@@ -0,0 +1,10 @@
+import { useTranslation } from '../../../i18n'
+import { FooterBase } from './FooterBase'
+
+export const Footer = async ({ lng, path }: {
+ lng: string;
+ path?: string;
+}) => {
+ const { t, i18n } = await useTranslation(lng, 'footer')
+ return
+}
diff --git a/app/[lng]/components/Header.tsx b/app/[lng]/components/Header.tsx
new file mode 100644
index 0000000..ec79beb
--- /dev/null
+++ b/app/[lng]/components/Header.tsx
@@ -0,0 +1,12 @@
+export const Header = ({ heading }) => (
+ <>
+
+ Next.js 13 (app directory) - i18next
+
+
+ {heading}
+
+
+
+ >
+)
diff --git a/app/[lng]/global.css b/app/[lng]/global.css
new file mode 100644
index 0000000..fe87094
--- /dev/null
+++ b/app/[lng]/global.css
@@ -0,0 +1,99 @@
+body {
+ font-family: 'Open Sans', sans-serif;
+ text-align: center;
+ background-image: linear-gradient(
+ to left top,
+ #ffffff,
+ #f5f5f5,
+ #eaeaea,
+ #e0e0e0,
+ #d6d6d6
+ );
+ display: flex;
+ flex-direction: column;
+ margin: 0;
+ min-height: 100vh;
+ min-width: 100vw;
+}
+
+h1,
+h2 {
+ font-family: 'Oswald', sans-serif;
+}
+
+h1 {
+ font-size: 3rem;
+ /* margin: 5rem 0; */
+}
+h2 {
+ min-width: 18rem;
+ font-size: 2rem;
+ opacity: 0.3;
+}
+h3 {
+ font-size: 1.5rem;
+ opacity: 0.5;
+}
+
+p {
+ line-height: 1.65em;
+}
+p:nth-child(2) {
+ font-style: italic;
+ opacity: 0.65;
+ margin-top: 1rem;
+}
+
+a.github {
+ position: fixed;
+ top: 0.5rem;
+ right: 0.75rem;
+ font-size: 4rem;
+ color: #888;
+ opacity: 0.8;
+}
+a.github:hover {
+ opacity: 1;
+}
+
+button {
+ display: inline-block;
+ vertical-align: bottom;
+ outline: 0;
+ text-decoration: none;
+ cursor: pointer;
+ background-color: rgba(255, 255, 255, 0.5);
+ box-sizing: border-box;
+ font-size: 1em;
+ font-family: inherit;
+ border-radius: 3px;
+ transition: box-shadow 0.2s ease;
+ user-select: none;
+ line-height: 2.5em;
+ min-height: 40px;
+ padding: 0 0.8em;
+ border: 0;
+ color: inherit;
+ position: relative;
+ transform: translateZ(0);
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
+ margin: 0.8rem;
+}
+
+button:hover,
+button:focus {
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4);
+}
+
+main {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ justify-content: center;
+ align-items: center;
+}
+footer {
+ background-color: rgba(255, 255, 255, 0.5);
+ width: 100vw;
+ padding: 3rem 0;
+}
diff --git a/app/[lng]/head.tsx b/app/[lng]/head.tsx
new file mode 100644
index 0000000..68a01e2
--- /dev/null
+++ b/app/[lng]/head.tsx
@@ -0,0 +1,17 @@
+import { languages, fallbackLng } from '../i18n/settings'
+import { useTranslation } from '../i18n'
+
+export default async function Head({ params: { lng } }) {
+ if (languages.indexOf(lng) < 0) lng = fallbackLng
+ const { t } = await useTranslation(lng)
+
+ return (
+ <>
+ {t('title')}
+
+ >
+ )
+}
diff --git a/app/[lng]/layout.tsx b/app/[lng]/layout.tsx
new file mode 100644
index 0000000..4023202
--- /dev/null
+++ b/app/[lng]/layout.tsx
@@ -0,0 +1,24 @@
+import './global.css'
+
+import { dir } from 'i18next'
+import { languages } from '../i18n/settings'
+
+export async function generateStaticParams() {
+ return languages.map((lng) => ({ lng }))
+}
+
+export default function RootLayout({
+ children,
+ params: {
+ lng
+ }
+}) {
+ return (
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/app/[lng]/page.tsx b/app/[lng]/page.tsx
new file mode 100644
index 0000000..f325305
--- /dev/null
+++ b/app/[lng]/page.tsx
@@ -0,0 +1,53 @@
+import Link from 'next/link'
+import { Trans } from 'react-i18next/TransWithoutContext'
+import { languages, fallbackLng } from '../i18n/settings'
+import { useTranslation } from '../i18n'
+import { Header } from './components/Header'
+import { Footer } from './components/Footer'
+
+export default async function Page({ params: { lng } }: {
+ params: {
+ lng: string;
+ };
+}) {
+ if (languages.indexOf(lng) < 0) lng = fallbackLng
+ const { t } = await useTranslation(lng)
+
+ return (
+ <>
+
+
+
+
+ Welcome to Next.js v13 appDir and i18next
+
+
+
+
+
+ Check out the corresponding blog post describing this example.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* @ts-expect-error Server Component */}
+