Skip to content

Commit 252f8cc

Browse files
authored
Internationalization improvements for the website (#7515)
2 parents c4e5494 + e1bb704 commit 252f8cc

File tree

20 files changed

+392
-194
lines changed

20 files changed

+392
-194
lines changed

apps/website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"scripts": {
66
"dev": "vite",
77
"build": "vite build",
8+
"test": "vitest",
89
"preview": "pnpm build && vite preview"
910
},
1011
"dependencies": {

apps/website/public/translations/en/translation.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"web_clipper_content": "Grab web pages (or screenshots) and place them directly into Trilium using the web clipper browser extension."
4040
},
4141
"note_types": {
42+
"title": "Multiple ways to represent your information",
4243
"text_title": "Text notes",
4344
"text_description": "The notes are edited using a visual (WYSIWYG) editor, with support for tables, images, math expressions, code blocks with syntax highlighting. Quickly format the text using Markdown-like syntax or using slash commands.",
4445
"code_title": "Code notes",
@@ -65,6 +66,7 @@
6566
"api_description": "Interact with Trilium programatically using its builtin REST API."
6667
},
6768
"collections": {
69+
"title": "Collections",
6870
"calendar_title": "Calendar",
6971
"calendar_description": "Organize your personal or professional events using a calendar, with support for all-day and multi-day events. See your events at a glance with the week, month and year views. Easy interaction to add or drag events.",
7072
"table_title": "Table",
@@ -106,6 +108,11 @@
106108
"linux_small": "for Linux",
107109
"more_platforms": "More platforms & server setup"
108110
},
111+
"header": {
112+
"get-started": "Get started",
113+
"documentation": "Documentation",
114+
"support-us": "Support us"
115+
},
109116
"footer": {
110117
"copyright_and_the": " and the ",
111118
"copyright_community": "community"

apps/website/public/translations/ro/translation.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@
106106
"linux_small": "pentru Linux",
107107
"more_platforms": "Mai multe platforme și instalarea server-ului"
108108
},
109+
"header": {
110+
"get-started": "Primii pași",
111+
"documentation": "Documentație",
112+
"support-us": "Sprijină-ne"
113+
},
109114
"footer": {
110115
"copyright_and_the": " și ",
111116
"copyright_community": "comunitatea"

apps/website/src/components/Card.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ComponentChildren, HTMLAttributes } from "preact";
22
import { Link } from "./Button.js";
33
import Icon from "./Icon.js";
4-
import { t } from "../i18n.js";
4+
import { useTranslation } from "react-i18next";
55

66
interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
77
title: ComponentChildren;
@@ -13,6 +13,8 @@ interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
1313
}
1414

1515
export default function Card({ title, children, imageUrl, iconSvg, className, moreInfoUrl, ...restProps }: CardProps) {
16+
const { t } = useTranslation();
17+
1618
return (
1719
<div className={`card ${className}`} {...restProps}>
1820
{imageUrl && <img class="image" src={imageUrl} loading="lazy" />}

apps/website/src/components/DownloadButton.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ import "./DownloadButton.css";
33
import Button from "./Button.js";
44
import downloadIcon from "../assets/boxicons/bx-arrow-in-down-square-half.svg?raw";
55
import packageJson from "../../../../package.json" with { type: "json" };
6-
import { useEffect, useState } from "preact/hooks";
7-
import { t } from "../i18n.js";
6+
import { useContext, useEffect, useState } from "preact/hooks";
7+
import { useTranslation } from "react-i18next";
8+
import { LocaleContext } from "../index.js";
89

910
interface DownloadButtonProps {
1011
big?: boolean;
1112
}
1213

1314
export default function DownloadButton({ big }: DownloadButtonProps) {
15+
const locale = useContext(LocaleContext);
16+
const { t } = useTranslation();
1417
const [ recommendedDownload, setRecommendedDownload ] = useState<RecommendedDownload | null>();
1518
useEffect(() => {
16-
getRecommendedDownload()?.then(setRecommendedDownload);
17-
}, []);
19+
getRecommendedDownload(t)?.then(setRecommendedDownload);
20+
}, [ t ]);
1821

1922
return (recommendedDownload &&
2023
<>
@@ -35,7 +38,7 @@ export default function DownloadButton({ big }: DownloadButtonProps) {
3538
) : (
3639
<Button
3740
className={`download-button desktop-only ${big ? "big" : ""}`}
38-
href="/get-started/"
41+
href={`/${locale}/get-started/`}
3942
iconSvg={downloadIcon}
4043
text={<>
4144
{t("download_now.text")}

apps/website/src/components/Footer.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,26 @@ footer {
55
color: var(--muted-color);
66
font-size: 0.8em;
77

8-
.content-wrapper {
8+
.row {
99
display: flex;
1010
justify-content: space-between;
1111
align-items: center;
1212
flex-direction: column-reverse;
1313
gap: 2em;
14+
margin-bottom: 1em;
1415

1516
@media (min-width: 720px) {
1617
flex-direction: row;
1718
}
1819
}
20+
21+
nav.languages {
22+
flex-grow: 1;
23+
justify-content: center;
24+
flex-wrap: wrap;
25+
display: flex;
26+
gap: 0.5em 1em;
27+
}
1928
}
2029

2130
.social-buttons {

apps/website/src/components/Footer.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,46 @@ import githubDiscussionsIcon from "../assets/boxicons/bx-discussion.svg?raw";
55
import matrixIcon from "../assets/boxicons/bx-message-dots.svg?raw";
66
import redditIcon from "../assets/boxicons/bx-reddit.svg?raw";
77
import { Link } from "./Button.js";
8-
import { t } from "../i18n";
8+
import { LOCALES, swapLocaleInUrl } from "../i18n";
9+
import { useTranslation } from "react-i18next";
10+
import { useLocation } from "preact-iso";
11+
import { useContext } from "preact/hooks";
12+
import { LocaleContext } from "..";
913

1014
export default function Footer() {
15+
const { t } = useTranslation();
16+
const { url } = useLocation();
17+
const currentLocale = useContext(LocaleContext);
18+
1119
return (
1220
<footer>
1321
<div class="content-wrapper">
14-
<div class="footer-text">
15-
© 2024-2025 <Link href="https://github.com/eliandoran" openExternally>Elian Doran</Link>{t("footer.copyright_and_the")}<Link href="https://github.com/TriliumNext/Trilium/graphs/contributors" openExternally>{t("footer.copyright_community")}</Link>.<br />
16-
© 2017-2024 <Link href="https://github.com/zadam" openExternally>zadam</Link>.
22+
<div class="row">
23+
<div class="footer-text">
24+
© 2024-2025 <Link href="https://github.com/eliandoran" openExternally>Elian Doran</Link>{t("footer.copyright_and_the")}<Link href="https://github.com/TriliumNext/Trilium/graphs/contributors" openExternally>{t("footer.copyright_community")}</Link>.<br />
25+
© 2017-2024 <Link href="https://github.com/zadam" openExternally>zadam</Link>.
26+
</div>
27+
28+
<SocialButtons />
1729
</div>
1830

19-
<SocialButtons />
31+
<div class="row">
32+
<nav class="languages">
33+
{LOCALES.map(locale => (
34+
locale.id !== currentLocale
35+
? <Link href={swapLocaleInUrl(url, locale.id)}>{locale.name}</Link>
36+
: <span className="active">{locale.name}</span>
37+
))}
38+
</nav>
39+
</div>
2040
</div>
2141
</footer>
2242
)
2343
}
2444

2545
export function SocialButtons({ className, withText }: { className?: string, withText?: boolean }) {
46+
const { t } = useTranslation();
47+
2648
return (
2749
<div className={`social-buttons ${className}`}>
2850
<SocialButton

apps/website/src/components/Header.tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,43 @@
11
import "./Header.css";
22
import { Link } from "./Button.js";
33
import { SocialButtons, SocialButton } from "./Footer.js";
4-
import { useEffect, useMemo, useState } from "preact/hooks";
4+
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
55
import { useLocation } from 'preact-iso';
66
import DownloadButton from './DownloadButton.js';
77
import githubIcon from "../assets/boxicons/bx-github.svg?raw";
88
import Icon from "./Icon.js";
99
import logoPath from "../assets/icon-color.svg";
1010
import menuIcon from "../assets/boxicons/bx-menu.svg?raw";
11+
import { LocaleContext } from "..";
12+
import { useTranslation } from "react-i18next";
13+
import { swapLocaleInUrl } from "../i18n";
1114

1215
interface HeaderLink {
1316
url: string;
1417
text: string;
1518
external?: boolean;
1619
}
1720

18-
const HEADER_LINKS: HeaderLink[] = [
19-
{ url: "/get-started/", text: "Get started" },
20-
{ url: "https://docs.triliumnotes.org/", text: "Documentation", external: true },
21-
{ url: "/support-us/", text: "Support us" }
22-
]
23-
2421
export function Header(props: {repoStargazersCount: number}) {
2522
const { url } = useLocation();
23+
const { t } = useTranslation();
24+
const locale = useContext(LocaleContext);
2625
const [ mobileMenuShown, setMobileMenuShown ] = useState(false);
2726

27+
const [ headerLinks, setHeaderLinks ] = useState<HeaderLink[]>([]);
28+
useEffect(() => {
29+
setHeaderLinks([
30+
{ url: "/get-started", text: t("header.get-started") },
31+
{ url: "https://docs.triliumnotes.org/", text: t("header.documentation"), external: true },
32+
{ url: "/support-us", text: t("header.support-us") }
33+
]);
34+
}, [ locale, t ]);
35+
2836
return (
2937
<header>
3038
<div class="content-wrapper">
3139
<div class="first-row">
32-
<a class="banner" href="/">
40+
<a class="banner" href={`/${locale}/`}>
3341
<img src={logoPath} width="300" height="300" alt="Trilium Notes logo" />&nbsp;<span>Trilium Notes</span>
3442
</a>
3543

@@ -46,16 +54,17 @@ export function Header(props: {repoStargazersCount: number}) {
4654
</div>
4755

4856
<nav className={`${mobileMenuShown ? "mobile-shown" : ""}`}>
49-
{HEADER_LINKS.map(link => (
50-
<Link
51-
href={link.url}
52-
className={url === link.url ? "active" : ""}
57+
{headerLinks.map(link => {
58+
const linkHref = link.external ? link.url : swapLocaleInUrl(link.url, locale);
59+
return (<Link
60+
href={linkHref}
61+
className={url === linkHref ? "active" : ""}
5362
openExternally={link.external}
5463
onClick={() => {
5564
setMobileMenuShown(false);
5665
}}
57-
>{link.text}</Link>
58-
))}
66+
>{link.text}</Link>)
67+
})}
5968

6069
<SocialButtons className="mobile-only" withText />
6170
</nav>
@@ -74,4 +83,4 @@ export function Header(props: {repoStargazersCount: number}) {
7483
</div>
7584
</header>
7685
);
77-
}
86+
}

0 commit comments

Comments
 (0)