The Node.js® Website
1import { useTranslations } from 'next-intl';
2import type { RichTranslationValues } from 'next-intl';
3
4import { siteNavigation } from '@/next.json.mjs';
5import type {
6 FormattedMessage,
7 NavigationEntry,
8 NavigationKeys,
9} from '@/types';
10
11type Context = Record<string, RichTranslationValues>;
12type Navigation = Record<string, NavigationEntry>;
13
14interface MappedNavigationEntry {
15 items: Array<[string, MappedNavigationEntry]>;
16 label: FormattedMessage;
17 link: string;
18}
19
20// Provides Context replacement for variables within the Link. This is also something that is not going
21// to happen in the future with `nodejs/nodejs.dev` codebase
22const replaceLinkWithContext = (
23 link: string,
24 context?: RichTranslationValues
25) =>
26 Object.entries(context || {}).reduce(
27 (finalLink, [find, replace]) =>
28 finalLink.replace(
29 `{${find}}`,
30 typeof replace === 'string' ? replace : ''
31 ),
32 link
33 );
34
35const useSiteNavigation = () => {
36 const t = useTranslations();
37
38 const mapNavigationEntries = (entries: Navigation, context: Context = {}) => {
39 const getFormattedMessage = (label: string, key: string) =>
40 t.rich(label, context[key] || {});
41
42 return Object.entries(entries).map(
43 ([key, { label, link, items }]): [string, MappedNavigationEntry] => [
44 key,
45 {
46 label: label ? getFormattedMessage(label, key) : '',
47 link: link ? replaceLinkWithContext(link, context[key]) : '',
48 items: items ? mapNavigationEntries(items, context) : [],
49 },
50 ]
51 );
52 };
53
54 const getSideNavigation = (
55 keys: Array<NavigationKeys>,
56 context: Context = {}
57 ) => {
58 const navigationEntries: Navigation = keys.reduce(
59 (acc, key) => ({ ...acc, [key]: siteNavigation.sideNavigation[key] }),
60 {}
61 );
62
63 return mapNavigationEntries(navigationEntries, context);
64 };
65
66 const navigationItems = mapNavigationEntries(siteNavigation.topNavigation);
67
68 return { getSideNavigation, navigationItems };
69};
70
71export default useSiteNavigation;