diff --git a/components/Layout/index.jsx b/components/Layout/index.jsx
index dfe472b..9290a6c 100644
--- a/components/Layout/index.jsx
+++ b/components/Layout/index.jsx
@@ -7,6 +7,7 @@ import NavBar from '../Navigation';
import MetaBar from '../Metabar';
import SideBar from '../Sidebar';
import Footer from '../Footer';
+import LocaleProvider from '../providers/LocaleProvider';
/**
* @typedef {Object} Props
@@ -20,7 +21,7 @@ import Footer from '../Footer';
* @param {Props} props
*/
export default ({ metadata, headings, readingTime, children }) => (
- <>
+
@@ -40,5 +41,5 @@ export default ({ metadata, headings, readingTime, children }) => (
- >
+
);
diff --git a/components/Navigation/index.jsx b/components/Navigation/index.jsx
index 5a3f369..5a2d7d9 100644
--- a/components/Navigation/index.jsx
+++ b/components/Navigation/index.jsx
@@ -2,23 +2,31 @@ import ThemeToggle from '@node-core/ui-components/Common/ThemeToggle';
import NavBar from '@node-core/ui-components/Containers/NavBar';
import styles from '@node-core/ui-components/Containers/NavBar/index.module.css';
import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub';
-
import SearchBox from '@node-core/doc-kit/src/generators/web/ui/components/SearchBox';
import { useTheme } from '@node-core/doc-kit/src/generators/web/ui/hooks/useTheme.mjs';
+
import { navigation } from '../../site.json' with { type: 'json' };
import Logo from '#theme/Logo';
+import { useLocale } from '../providers/LocaleProvider';
+import { useMemo } from 'preact/hooks';
/**
* NavBar component that displays the headings, search, etc.
*/
export default ({ metadata }) => {
const [themePreference, setThemePreference] = useTheme();
+ const { localizeLink } = useLocale();
+
+ const navigationItems = useMemo(
+ () => navigation.map(item => ({ ...item, link: localizeLink(item.link) })),
+ [localizeLink]
+ );
return (
+ link.replace(new RegExp(`^/${defaultLocale}(?=/|$)`), `/${locale}`);
+
+/**
+ * Detects the locale from the NEXT_LOCALE cookie.
+ * Falls back to the default locale when the cookie is missing.
+ *
+ * @returns {string}
+ */
+export const detectLocaleFromCookies = () => {
+ if (typeof document === 'undefined') {
+ return defaultLocale;
+ }
+
+ const match = document.cookie
+ .split(';')
+ .map(cookie => cookie.trim())
+ .find(cookie => cookie.startsWith(`${LOCALE_COOKIE}=`));
+
+ return match
+ ? decodeURIComponent(match.slice(LOCALE_COOKIE.length + 1))
+ : defaultLocale;
+};
+
+const LocaleContext = createContext({
+ locale: defaultLocale,
+ localizeLink: link => link,
+});
+
+export const useLocale = () => useContext(LocaleContext);
+
+/**
+ * Provides locale and a pre-bound localizeLink fn to the component tree.
+ *
+ * @param {{ locale?: string, children: import('preact').ComponentChildren }} props
+ */
+export default function LocaleProvider({ locale, children }) {
+ const [detectedLocale, setDetectedLocale] = useState(defaultLocale);
+
+ useEffect(() => {
+ setDetectedLocale(locale ?? detectLocaleFromCookies());
+ }, [locale]);
+
+ const value = {
+ locale: detectedLocale,
+ localizeLink: link => localizeLink(link, detectedLocale),
+ };
+
+ return (
+ {children}
+ );
+}
diff --git a/site.json b/site.json
index 2e0e07b..fc9675a 100644
--- a/site.json
+++ b/site.json
@@ -1,11 +1,12 @@
{
+ "defaultLocale": "en",
"navigation": [
{
"link": "/learn",
"text": "Learn"
},
{
- "link": "/about",
+ "link": "/en/about",
"text": "About"
},
{
@@ -13,7 +14,7 @@
"text": "Download"
},
{
- "link": "/blog",
+ "link": "/en/blog",
"text": "Blog"
},
{