Skip to content

Commit

Permalink
Setup i18n for website
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardozgz committed Jul 19, 2024
1 parent a596e64 commit ce99aa2
Show file tree
Hide file tree
Showing 24 changed files with 1,195 additions and 108 deletions.
2 changes: 0 additions & 2 deletions apps/bot/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
interface Resources {
"main": {
"langCode": "en-US",
"langName": "English, US",
"interaction": {
"commandHandler": {
"error": {
Expand Down
2 changes: 0 additions & 2 deletions apps/bot/src/locales/en-US/main.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{
"langCode": "en-US",
"langName": "English, US",
"interaction": {
"commandHandler": {
"error": {
Expand Down
5 changes: 1 addition & 4 deletions apps/bot/src/locales/es-ES/main.json
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
{
"langCode": "es-ES",
"langName": "Español"
}
{}
5 changes: 1 addition & 4 deletions apps/bot/src/locales/ru/main.json
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
{
"langCode": "ru",
"langName": "Русский"
}
{}
12 changes: 10 additions & 2 deletions apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"private": true,
"type": "module",
"scripts": {
"build": "pnpm with-env next build --experimental-build-mode compile",
"build": "pnpm run generate-i18next-res; pnpm with-env next build --experimental-build-mode compile",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env next dev",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"start": "pnpm with-env next start",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
"with-env": "dotenv -e ../../.env --",
"generate-i18next-res": "i18next-resources-for-ts interface -i ./src/i18n/locales/en-US -o ./src/@types/resources.d.ts"
},
"dependencies": {
"@discordjs/rest": "^2.3.0",
Expand All @@ -28,7 +29,11 @@
"@trpc/react-query": "next",
"@trpc/server": "next",
"@twemoji/api": "^15.1.0",
"accept-language": "^3.0.18",
"discord-api-types": "^0.37.83",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-resources-to-backend": "^1.2.1",
"ioredis": "^5.4.1",
"iron-session": "^8.0.1",
"is-hotkey": "^0.2.0",
Expand All @@ -39,7 +44,9 @@
"node-loader": "^2.0.0",
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-cookie": "^7.1.4",
"react-dom": "18.2.0",
"react-i18next": "^15.0.0",
"server-only": "^0.0.1",
"slate": "^0.103.0",
"slate-history": "^0.100.0",
Expand All @@ -63,6 +70,7 @@
"@types/uuid": "^9.0.8",
"dotenv-cli": "^7.4.1",
"eslint": "^9.2.0",
"i18next-resources-for-ts": "^1.5.0",
"jiti": "^1.21.0",
"prettier": "^3.2.5",
"tailwindcss": "^3.4.3",
Expand Down
8 changes: 8 additions & 0 deletions apps/website/src/@types/i18next.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type Resources from "./resources";

declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "main";
resources: Resources;
}
}
32 changes: 32 additions & 0 deletions apps/website/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
interface Resources {
"main": {
"hooks": {
"useConfirmOnLeave": "You have unsaved changes - are you sure you wish to leave this page?"
},
"components": {
"NavBar": {
"supportEntry": "Support",
"dashboardEntry": "Dashboard",
"accountEntry": "Account"
}
},
"pages": {
"error": {
"errors": {
"NotAuthenticated": "You are not logged in.",
"NotAuthorized": "You are not authorized to access this page.",
"NotFound": "The page you were looking for could not be found.",
"InternalServerError": "An unexpected error occurred."
},
"nav": {
"homeBtn": "Go home",
"tryAgainBtn": "Try again",
"supportBtn": "Get support",
"backBtn": "Go back"
}
}
}
}
}

export default Resources;
36 changes: 36 additions & 0 deletions apps/website/src/app/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Globe2Icon } from "lucide-react";

import { Button } from "@mc/ui/button";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@mc/ui/dropdown-menu";

import { useTranslation } from "~/i18n/client";
import { languageEntries } from "~/i18n/settings";

export function LanguageSelector() {
const [_t, i18n] = useTranslation();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Globe2Icon className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{Object.entries(languageEntries).map(([code, label]) => (
<DropdownMenuCheckboxItem
key={code}
checked={code === i18n.language}
onCheckedChange={() => i18n.changeLanguage(code)}
>
{label}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}
11 changes: 7 additions & 4 deletions apps/website/src/app/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { usePathname } from "next/navigation";

import { cn } from "@mc/ui";

import { useTranslation } from "~/i18n/client";
import { Routes } from "~/other/routes";
import { BotIcon } from "./BotIcon";
import { LanguageSelector } from "./LanguageSelector";

const major = Major_Mono_Display({ subsets: ["latin"], weight: "400" });

export default function NavBar() {
const pathname = usePathname();

const [t] = useTranslation();
return (
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<nav className="container flex h-14 items-center gap-4 text-sm lg:gap-6">
Expand All @@ -36,7 +38,7 @@ export default function NavBar() {
rel="noreferer"
className="text-muted-foreground hover:text-foreground"
>
Support
{t("components.NavBar.supportEntry")}
</a>
<Link
href={Routes.Dashboard}
Expand All @@ -45,7 +47,7 @@ export default function NavBar() {
"hover:text-foreground",
)}
>
Dashboard
{t("components.NavBar.dashboardEntry")}
</Link>
<Link
href={Routes.Account}
Expand All @@ -54,8 +56,9 @@ export default function NavBar() {
"hover:text-foreground",
)}
>
Account
{t("components.NavBar.accountEntry")}
</Link>
<LanguageSelector />
</nav>
</header>
);
Expand Down
46 changes: 23 additions & 23 deletions apps/website/src/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import { useRouter } from "next/navigation";

import { Button } from "@mc/ui/button";

import { useTranslation } from "~/i18n/client";
import { Routes } from "~/other/routes";
import { Errors } from "./errors";

const errorCodes = {
const errorCodes: Record<string, string> = {
[Errors.NotAuthenticated]: "401",
[Errors.NotAuthorized]: "403",
[Errors.NotFound]: "404",
};

const errorMessages = {
[Errors.NotAuthenticated]: "You are not logged in.",
[Errors.NotAuthorized]: "You are not authorized to access this page.",
[Errors.NotFound]: "The page you were looking for could not be found.",
};
[Errors.NotAuthenticated]: "pages.error.errors.NotAuthenticated",
[Errors.NotAuthorized]: "pages.error.errors.NotAuthorized",
[Errors.NotFound]: "pages.error.errors.NotFound",
} as const;

export default function Error({
error,
Expand All @@ -28,23 +29,22 @@ export default function Error({
reset?: () => void;
}) {
const router = useRouter();
const [t] = useTranslation();

let code = "500";
let message = "An unexpected error occurred.";
const digest = error.digest;
const code = errorCodes[error.message] ?? "500";
let message = t("pages.error.errors.InternalServerError");

for (const [errorKey, errorMessage] of Object.entries(errorMessages)) {
if (error.message === errorKey) {
message = errorMessage;
break;
}
}
const digest = error.digest;

for (const [errorKey, errorCode] of Object.entries(errorCodes)) {
if (error.message === errorKey) {
code = errorCode;
break;
}
if (error.message in errorMessages) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const tKey = errorMessages[error.message];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
message = t(tKey);
}

return (
Expand All @@ -59,21 +59,21 @@ export default function Error({
<div className="flex w-full flex-col justify-center gap-2 p-2 sm:flex-row">
<Link className="inline-block" href={Routes.Home}>
<Button variant={"secondary"} className="w-full">
Go home
{t("pages.error.nav.homeBtn")}
</Button>
</Link>
<Link className="inline-block" href={Routes.Support} target="_blank">
<Button variant={"secondary"} className="w-full">
Get support
{t("pages.error.nav.supportBtn")}
</Button>
</Link>
{reset && (
<Button onClick={() => reset()} variant={"secondary"}>
Try again
{t("pages.error.nav.tryAgainBtn")}
</Button>
)}
<Button onClick={() => router.back()} variant={"secondary"}>
Go back
{t("pages.error.nav.backBtn")}
</Button>
</div>
</main>
Expand Down
3 changes: 1 addition & 2 deletions apps/website/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ export const metadata = {
title: "Member Counter",
};

// TODO setup i18next
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<html>
<body
className={`${inter.className} antialiasing dark flex min-h-screen flex-col`}
style={{ backgroundColor: "#0c0a09" }}
Expand Down
16 changes: 0 additions & 16 deletions apps/website/src/hooks/useBreakpoint copy.ts

This file was deleted.

7 changes: 4 additions & 3 deletions apps/website/src/hooks/useConfirmOnLeave.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useCallback, useEffect } from "react";

import { env } from "~/env";
import { useTranslation } from "~/i18n/client";
import { useInterceptAppRouter } from "./useInterceptAppRouter";

function useConfirmOnLeave(shouldConfirm: boolean) {
if (env.NODE_ENV === "development") shouldConfirm = false;
const [t] = useTranslation();

const warningText =
"You have unsaved changes - are you sure you wish to leave this page?";
const warningText = t("hooks.useConfirmOnLeave");

useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
Expand All @@ -34,7 +35,7 @@ function useConfirmOnLeave(shouldConfirm: boolean) {
proceed();
}
},
[shouldConfirm],
[shouldConfirm, warningText],
);

useInterceptAppRouter("back", handleRouterChange);
Expand Down
Loading

0 comments on commit ce99aa2

Please sign in to comment.