Skip to content

Commit

Permalink
[web] enable translations #1155 (#1158)
Browse files Browse the repository at this point in the history
* enable translations

* prettier

* enable translations
  • Loading branch information
janavlachova authored Jul 27, 2024
1 parent 5c64e01 commit d01c875
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 51 deletions.
27 changes: 27 additions & 0 deletions .vscode/terminals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"autorun": true,
"autokill": true,
"terminals": [
{
"name": "ROOT",
"description": "For running git commands",
"open": true,
"focus": true,
"commands": ["git fetch -v"]
},
{
"name": "web SERVER",
"description": "For running web dev server",
"open": true,
"focus": false,
"commands": ["cd agdb_web_next", "npm run dev"]
},
{
"name": "web SCRIPTS",
"description": "For running web scripts",
"open": true,
"focus": false,
"commands": ["cd agdb_web_next"]
}
]
}
37 changes: 1 addition & 36 deletions agdb_web_next/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
**Note: This project is still under construction.**
18 changes: 18 additions & 0 deletions agdb_web_next/components/common/link-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Link from "next/link";
import React from "react";
import { useI18n } from "@/hooks/i18n";

type LinkItemProps = {
i18nKey: string;
children?: React.ReactNode;
};

export const LinkItem = ({ i18nKey, children }: LinkItemProps) => {
const { t } = useI18n();
return (
<Link href={t(`url.${i18nKey}`)}>
{t(`link.${i18nKey}`)}
{children}
</Link>
);
};
20 changes: 20 additions & 0 deletions agdb_web_next/components/layout/footer.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.footer {
padding: 1rem;
.copyright {
font-size: 0.75rem;
font-weight: 300;
}
}
.footerLinks {
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
margin: 0 0 1rem 0;
.footerColumn {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 0 1rem;
}
}
16 changes: 16 additions & 0 deletions agdb_web_next/components/layout/footer.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import Footer from "@/components/layout/footer";

vi.mock("@/hooks/i18n", () => ({
useI18n: () => ({
t: (key: string) => key,
}),
}));

describe("footer", () => {
it("should render the footer", () => {
render(<Footer />);
expect(screen.getByText(/copyright/i)).toBeDefined();
});
});
33 changes: 33 additions & 0 deletions agdb_web_next/components/layout/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import styles from "./footer.module.scss";
import { LinkItem } from "../common/link-item";

export default function Footer() {
return (
<footer
className={
styles.footer +
" nx-mx-auto nx-max-w-[90rem] nx-text-gray-600 dark:nx-text-gray-400"
}
>
<div className={styles.footerLinks}>
<div className={styles.footerColumn}>
<LinkItem i18nKey="docs" />
<LinkItem i18nKey="api" />
<LinkItem i18nKey="enterprise" />
</div>
<div className={styles.footerColumn}>
<LinkItem i18nKey="blog" />
<LinkItem i18nKey="about" />
<LinkItem i18nKey="contact" />
</div>
<div className={styles.footerColumn}>
<LinkItem i18nKey="privacy-policy" />
<LinkItem i18nKey="terms-and-conditions" />
</div>
</div>
<div className={styles.copyright}>
Copyright @ {new Date().getFullYear()} agdb
</div>
</footer>
);
}
46 changes: 46 additions & 0 deletions agdb_web_next/hooks/i18n.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, expect, it, vi } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { useI18n } from "@/hooks/i18n";

vi.mock("next/router", () => ({
useRouter: () => ({
locale: "cs-CZ",
}),
}));

vi.mock("nextra/constants", () => ({
DEFAULT_LOCALE: "en-US",
}));

vi.mock("@/messages/cs-CZ.json", () => ({
test: "testCZ",
}));

vi.mock("@/messages/en-US.json", () => ({
test: "testEN",
test2: {
test3: "testEN2",
},
}));

describe("i18n", () => {
it("should return the default locale", () => {
const { result } = renderHook(() => useI18n());
expect(result.current.locale).toBe("cs-CZ");
});

it("should return a translation", async () => {
const { result } = renderHook(() => useI18n());

await waitFor(() => {
expect(result.current.t("test")).toBe("testCZ");
});
});

it("should return a fallback translation", async () => {
const { result } = renderHook(() => useI18n());
await waitFor(() => {
expect(result.current.t("test2.test3")).toBe("testEN2");
});
});
});
56 changes: 56 additions & 0 deletions agdb_web_next/hooks/i18n.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { DEFAULT_LOCALE } from "nextra/constants";

type MessagesStructure = { [key: string]: string | MessagesStructure };

const iterateMessages = (
prefix: string | null,
obj: MessagesStructure,
map: Map<string, string>,
): void => {
for (const [key, value] of Object.entries(obj)) {
const keyName = prefix ? `${prefix}.${key}` : key;
if (typeof value === "string") {
map.set(keyName, value);
} else {
iterateMessages(keyName, value, map);
}
}
};

export const useI18n = () => {
const { locale } = useRouter();
const [fallbackMessages, setFallbackMessages] = useState(
new Map<string, string>(),
);
const [messages, setMessages] = useState(new Map<string, string>());

const defaultLocale = DEFAULT_LOCALE;

useEffect(() => {
import(`../messages/${defaultLocale}.json`)
.then((data) => {
const messages = new Map<string, string>();
iterateMessages(null, data, messages);
setFallbackMessages(messages);
})
.catch(() => setFallbackMessages(new Map<string, string>()));
}, [defaultLocale]);

useEffect(() => {
import(`../messages/${locale}.json`)
.then((data) => {
const messages = new Map<string, string>();
iterateMessages(null, data, messages);
setMessages(messages);
})
.catch(() => setMessages(new Map<string, string>()));
}, [locale]);

const t = (key: string): string => {
return messages.get(key) || fallbackMessages.get(key) || "";
};

return { locale, t };
};
24 changes: 24 additions & 0 deletions agdb_web_next/messages/cs-CZ.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"url": {
"home": "/",
"about": "/o-agdb",
"contact": "/kontakt",
"docs": "/docs",
"blog": "/blog",
"api": "/api",
"enterprise": "/enterprise",
"terms-and-conditions": "/podminky-uziti",
"privacy-policy": "/ochrana-osobnich-udaju"
},
"link": {
"home": "Domů",
"about": "O agdb",
"contact": "Kontakt",
"docs": "Dokumentace",
"blog": "Blog",
"api": "API",
"enterprise": "Enterprise",
"terms-and-conditions": "Podmínky užití",
"privacy-policy": "Ochrana osobních údajů"
}
}
24 changes: 24 additions & 0 deletions agdb_web_next/messages/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"url": {
"home": "/",
"about": "/about",
"contact": "/contact",
"docs": "/docs",
"blog": "/blog",
"api": "/api",
"enterprise": "/enterprise",
"terms-and-conditions": "/terms-and-conditions",
"privacy-policy": "/privacy-policy"
},
"link": {
"home": "Home",
"about": "About",
"contact": "Contact",
"docs": "Docs",
"blog": "Blog",
"api": "API",
"enterprise": "Enterprise",
"terms-and-conditions": "Terms and Conditions",
"privacy-policy": "Privacy Policy"
}
}
1 change: 1 addition & 0 deletions agdb_web_next/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
import withNextra from "nextra";

const nextConfig = {
i18n: {
locales: ["en-US", "cs-CZ"],
Expand Down
Loading

0 comments on commit d01c875

Please sign in to comment.