Skip to content

Commit 89541ba

Browse files
committed
i18n: Proof of concept
1 parent 38eb696 commit 89541ba

9 files changed

+123
-3
lines changed

i18n/index.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const en = require("./translations.en.json");
2+
const es = require("./translations.es.json");
3+
4+
const i18n = {
5+
translations: {
6+
en,
7+
es,
8+
},
9+
defaultLang: "en",
10+
useBrowserDefault: true,
11+
languageDataStore: "localStorage",
12+
};
13+
14+
module.exports = i18n;

i18n/translations.en.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"tagline": "An open source Flash Player emulator"
3+
}

i18n/translations.es.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"tagline": "Un emulador de Flash Player de código abierto"
3+
}

package-lock.json

+26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"feed": "^4.2.2",
3939
"gray-matter": "^4.0.3",
4040
"jsdom": "^25.0.1",
41+
"next-export-i18n": "^3.0.0",
4142
"octokit": "^4.0.2",
4243
"postcss": "^8.4.49",
4344
"postcss-preset-mantine": "^1.17.0",

src/app/globals.css

+3
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ a:link,
22
a:visited {
33
color: var(--ruffle-orange-3);
44
}
5+
span[data-language-switcher] {
6+
display: none;
7+
}

src/app/page.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import dynamic from "next/dynamic";
4+
import { useTranslation } from "next-export-i18n";
45
import classes from "./index.module.css";
56
import {
67
Container,
@@ -26,6 +27,7 @@ const Installers = dynamic(() => import("./installers"), {
2627
});
2728

2829
export default function Home() {
30+
const { t } = useTranslation();
2931
const [latest, setLatest] = React.useState<GithubRelease | null>(null);
3032

3133
React.useEffect(() => {
@@ -45,8 +47,8 @@ export default function Home() {
4547
<InteractiveLogo className={classes.logo} />
4648

4749
<Container size="md">
48-
<Title className={classes.title}>
49-
An open source Flash Player emulator
50+
<Title className={classes.title} suppressHydrationWarning>
51+
{t("tagline")}
5052
</Title>
5153
<div className={classes.hero}>
5254
<Image

src/components/header.module.css

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
}
3535
}
3636

37+
.hiddenOnDesktop {
38+
@media (min-width: $mantine-breakpoint-md) {
39+
display: none;
40+
}
41+
}
42+
3743
.burger {
3844
--burger-color: var(--ruffle-orange);
3945
}

src/components/header.tsx

+63-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import classes from "./header.module.css";
66
import Image from "next/image";
77
import Link from "next/link";
88
import { usePathname } from "next/navigation";
9+
import { LanguageSwitcher } from "next-export-i18n";
10+
import React, { useState, useEffect } from "react";
911

1012
const links = [
1113
{ link: "/", label: "About Ruffle" },
@@ -21,8 +23,45 @@ const links = [
2123
target: "_blank",
2224
},
2325
];
26+
const languages: Record<string, string> = {
27+
en: "English",
28+
es: "Español",
29+
};
2430

2531
export function Header() {
32+
const [selectedLang, setSelectedLang] = useState("en");
33+
useEffect(() => {
34+
if (typeof window !== "undefined") {
35+
const browserLang =
36+
window.navigator.language || window.navigator.languages
37+
? (window.navigator.language || window.navigator.languages[0])
38+
.split("-")[0]
39+
.toLowerCase()
40+
: "en";
41+
setSelectedLang(browserLang);
42+
const selectedLang = window.localStorage
43+
? window.localStorage.getItem("next-export-i18n-lang")
44+
: null;
45+
if (selectedLang) {
46+
setSelectedLang(selectedLang);
47+
}
48+
}
49+
}, []);
50+
51+
const handleLanguageChange = (
52+
event: React.ChangeEvent<HTMLSelectElement>,
53+
) => {
54+
const newLang = event.target.value;
55+
setSelectedLang(newLang);
56+
// Trigger the LanguageSwitcher programmatically
57+
const languageSwitcher = document.querySelector(
58+
`[data-language-switcher][aria-label='set language to ${newLang}']`,
59+
) as HTMLElement;
60+
if (languageSwitcher) {
61+
languageSwitcher.click();
62+
}
63+
};
64+
2665
const [opened, { toggle, close }] = useDisclosure(false);
2766
const pathname = usePathname();
2867

@@ -43,7 +82,7 @@ export function Header() {
4382

4483
return (
4584
<header className={classes.header}>
46-
<Container size="md" className={classes.inner}>
85+
<Container size="lg" className={classes.inner}>
4786
<Link href="/">
4887
<Image
4988
src="/logo.svg"
@@ -55,7 +94,19 @@ export function Header() {
5594
</Link>
5695
<Group gap={5} visibleFrom="md">
5796
{items}
97+
<select value={selectedLang} onChange={handleLanguageChange}>
98+
{Object.entries(languages).map(([langCode, langName]) => (
99+
<option key={langCode} value={langCode}>
100+
{langName}
101+
</option>
102+
))}
103+
</select>
58104
</Group>{" "}
105+
{Object.keys(languages).map((langCode) => (
106+
<LanguageSwitcher key={langCode} lang={langCode}>
107+
{languages[langCode]}
108+
</LanguageSwitcher>
109+
))}
59110
<Drawer
60111
opened={opened}
61112
onClose={close}
@@ -69,6 +120,17 @@ export function Header() {
69120
>
70121
{items}
71122
</Drawer>
123+
<select
124+
className={classes.hiddenOnDesktop}
125+
value={selectedLang}
126+
onChange={handleLanguageChange}
127+
>
128+
{Object.entries(languages).map(([langCode, langName]) => (
129+
<option key={langCode} value={langCode}>
130+
{langName}
131+
</option>
132+
))}
133+
</select>
72134
<Burger
73135
opened={opened}
74136
onClick={toggle}

0 commit comments

Comments
 (0)