diff --git a/front/apps/mobile/package.json b/front/apps/mobile/package.json
index 2e04546c..29511949 100644
--- a/front/apps/mobile/package.json
+++ b/front/apps/mobile/package.json
@@ -17,9 +17,11 @@
"expo-linking": "~3.2.3",
"expo-router": "^0.0.36",
"expo-status-bar": "~1.4.2",
+ "i18next": "^22.0.6",
"moti": "^0.21.0",
"react": "18.1.0",
"react-dom": "18.1.0",
+ "react-i18next": "^12.0.0",
"react-native": "0.70.5",
"react-native-reanimated": "~2.12.0",
"react-native-safe-area-context": "4.4.1",
diff --git a/front/apps/web/package.json b/front/apps/web/package.json
index 3d02cd4d..aa8044fb 100644
--- a/front/apps/web/package.json
+++ b/front/apps/web/package.json
@@ -25,6 +25,7 @@
"csstype": "^3.1.1",
"expo-linear-gradient": "^12.0.1",
"hls.js": "^1.2.8",
+ "i18next": "^22.0.6",
"jotai": "^1.10.0",
"moti": "^0.21.0",
"next": "13.0.5",
@@ -34,6 +35,7 @@
"raf": "^3.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
+ "react-i18next": "^12.0.0",
"react-infinite-scroll-component": "^6.1.0",
"react-native-reanimated": "^2.13.0",
"react-native-web": "^0.18.10",
diff --git a/front/apps/web/src/i18n.tsx b/front/apps/web/src/i18n.tsx
new file mode 100644
index 00000000..f9d49c81
--- /dev/null
+++ b/front/apps/web/src/i18n.tsx
@@ -0,0 +1,83 @@
+/*
+ * Kyoo - A portable and vast media library solution.
+ * Copyright (c) Kyoo.
+ *
+ * See AUTHORS.md and LICENSE file in the project root for full license information.
+ *
+ * Kyoo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * Kyoo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Kyoo. If not, see .
+ */
+
+import { ComponentType, useMemo } from "react";
+import i18next, { InitOptions } from "i18next";
+import { I18nextProvider } from "react-i18next";
+import { AppContext, AppInitialProps, type AppProps } from "next/app";
+
+import en from "../../../translations/en.json";
+import fr from "../../../translations/fr.json";
+
+export const withTranslations = (
+ AppToTranslate: ComponentType & {
+ getInitialProps: (ctx: AppContext) => Promise;
+ },
+) => {
+ const i18n = i18next.createInstance();
+ const commonOptions: InitOptions = {
+ debug: true,
+ interpolation: {
+ escapeValue: false,
+ },
+ };
+
+ const AppWithTranslations = (props: AppProps) => {
+ const li18n = useMemo(
+ () =>
+ typeof window === "undefined"
+ ? i18n
+ : (i18next.init({
+ ...commonOptions,
+ lng: props.pageProps.__lang,
+ resources: props.pageProps.__resources,
+ }),
+ i18next),
+ [props.pageProps.__lang, props.pageProps.__resources],
+ );
+
+ return (
+
+
+
+ );
+ };
+ AppWithTranslations.getInitialProps = async (ctx: AppContext) => {
+ const props: AppInitialProps = await AppToTranslate.getInitialProps(ctx);
+ const lng = ctx.router.locale || ctx.router.defaultLocale || "en";
+ // TODO: use a backend to fetch only the needed translations.
+ // TODO: use a different backend on the client and fetch needed translations.
+ const resources = {
+ en: { translation: en },
+ fr: { translation: fr },
+ };
+ await i18n.init({
+ ...commonOptions,
+ lng,
+ fallbackLng: ctx.router.defaultLocale || "en",
+ resources,
+ });
+ props.pageProps.__lang = lng;
+ props.pageProps.__resources = resources;
+ return props;
+ };
+
+ return AppWithTranslations;
+};
diff --git a/front/apps/web/src/pages/_app.tsx b/front/apps/web/src/pages/_app.tsx
index dff31e9a..f9196fda 100755
--- a/front/apps/web/src/pages/_app.tsx
+++ b/front/apps/web/src/pages/_app.tsx
@@ -21,15 +21,15 @@
import "../polyfill";
import { ReactNode, useState } from "react";
-import appWithI18n from "next-translate/appWithI18n";
-import { useTheme, useMobileHover } from "yoshiki/web";
-import { createTheme, ThemeProvider as MTheme } from "@mui/material";
import NextApp, { AppContext, type AppProps } from "next/app";
+import { createTheme, ThemeProvider as MTheme } from "@mui/material";
import { Hydrate, QueryClientProvider } from "@tanstack/react-query";
+import { ThemeSelector as KThemeSelector, WebTooltip } from "@kyoo/primitives";
import { createQueryClient, fetchQuery, QueryIdentifier, QueryPage } from "@kyoo/models";
+import { useTheme, useMobileHover } from "yoshiki/web";
import superjson from "superjson";
import Head from "next/head";
-import { ThemeSelector as KThemeSelector, WebTooltip } from "@kyoo/primitives";
+import { withTranslations } from "../i18n";
const ThemeSelector = ({ children }: { children?: ReactNode | ReactNode[] }) => {
// TODO: Handle user selected mode (light, dark, auto)
@@ -106,15 +106,5 @@ App.getInitialProps = async (ctx: AppContext) => {
return { pageProps: superjson.serialize(appProps.pageProps) };
};
-// The as any is needed since appWithI18n as wrong type hints
-export default appWithI18n(App as any, {
- skipInitialProps: false,
- locales: ["en", "fr"],
- defaultLocale: "en",
- loader: false,
- pages: {
- "*": ["common", "browse", "player"],
- },
- loadLocaleFrom: (locale, namespace) =>
- import(`../../locales/${locale}/${namespace}`).then((m) => m.default),
-});
+
+export default withTranslations(App);
diff --git a/front/apps/web/src/pages/browse/index.tsx b/front/apps/web/src/pages/browse/index.tsx
index 9178c46d..64fc8e46 100644
--- a/front/apps/web/src/pages/browse/index.tsx
+++ b/front/apps/web/src/pages/browse/index.tsx
@@ -433,7 +433,7 @@ BrowsePage.getLayout = (page) => {
BrowsePage.getFetchUrls = ({ slug, sortBy }) => [
query(slug, sortBy?.split("-")[0] as SortBy, sortBy?.split("-")[1] as SortOrd),
- /* Navbar.query(), */
+ Navbar.query(),
];
export default withRoute(BrowsePage);
diff --git a/front/apps/web/src/styled-jsx.d.ts b/front/apps/web/src/styled-jsx.d.ts
deleted file mode 100644
index 58518fe7..00000000
--- a/front/apps/web/src/styled-jsx.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/front/packages/ui/package.json b/front/packages/ui/package.json
index 9561b9ef..d8a4ea5b 100644
--- a/front/packages/ui/package.json
+++ b/front/packages/ui/package.json
@@ -15,8 +15,10 @@
"peerDependencies": {
"@tanstack/react-query": "*",
"expo-linear-gradient": "*",
+ "i18next": "*",
"moti": "*",
"react": "*",
+ "react-i18next": "*",
"react-native": "*",
"react-native-reanimated": "*",
"yoshiki": "*"
diff --git a/front/packages/ui/src/navbar/index.tsx b/front/packages/ui/src/navbar/index.tsx
index 09b65589..42df77f1 100644
--- a/front/packages/ui/src/navbar/index.tsx
+++ b/front/packages/ui/src/navbar/index.tsx
@@ -18,11 +18,11 @@
* along with Kyoo. If not, see .
*/
-import useTranslation from "next-translate/useTranslation";
import { Library, LibraryP, Page, Paged, QueryIdentifier } from "@kyoo/models";
-import { rem, useYoshiki } from "yoshiki/native";
import { IconButton, Header, Avatar, A, Skeleton, tooltip, ts } from "@kyoo/primitives";
import { View } from "react-native";
+import { useTranslation } from 'react-i18next';
+import { rem, useYoshiki } from "yoshiki/native";
import { Fetch } from "../fetch";
import { KyooLongLogo } from "./icon";
@@ -30,7 +30,7 @@ export const NavbarTitle = KyooLongLogo;
export const Navbar = () => {
const { css } = useYoshiki();
- const { t } = useTranslation("common");
+ const { t } = useTranslation();
return (
= 19.0.0"
+ react: ">= 16.8.0"
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ checksum: f523d7ec5dcb7f5fd36efc9385639d01160071f4dadbbc5a3f1daa64b0f332f709347bdd3bc68f5aefbd72c8742233aa715b308b1ca87179ad501acc19c72068
+ languageName: node
+ linkType: hard
+
"react-infinite-scroll-component@npm:^6.1.0":
version: 6.1.0
resolution: "react-infinite-scroll-component@npm:6.1.0"
@@ -13116,6 +13156,13 @@ __metadata:
languageName: node
linkType: hard
+"void-elements@npm:3.1.0":
+ version: 3.1.0
+ resolution: "void-elements@npm:3.1.0"
+ checksum: 0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f
+ languageName: node
+ linkType: hard
+
"walker@npm:^1.0.7":
version: 1.0.8
resolution: "walker@npm:1.0.8"
@@ -13175,6 +13222,7 @@ __metadata:
eslint-config-next: 13.0.5
expo-linear-gradient: ^12.0.1
hls.js: ^1.2.8
+ i18next: ^22.0.6
jotai: ^1.10.0
moti: ^0.21.0
next: 13.0.5
@@ -13184,6 +13232,7 @@ __metadata:
raf: ^3.4.1
react: 18.2.0
react-dom: 18.2.0
+ react-i18next: ^12.0.0
react-infinite-scroll-component: ^6.1.0
react-native-reanimated: ^2.13.0
react-native-web: ^0.18.10