diff --git a/frontend/apps/mobile/src/i18n.ts b/frontend/apps/mobile/src/i18n.ts
new file mode 100644
index 00000000..55b49bbe
--- /dev/null
+++ b/frontend/apps/mobile/src/i18n.ts
@@ -0,0 +1,15 @@
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import { resources, defaultNS } from "@abrechnung/translations";
+
+i18n.use(initReactI18next).init({
+ ns: [defaultNS],
+ resources,
+ defaultNS,
+ lng: "en",
+ fallbackLng: "en",
+ debug: true,
+ interpolation: { escapeValue: false },
+});
+
+export default i18n;
diff --git a/frontend/apps/mobile/src/i18next.d.ts b/frontend/apps/mobile/src/i18next.d.ts
new file mode 100644
index 00000000..8e2672a6
--- /dev/null
+++ b/frontend/apps/mobile/src/i18next.d.ts
@@ -0,0 +1,9 @@
+import "i18next";
+import type { resources, defaultNS } from "@abrechnung/translations";
+
+declare module "i18next" {
+ interface CustomTypeOptions {
+ defaultNS: typeof defaultNS;
+ resources: (typeof resources)["en"];
+ }
+}
diff --git a/frontend/apps/web/src/app/authenticated-layout/AuthenticatedLayout.tsx b/frontend/apps/web/src/app/authenticated-layout/AuthenticatedLayout.tsx
index 993c3c73..2900f28b 100644
--- a/frontend/apps/web/src/app/authenticated-layout/AuthenticatedLayout.tsx
+++ b/frontend/apps/web/src/app/authenticated-layout/AuthenticatedLayout.tsx
@@ -45,6 +45,7 @@ import { useTheme } from "@mui/material/styles";
import { Banner } from "../../components/style/Banner";
import Loading from "../../components/style/Loading";
import styles from "./AuthenticatedLayout.module.css";
+import { LanguageSelect } from "@/components/LanguageSelect";
const drawerWidth = 240;
const AUTH_FALLBACK = "/login";
@@ -216,6 +217,7 @@ export const AuthenticatedLayout: React.FC = () => {
+
= ({ activeGroupId }) => {
+ const { t } = useTranslation();
const isGuest = useAppSelector((state) => selectIsGuestUser({ state: selectAuthSlice(state) }));
const groups = useAppSelector((state) => selectGroups({ state: selectGroupSlice(state) }));
const [showGroupCreationModal, setShowGroupCreationModal] = useState(false);
@@ -45,9 +47,11 @@ export const SidebarGroupList: React.FC = ({ activeGroupId }) => {
{!isGuest && (
-
-
-
+
+
+
+
+
)}
diff --git a/frontend/apps/web/src/app/unauthenticated-layout/UnauthenticatedLayout.tsx b/frontend/apps/web/src/app/unauthenticated-layout/UnauthenticatedLayout.tsx
index 9617ccb1..41d829ad 100644
--- a/frontend/apps/web/src/app/unauthenticated-layout/UnauthenticatedLayout.tsx
+++ b/frontend/apps/web/src/app/unauthenticated-layout/UnauthenticatedLayout.tsx
@@ -4,6 +4,7 @@ import { AppBar, Box, Button, Container, CssBaseline, Toolbar, Typography } from
import { Banner } from "../../components/style/Banner";
import { selectIsAuthenticated } from "@abrechnung/redux";
import { useAppSelector, selectAuthSlice } from "../../store";
+import { LanguageSelect } from "@/components/LanguageSelect";
export const UnauthenticatedLayout: React.FC = () => {
const authenticated = useAppSelector((state) => selectIsAuthenticated({ state: selectAuthSlice(state) }));
@@ -27,6 +28,7 @@ export const UnauthenticatedLayout: React.FC = () => {
Abrechnung
+
diff --git a/frontend/apps/web/src/components/LanguageSelect.tsx b/frontend/apps/web/src/components/LanguageSelect.tsx
new file mode 100644
index 00000000..73c8e596
--- /dev/null
+++ b/frontend/apps/web/src/components/LanguageSelect.tsx
@@ -0,0 +1,21 @@
+import { MenuItem, Select, SelectChangeEvent, SelectProps } from "@mui/material";
+import * as React from "react";
+import { useTranslation } from "react-i18next";
+
+export type LanguageSelectProps = Omit;
+
+export const LanguageSelect: React.FC = (props) => {
+ const { t, i18n } = useTranslation();
+
+ const handleSetLanguage = (event: SelectChangeEvent) => {
+ const lang = event.target.value as string;
+ i18n.changeLanguage(lang);
+ };
+
+ return (
+
+ );
+};
diff --git a/frontend/apps/web/src/i18n.ts b/frontend/apps/web/src/i18n.ts
index 145e77a2..0949cd7d 100644
--- a/frontend/apps/web/src/i18n.ts
+++ b/frontend/apps/web/src/i18n.ts
@@ -1,14 +1,15 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
-import Backend from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";
+import { resources, defaultNS } from "@abrechnung/translations";
-i18n.use(Backend)
- .use(LanguageDetector)
+i18n.use(LanguageDetector)
.use(initReactI18next)
.init({
- backend: { loadPath: "/assets/locales/{{lng}}/{{ns}}.json" },
- lng: "en",
+ ns: [defaultNS],
+ resources,
+ defaultNS,
+ lng: "en-US",
fallbackLng: "en",
debug: true,
interpolation: { escapeValue: false },
diff --git a/frontend/apps/web/src/i18next.d.ts b/frontend/apps/web/src/i18next.d.ts
new file mode 100644
index 00000000..0d806f61
--- /dev/null
+++ b/frontend/apps/web/src/i18next.d.ts
@@ -0,0 +1,10 @@
+import "i18next";
+import type { resources, defaultNS } from "@abrechnung/translations";
+
+declare module "i18next" {
+ interface CustomTypeOptions {
+ defaultNS: typeof defaultNS;
+ nsSeparator: "";
+ resources: { "": (typeof resources)["en"]["translations"] };
+ }
+}
diff --git a/frontend/apps/web/src/pages/accounts/Balances.tsx b/frontend/apps/web/src/pages/accounts/Balances.tsx
index 6c61ea5c..9bc78528 100644
--- a/frontend/apps/web/src/pages/accounts/Balances.tsx
+++ b/frontend/apps/web/src/pages/accounts/Balances.tsx
@@ -152,7 +152,7 @@ export const Balances: React.FC = ({ groupId }) => {
top: 20,
right: 20,
bottom: 20,
- left: 20,
+ left: 30,
}}
layout="vertical"
onClick={handleBarClick}
diff --git a/frontend/apps/web/src/pages/profile/ChangeEmail.tsx b/frontend/apps/web/src/pages/profile/ChangeEmail.tsx
index 52f54e4e..f959e7b2 100644
--- a/frontend/apps/web/src/pages/profile/ChangeEmail.tsx
+++ b/frontend/apps/web/src/pages/profile/ChangeEmail.tsx
@@ -17,14 +17,14 @@ type FormSchema = z.infer;
export const ChangeEmail: React.FC = () => {
const { t } = useTranslation();
- useTitle(t("Abrechnung - Change E-Mail"));
+ useTitle(t("profile.changeEmail.tabTitle"));
const handleSubmit = (values: FormSchema, { setSubmitting, resetForm }: FormikHelpers) => {
api.client.auth
.changeEmail({ requestBody: { password: values.password, email: values.newEmail } })
.then(() => {
setSubmitting(false);
- toast.success(t("Requested email change, you should receive an email with a confirmation link soon"));
+ toast.success(t("profile.changeEmail.success"));
resetForm();
})
.catch((error) => {
@@ -36,7 +36,7 @@ export const ChangeEmail: React.FC = () => {
return (
- {t("Change E-Mail")}
+ {t("profile.changeEmail.pageTitle")}
{
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
- label={t("Password")}
+ label={t("common.password")}
error={touched.password && !!errors.password}
helperText={touched.password && errors.password}
/>
@@ -69,14 +69,14 @@ export const ChangeEmail: React.FC = () => {
value={values.newEmail}
onChange={handleChange}
onBlur={handleBlur}
- label={t("New E-Mail")}
+ label={t("profile.changeEmail.newEmail")}
error={touched.newEmail && !!errors.newEmail}
helperText={touched.newEmail && errors.newEmail}
/>
{isSubmitting && }
)}
diff --git a/frontend/apps/web/src/pages/profile/ChangePassword.tsx b/frontend/apps/web/src/pages/profile/ChangePassword.tsx
index 9267d82b..b10280ee 100644
--- a/frontend/apps/web/src/pages/profile/ChangePassword.tsx
+++ b/frontend/apps/web/src/pages/profile/ChangePassword.tsx
@@ -23,14 +23,14 @@ type FormSchema = z.infer;
export const ChangePassword: React.FC = () => {
const { t } = useTranslation();
- useTitle(t("Abrechnung - Change Password"));
+ useTitle(t("profile.changePassword.tabTitle"));
const handleSubmit = (values: FormSchema, { setSubmitting, resetForm }: FormikHelpers) => {
api.client.auth
.changePassword({ requestBody: { old_password: values.password, new_password: values.newPassword } })
.then(() => {
setSubmitting(false);
- toast.success(t("Successfully changed password"));
+ toast.success(t("profile.changePassword.success"));
resetForm();
})
.catch((error) => {
@@ -42,7 +42,7 @@ export const ChangePassword: React.FC = () => {
return (
- {t("Change Password")}
+ {t("profile.changePassword.pageTitle")}
{
margin="normal"
type="password"
name="password"
- label={t("Password")}
+ label={t("common.password")}
variant="standard"
value={values.password}
onChange={handleChange}
@@ -75,7 +75,7 @@ export const ChangePassword: React.FC = () => {
margin="normal"
type="password"
name="newPassword"
- label={t("New Password")}
+ label={t("profile.changePassword.newPassword")}
variant="standard"
value={values.newPassword}
onChange={handleChange}
@@ -93,14 +93,14 @@ export const ChangePassword: React.FC = () => {
margin="normal"
type="password"
name="newPassword2"
- label={t("Repeat Password")}
+ label={t("profile.changePassword.repeatPassword")}
error={touched.newPassword2 && !!errors.newPassword2}
helperText={touched.newPassword2 && errors.newPassword2}
/>
{isSubmitting && }
)}
diff --git a/frontend/apps/web/src/pages/profile/Profile.tsx b/frontend/apps/web/src/pages/profile/Profile.tsx
index 7e0d01e0..102be4a3 100644
--- a/frontend/apps/web/src/pages/profile/Profile.tsx
+++ b/frontend/apps/web/src/pages/profile/Profile.tsx
@@ -5,37 +5,34 @@ import { selectAuthSlice, useAppSelector } from "@/store";
import { selectProfile } from "@abrechnung/redux";
import { Alert, List, ListItem, ListItemText, Typography } from "@mui/material";
import { DateTime } from "luxon";
-import React from "react";
+import * as React from "react";
+import { useTranslation } from "react-i18next";
export const Profile: React.FC = () => {
+ const { t } = useTranslation();
const profile = useAppSelector((state) => selectProfile({ state: selectAuthSlice(state) }));
- useTitle("Abrechnung - Profile");
+ useTitle(t("profile.index.tabTitle"));
return (
- Profile
+ {t("profile.index.pageTitle")}
{profile === undefined ? (
) : (
<>
- {profile.is_guest_user && (
-
- You are a guest user on this Abrechnung and therefore not permitted to create new groups or
- group invites.
-
- )}
+ {profile.is_guest_user && {t("profile.index.guestUserDisclaimer")}}
-
+
-
+
{
);
};
-
-export default Profile;
diff --git a/frontend/apps/web/src/pages/profile/Settings.tsx b/frontend/apps/web/src/pages/profile/Settings.tsx
index 224c9e6b..8a508858 100644
--- a/frontend/apps/web/src/pages/profile/Settings.tsx
+++ b/frontend/apps/web/src/pages/profile/Settings.tsx
@@ -1,4 +1,14 @@
-import React, { useState } from "react";
+import { MobilePaper } from "@/components/style/mobile";
+import { useTitle } from "@/core/utils";
+import {
+ ThemeMode,
+ persistor,
+ selectSettingsSlice,
+ selectTheme,
+ themeChanged,
+ useAppDispatch,
+ useAppSelector,
+} from "@/store";
import {
Alert,
Box,
@@ -8,32 +18,24 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
- Divider,
FormControl,
FormGroup,
FormLabel,
MenuItem,
Select,
+ Stack,
Typography,
} from "@mui/material";
-import { MobilePaper } from "../../components/style/mobile";
-import { useTitle } from "../../core/utils";
-import {
- useAppDispatch,
- useAppSelector,
- themeChanged,
- selectTheme,
- persistor,
- ThemeMode,
- selectSettingsSlice,
-} from "../../store";
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
export const Settings: React.FC = () => {
+ const { t } = useTranslation();
const dispatch = useAppDispatch();
const themeMode = useAppSelector((state) => selectTheme({ state: selectSettingsSlice(state) }));
- useTitle("Abrechnung - Settings");
+ useTitle(t("profile.settings.tabTitle"));
const [confirmClearCacheOpen, setConfirmClearCacheOpen] = useState(false);
@@ -62,44 +64,36 @@ export const Settings: React.FC = () => {
return (
-
- Settings
-
-
- These settings are stored locally on your device. Clearing your Browser's local storage will reset
- them.
-
-
-
-
- Theme
-
-
-
-
-
-
-
- Clear Cache
-
-
-
- {/*ACHTUNG!*/}
-
-
+
+
+ {t("profile.settings.pageTitle")}
+
+ {t("profile.settings.info")}
+
+
+
+ {t("profile.settings.theme")}
+
+
+
+
+
+
+
+
diff --git a/frontend/apps/web/tsconfig.json b/frontend/apps/web/tsconfig.json
index 576a22f9..988dd790 100644
--- a/frontend/apps/web/tsconfig.json
+++ b/frontend/apps/web/tsconfig.json
@@ -19,6 +19,7 @@
"@abrechnung/redux": ["../../libs/redux/src/index.ts"],
"@abrechnung/types": ["../../libs/types/src/index.ts"],
"@abrechnung/utils": ["../../libs/utils/src/index.ts"],
+ "@abrechnung/translations": ["../../libs/translations/src/index.ts"],
"@/*": ["src/*"]
}
},
diff --git a/frontend/assets/locales/de/translation.json b/frontend/assets/locales/de/translation.json
deleted file mode 100644
index 9986efd7..00000000
--- a/frontend/assets/locales/de/translation.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "Abrechnung - Change E-Mail": "Abrechnung - E-Mail ändern",
- "Abrechnung - Change Password": "Abrechnung - Passwort ändern",
- "Successfully changed password": "Passwort erfolgreich geändert",
- "Change Password": "Passwort Ändern",
- "Password": "Passwort",
- "New Password": "Neues Passwort",
- "Repeat Password": "Passwort wiederholen",
- "Save": "Speichern"
-}
diff --git a/frontend/libs/translations/.eslintrc.json b/frontend/libs/translations/.eslintrc.json
new file mode 100644
index 00000000..5626944b
--- /dev/null
+++ b/frontend/libs/translations/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["../../.eslintrc.json"],
+ "ignorePatterns": ["!**/*"],
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.js", "*.jsx"],
+ "rules": {}
+ }
+ ]
+}
diff --git a/frontend/libs/translations/README.md b/frontend/libs/translations/README.md
new file mode 100644
index 00000000..f8d3a2c1
--- /dev/null
+++ b/frontend/libs/translations/README.md
@@ -0,0 +1,3 @@
+# translations
+
+This library was generated with [Nx](https://nx.dev).
diff --git a/frontend/libs/translations/project.json b/frontend/libs/translations/project.json
new file mode 100644
index 00000000..c99f01f2
--- /dev/null
+++ b/frontend/libs/translations/project.json
@@ -0,0 +1,16 @@
+{
+ "name": "translations",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "libs/translations/src",
+ "projectType": "library",
+ "targets": {
+ "lint": {
+ "executor": "@nx/eslint:lint",
+ "outputs": ["{options.outputFile}"],
+ "options": {
+ "lintFilePatterns": ["libs/translations/**/*.ts"]
+ }
+ }
+ },
+ "tags": []
+}
diff --git a/frontend/libs/translations/src/index.ts b/frontend/libs/translations/src/index.ts
new file mode 100644
index 00000000..8cd5167d
--- /dev/null
+++ b/frontend/libs/translations/src/index.ts
@@ -0,0 +1 @@
+export * from "./lib";
diff --git a/frontend/libs/translations/src/lib/de.ts b/frontend/libs/translations/src/lib/de.ts
new file mode 100644
index 00000000..8b34f60b
--- /dev/null
+++ b/frontend/libs/translations/src/lib/de.ts
@@ -0,0 +1,12 @@
+import type { en } from "./en";
+
+const translations = {
+ languages: {
+ en: "Englisch",
+ de: "Deutsch",
+ },
+} satisfies Partial<(typeof en)["translations"]>;
+
+export const de = {
+ translations,
+};
diff --git a/frontend/libs/translations/src/lib/en.ts b/frontend/libs/translations/src/lib/en.ts
new file mode 100644
index 00000000..aaae5360
--- /dev/null
+++ b/frontend/libs/translations/src/lib/en.ts
@@ -0,0 +1,55 @@
+const translations = {
+ common: {
+ username: "Username",
+ email: "E-Mail",
+ password: "Password",
+ save: "Save",
+ yes: "Yes",
+ cancel: "Cancel",
+ },
+ groups: {
+ addGroup: "Add Group",
+ },
+ profile: {
+ index: {
+ tabTitle: "Abrechnung - Profile",
+ pageTitle: "Profile",
+ guestUserDisclaimer:
+ "You are a guest user on this Abrechnung and therefore not permitted to create new groups or group invites.",
+ registered: "Registered",
+ },
+ settings: {
+ tabTitle: "Abrechnung - Settings",
+ pageTitle: "Settings",
+ info: "These settings are stored locally on your device. Clearing your Browser's local storage will reset them.",
+ theme: "Theme",
+ themeSystemDefault: "System Default",
+ themeDarkMode: "Dark Mode",
+ themeLightMode: "Light Mode",
+ clearCache: "Clear Cache",
+ confirmClearCache:
+ "This action will clear your local cache. All your settings (this page) will not be reset.",
+ },
+ changePassword: {
+ tabTitle: "Abrechnung - Change Password",
+ pageTitle: "Change Password",
+ success: "Successfully changed password",
+ newPassword: "New Password",
+ repeatPassword: "Repeat Password",
+ },
+ changeEmail: {
+ tabTitle: "Abrechnung - Change E-Mail",
+ pageTitle: "Change E-Mail",
+ success: "Requested email change, you should receive an email with a confirmation link soon",
+ newEmail: "New E-Mail",
+ },
+ },
+ languages: {
+ en: "English",
+ de: "German",
+ },
+};
+
+export const en = {
+ translations,
+};
diff --git a/frontend/libs/translations/src/lib/index.ts b/frontend/libs/translations/src/lib/index.ts
new file mode 100644
index 00000000..690c2092
--- /dev/null
+++ b/frontend/libs/translations/src/lib/index.ts
@@ -0,0 +1,11 @@
+import { en } from "./en";
+import { de } from "./de";
+export * from "./de";
+export * from "./en";
+
+export const defaultNS = "translations";
+
+export const resources = {
+ en: en,
+ de: de,
+};
diff --git a/frontend/libs/translations/tsconfig.json b/frontend/libs/translations/tsconfig.json
new file mode 100644
index 00000000..4d6eaab3
--- /dev/null
+++ b/frontend/libs/translations/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "module": "commonjs"
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ }
+ ]
+}
diff --git a/frontend/libs/translations/tsconfig.lib.json b/frontend/libs/translations/tsconfig.lib.json
new file mode 100644
index 00000000..d349f641
--- /dev/null
+++ b/frontend/libs/translations/tsconfig.lib.json
@@ -0,0 +1,11 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "outDir": "../../dist/out-tsc",
+ "declaration": true,
+ "types": ["node"]
+ },
+ "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
+ "include": ["src/**/*.ts"]
+}
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 23d02166..58715512 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -36,16 +36,15 @@
"core-js": "^3.33.2",
"deepmerge": "^4.3.1",
"formik": "^2.4.5",
- "i18next": "^23.6.0",
+ "i18next": "^23.7.13",
"i18next-browser-languagedetector": "^7.1.0",
- "i18next-http-backend": "^2.3.1",
"localforage": "^1.10.0",
"luxon": "^3.4.3",
"multer": "^1.4.5-lts.1",
"proxy-memoize": "^1.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-i18next": "^13.3.1",
+ "react-i18next": "^13.5.0",
"react-native": "0.72.6",
"react-native-gesture-handler": "~2.13.4",
"react-native-pager-view": "^6.2.2",
@@ -23817,9 +23816,9 @@
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
},
"node_modules/i18next": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.6.0.tgz",
- "integrity": "sha512-z0Cxr0MGkt+kli306WS4nNNM++9cgt2b2VCMprY92j+AIab/oclgPxdwtTZVLP1zn5t5uo8M6uLsZmYrcjr3HA==",
+ "version": "23.7.13",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.7.13.tgz",
+ "integrity": "sha512-DbCPlw6VmURSZa43iOnycxq9o15e+WuBWDBZ3aj+gQZcDz4sgnuKwrcwmP1n8gSSCwCN7CRFGTpnwTd93A16Mg==",
"funding": [
{
"type": "individual",
@@ -23835,7 +23834,7 @@
}
],
"dependencies": {
- "@babel/runtime": "^7.22.5"
+ "@babel/runtime": "^7.23.2"
}
},
"node_modules/i18next-browser-languagedetector": {
@@ -23846,60 +23845,6 @@
"@babel/runtime": "^7.19.4"
}
},
- "node_modules/i18next-http-backend": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.3.1.tgz",
- "integrity": "sha512-jnagFs5cnq4ryb+g92Hex4tB5kj3tWmiRWx8gHMCcE/PEgV1fjH5rC7xyJmPSgyb9r2xgcP8rvZxPKgsmvMqTw==",
- "dependencies": {
- "cross-fetch": "4.0.0"
- }
- },
- "node_modules/i18next-http-backend/node_modules/cross-fetch": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
- "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
- "dependencies": {
- "node-fetch": "^2.6.12"
- }
- },
- "node_modules/i18next-http-backend/node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/i18next-http-backend/node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
- },
- "node_modules/i18next-http-backend/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
- },
- "node_modules/i18next-http-backend/node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
"node_modules/i18next-parser": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/i18next-parser/-/i18next-parser-8.9.0.tgz",
@@ -32273,9 +32218,9 @@
}
},
"node_modules/react-i18next": {
- "version": "13.3.1",
- "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-13.3.1.tgz",
- "integrity": "sha512-JAtYREK879JXaN9GdzfBI4yJeo/XyLeXWUsRABvYXiFUakhZJ40l+kaTo+i+A/3cKIED41kS/HAbZ5BzFtq/Og==",
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-13.5.0.tgz",
+ "integrity": "sha512-CFJ5NDGJ2MUyBohEHxljOq/39NQ972rh1ajnadG9BjTk+UXbHLq4z5DKEbEQBDoIhUmmbuS/fIMJKo6VOax1HA==",
"dependencies": {
"@babel/runtime": "^7.22.5",
"html-parse-stringify": "^3.0.1"
diff --git a/frontend/package.json b/frontend/package.json
index d1d7ca0b..24b35c12 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -36,16 +36,15 @@
"core-js": "^3.33.2",
"deepmerge": "^4.3.1",
"formik": "^2.4.5",
- "i18next": "^23.6.0",
+ "i18next": "^23.7.13",
"i18next-browser-languagedetector": "^7.1.0",
- "i18next-http-backend": "^2.3.1",
"localforage": "^1.10.0",
"luxon": "^3.4.3",
"multer": "^1.4.5-lts.1",
"proxy-memoize": "^1.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-i18next": "^13.3.1",
+ "react-i18next": "^13.5.0",
"react-native": "0.72.6",
"react-native-gesture-handler": "~2.13.4",
"react-native-pager-view": "^6.2.2",
diff --git a/frontend/tsconfig.base.json b/frontend/tsconfig.base.json
index 01a3457f..6d8f76ed 100644
--- a/frontend/tsconfig.base.json
+++ b/frontend/tsconfig.base.json
@@ -18,6 +18,7 @@
"@abrechnung/api": ["libs/api/src/index.ts"],
"@abrechnung/core": ["libs/core/src/index.ts"],
"@abrechnung/redux": ["libs/redux/src/index.ts"],
+ "@abrechnung/translations": ["libs/translations/src/index.ts"],
"@abrechnung/types": ["libs/types/src/index.ts"],
"@abrechnung/utils": ["libs/utils/src/index.ts"]
},