The Node.js® Website

meta: adopt next-intl and app router (#6092)

* meta: adopt next-intl and app router

code: kept working on adoption of i18n

feat: app router transition

meta: renamed sections to containers due to fs casing

fix: force static

fix: minor fixes of build and caching

feat: metadata of each page

fix: tests and stuff

fix: tiny type fix

chore: updated link to guide

chore: intl on stories

chore: no usage of assert

refactor: some more cleanups on codebase

meta: some improvements and fixes

refactor: more code review changes

refactor: more standardisation next-intl

chore: minor changes to 404

refactor: code cleanup and reusability

fix: fixed tests and imports

chore: minor fixes and test updates

fix: sitemap generation and 404 ignore

chore: cleanup activelocalizedlink

chore: remove legacy sto-top

chore: more cleanups

chore: more cleanups

chore: use right import

chore: optimise generation of blog meta

chore: small cleanup

chore: updated collaborator guide

meta: provide server and client version of hooks

fix: tests imports

fix: styles of blogcard

* fix: fix eslint rules

---------

Co-authored-by: Claudio Wunder <cwunder@hubspot.com>

authored by Guilherme Araújo Claudio Wunder and committed by GitHub b5114799 10fb8784

Changed files
+4814 -3963
.storybook
app
components
hooks
i18n
layouts
next-data
pages
en
about
blog
advisory-board
announcements
community
feature
module
npm
release
uncategorized
video
vulnerability
weekly-updates
download
providers
styles
types
util
+4 -1
.eslintrc.json
··· 59 { 60 "files": ["**/*.md?(x)"], 61 "extends": "plugin:mdx/recommended", 62 - "rules": { "react/jsx-no-undef": "off" } 63 }, 64 { 65 "files": ["**/*.{mdx,tsx}"],
··· 59 { 60 "files": ["**/*.md?(x)"], 61 "extends": "plugin:mdx/recommended", 62 + "rules": { 63 + "react/jsx-no-undef": "off", 64 + "@next/next/no-img-element": "off" 65 + } 66 }, 67 { 68 "files": ["**/*.{mdx,tsx}"],
+22 -21
.storybook/preview.tsx
··· 1 - import NextImage from 'next/image'; 2 import classNames from 'classnames'; 3 import { withThemeByDataAttribute } from '@storybook/addon-themes'; 4 - import { SiteProvider } from '../providers/siteProvider'; 5 - import { LocaleProvider } from '../providers/localeProvider'; 6 - import { NotificationProvider } from '../providers/notificationProvider'; 7 - import * as constants from './constants'; 8 import type { Preview, ReactRenderer } from '@storybook/react'; 9 10 import '../styles/new/index.css'; 11 12 const rootClasses = classNames( 13 - constants.OPEN_SANS_FONT.variable, 14 - constants.IBM_PLEX_MONO_FONT.variable, 15 'font-open-sans' 16 ); 17 18 const preview: Preview = { 19 parameters: { 20 nextjs: { router: { basePath: '' } }, 21 - chromatic: { modes: constants.STORYBOOK_MODES }, 22 - viewport: { 23 - defaultViewport: 'large', 24 - viewports: constants.STORYBOOK_SIZES, 25 - }, 26 }, 27 // These are extra Storybook Decorators applied to all stories 28 // that introduce extra functionality such as Theme Switching 29 // and all the App's Providers (Site, Theme, Locale) 30 decorators: [ 31 Story => ( 32 - <SiteProvider> 33 - <LocaleProvider> 34 - <NotificationProvider viewportClassName="absolute top-0 left-0 list-none"> 35 - <div className={rootClasses}> 36 - <Story /> 37 - </div> 38 - </NotificationProvider> 39 - </LocaleProvider> 40 - </SiteProvider> 41 ), 42 withThemeByDataAttribute<ReactRenderer>({ 43 themes: {
··· 1 import classNames from 'classnames'; 2 + import { NextIntlClientProvider } from 'next-intl'; 3 + 4 import { withThemeByDataAttribute } from '@storybook/addon-themes'; 5 + import { NotificationProvider } from '@/providers/notificationProvider'; 6 + import { 7 + OPEN_SANS_FONT, 8 + IBM_PLEX_MONO_FONT, 9 + STORYBOOK_MODES, 10 + STORYBOOK_SIZES, 11 + } from '@/.storybook/constants'; 12 import type { Preview, ReactRenderer } from '@storybook/react'; 13 14 + import englishLocale from '@/i18n/locales/en.json'; 15 + 16 import '../styles/new/index.css'; 17 18 const rootClasses = classNames( 19 + OPEN_SANS_FONT.variable, 20 + IBM_PLEX_MONO_FONT.variable, 21 'font-open-sans' 22 ); 23 24 const preview: Preview = { 25 parameters: { 26 nextjs: { router: { basePath: '' } }, 27 + chromatic: { modes: STORYBOOK_MODES }, 28 + viewport: { defaultViewport: 'large', viewports: STORYBOOK_SIZES }, 29 }, 30 // These are extra Storybook Decorators applied to all stories 31 // that introduce extra functionality such as Theme Switching 32 // and all the App's Providers (Site, Theme, Locale) 33 decorators: [ 34 Story => ( 35 + <NextIntlClientProvider locale="en" messages={englishLocale}> 36 + <NotificationProvider viewportClassName="absolute top-0 left-0 list-none"> 37 + <div className={rootClasses}> 38 + <Story /> 39 + </div> 40 + </NotificationProvider> 41 + </NextIntlClientProvider> 42 ), 43 withThemeByDataAttribute<ReactRenderer>({ 44 themes: {
+8 -9
COLLABORATOR_GUIDE.md
··· 80 - [Tailwind][] is used as our CSS Framework and the Foundation of our Design System 81 - [Hero Icons](https://heroicons.com/) is an SVG Icon Library used within our Codebase 82 - [Radix UI][] is a collection of customizable UI components 83 - - [Shiki][] is a Syntax Highlighter used for our Codeboxes 84 - - A [Rehype Plugin](https://rehype-pretty-code.netlify.app/) is used here for transforming `pre` and `code` tags into Syntax Highlighted Codeboxes 85 - [MDX][] and Markdown are used for structuring the Content of the Website 86 - [`react-intl`][] is the i18n Library adopted within the Website 87 - [`next-sitemap`](https://www.npmjs.com/package/next-sitemap) is used for Sitemap and `robots.txt` Generation ··· 442 Besides that, MDX is also a pluggable parser built on top of `unified` which supports Rehype and Remark Plugins. 443 MDX is becoming the standard for parsing human-content on React/Next.js-based Applications. 444 445 - Some of the plugins that we use include: 446 447 - `remark-headings`: Generates Metadata for Markdown Headings 448 - This allows us to build the Table of Contents for each Page, for example. 449 - `rehype-autolink-headings`: Allows us to add Anchor Links to Markdown Headings 450 - `rehype-slug`: Allows us to add IDs to Markdown Headings 451 - - `rehype-pretty-code`: Allows us to transform `pre` and `code` tags into Syntax Highlighted Codeboxes by using [Shiki][] 452 453 - #### Syntax Highlighting (Shiki) and Vercel 454 455 - Since we use Incremental Static Rendering and Serverless Functions, Vercel attempts to simplify the bundled Node.js runtime by removing all unnecessary dependencies. 456 - This means that Shiki's Themes and Languages are not bundled by default. 457 458 - Hence the `shiki.config.mjs` file, where we define our custom set of supported Languages and we bundle them directly by using [Shiki's Grammar Property](https://github.com/shikijs/shiki/blob/main/docs/languages.md#supporting-your-own-languages-with-shiki) which allows us to embed the languages directly. 459 460 ### Vercel 461 ··· 496 [MDX]: https://mdxjs.com/ 497 [PostCSS]: https://postcss.org/ 498 [React]: https://react.dev/ 499 - [Shiki]: https://github.com/shikijs/shiki 500 [Tailwind]: https://tailwindcss.com/ 501 [Radix UI]: https://www.radix-ui.com/
··· 80 - [Tailwind][] is used as our CSS Framework and the Foundation of our Design System 81 - [Hero Icons](https://heroicons.com/) is an SVG Icon Library used within our Codebase 82 - [Radix UI][] is a collection of customizable UI components 83 + - [Shikiji][] is a Syntax Highlighter used for our Codeboxes 84 + - The syntax highlighting is done within the processing of the Markdown files with the MDX compiler as a Rehype plugin. 85 - [MDX][] and Markdown are used for structuring the Content of the Website 86 - [`react-intl`][] is the i18n Library adopted within the Website 87 - [`next-sitemap`](https://www.npmjs.com/package/next-sitemap) is used for Sitemap and `robots.txt` Generation ··· 442 Besides that, MDX is also a pluggable parser built on top of `unified` which supports Rehype and Remark Plugins. 443 MDX is becoming the standard for parsing human-content on React/Next.js-based Applications. 444 445 + **Some of the plugins that we use include:** 446 447 + - `remark-gfm`: Allows us to bring GitHub Flavoured Markdown within MDX 448 - `remark-headings`: Generates Metadata for Markdown Headings 449 - This allows us to build the Table of Contents for each Page, for example. 450 - `rehype-autolink-headings`: Allows us to add Anchor Links to Markdown Headings 451 - `rehype-slug`: Allows us to add IDs to Markdown Headings 452 453 + #### Syntax Highlighting (Shikiji) and Vercel 454 455 + We use [Shikiji][] which is a refactor of the famous [Shiki](https://github.com/shikijs/shiki) syntax highlighter in ESM. We use it to support our native ESM-nature, and since Shiki is incompatible on serverless environments and Edge functions due of the need of Node's `fs`. Shikiji is definitely a nice port/rewrite of Shiki which supports our needs. 456 457 + Shikiji is integrated on our workflow as a Reype Plugin, see the `next.mdx.shiki.mjs` file. We also use the `nord` theme for Shikiji and a subset of the supported languages as defined on the `shiki.config.mjs` file. 458 459 ### Vercel 460 ··· 495 [MDX]: https://mdxjs.com/ 496 [PostCSS]: https://postcss.org/ 497 [React]: https://react.dev/ 498 + [Shikiji]: https://github.com/antfu/shikiji 499 [Tailwind]: https://tailwindcss.com/ 500 [Radix UI]: https://www.radix-ui.com/
+119
app/[locale]/[[...path]]/page.tsx
···
··· 1 + import { notFound } from 'next/navigation'; 2 + import { unstable_setRequestLocale } from 'next-intl/server'; 3 + import type { FC } from 'react'; 4 + 5 + import { setClientContext } from '@/client-context'; 6 + import { MDXRenderer } from '@/components/mdxRenderer'; 7 + import { WithLayout } from '@/components/withLayout'; 8 + import { DEFAULT_VIEWPORT, ENABLE_STATIC_EXPORT } from '@/next.constants.mjs'; 9 + import { dynamicRouter } from '@/next.dynamic.mjs'; 10 + import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs'; 11 + import { MatterProvider } from '@/providers/matterProvider'; 12 + 13 + type DynamicStaticPaths = { path: string[]; locale: string }; 14 + type DynamicParams = { params: DynamicStaticPaths }; 15 + 16 + // This is the default Viewport Metadata 17 + // @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport 18 + export const viewport = DEFAULT_VIEWPORT; 19 + 20 + // This generates each page's HTML Metadata 21 + // @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata 22 + export const generateMetadata = async (c: DynamicParams) => { 23 + const { path = [], locale = defaultLocale.code } = c.params; 24 + 25 + const pathname = dynamicRouter.getPathname(path); 26 + 27 + // Retrieves and rewriting rule if the pathname matches any rule 28 + const [, rewriteRule] = dynamicRouter.getRouteRewrite(pathname); 29 + 30 + return dynamicRouter.getPageMetadata( 31 + locale, 32 + rewriteRule ? rewriteRule(pathname) : pathname 33 + ); 34 + }; 35 + 36 + // This provides all the possible paths that can be generated statically 37 + // + provides all the paths that we support on the Node.js Website 38 + export const generateStaticParams = async () => { 39 + const paths: DynamicStaticPaths[] = []; 40 + 41 + // We don't need to compute all possible paths on regular builds 42 + // as we benefit from Next.js's ISR (Incremental Static Regeneration) 43 + if (!ENABLE_STATIC_EXPORT) { 44 + return []; 45 + } 46 + 47 + for (const locale of availableLocaleCodes) { 48 + const routesForLanguage = await dynamicRouter.getRoutesByLanguage(locale); 49 + 50 + const mappedRoutesWithLocale = routesForLanguage.map(pathname => 51 + dynamicRouter.mapPathToRoute(locale, pathname) 52 + ); 53 + 54 + paths.push(...mappedRoutesWithLocale); 55 + } 56 + 57 + return paths.sort(); 58 + }; 59 + 60 + // This method parses the current pathname and does any sort of modifications needed on the route 61 + // then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component 62 + // finally it returns (if the locale and route are valid) the React Component with the relevant context 63 + // and attached context providers for rendering the current page 64 + const getPage: FC<DynamicParams> = async ({ params }) => { 65 + const { path = [], locale = defaultLocale.code } = params; 66 + 67 + if (!availableLocaleCodes.includes(locale)) { 68 + // Forces the current locale to be the Default Locale 69 + unstable_setRequestLocale(defaultLocale.code); 70 + 71 + return notFound(); 72 + } 73 + 74 + // Configures the current Locale to be the given Locale of the Request 75 + unstable_setRequestLocale(locale); 76 + 77 + const pathname = dynamicRouter.getPathname(path); 78 + 79 + if (dynamicRouter.shouldIgnoreRoute(pathname)) { 80 + return notFound(); 81 + } 82 + 83 + // Retrieves and rewriting rule if the pathname matches any rule 84 + const [, rewriteRule] = dynamicRouter.getRouteRewrite(pathname); 85 + 86 + // We retrieve the source of the Markdown file by doing an educated guess 87 + // of what possible files could be the source of the page, since the extension 88 + // context is lost from `getStaticProps` as a limitation of Next.js itself 89 + const { source, filename } = await dynamicRouter.getMarkdownFile( 90 + locale, 91 + rewriteRule ? rewriteRule(pathname) : pathname 92 + ); 93 + 94 + if (source.length && filename.length) { 95 + // This parses the source Markdown content and returns a React Component and 96 + // relevant context from the Markdown File 97 + const { MDXContent, frontmatter, headings } = 98 + await dynamicRouter.getMDXContent(source, filename); 99 + 100 + // Defines a shared Server Context for the Client-Side 101 + // That is shared for all pages under the dynamic router 102 + setClientContext({ frontmatter, headings, pathname }); 103 + 104 + return ( 105 + <MatterProvider matter={frontmatter} headings={headings}> 106 + <WithLayout layout={frontmatter.layout}> 107 + <MDXRenderer Component={MDXContent} /> 108 + </WithLayout> 109 + </MatterProvider> 110 + ); 111 + } 112 + 113 + return notFound(); 114 + }; 115 + 116 + // Enforce that all these routes are compatible with SSR 117 + export const dynamic = 'error'; 118 + 119 + export default getPage;
+3
app/[locale]/not-found.tsx
···
··· 1 + import NotFound from '@/app/not-found'; 2 + 3 + export default NotFound;
+2 -1
app/en/feed/[feed]/route.ts app/[locale]/feed/[feed]/route.ts
··· 2 3 import { generateWebsiteFeeds } from '@/next.data.mjs'; 4 import { blogData } from '@/next.json.mjs'; 5 6 // loads all the data from the blog-posts-data.json file 7 const websiteFeeds = generateWebsiteFeeds(blogData); ··· 27 // Note that differently from the App Router these don't get built at the build time 28 // only if the export is already set for static export 29 export const generateStaticParams = () => 30 - [...websiteFeeds.keys()].map(feed => ({ feed })); 31 32 // Enforces that this route is used as static rendering 33 // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
··· 2 3 import { generateWebsiteFeeds } from '@/next.data.mjs'; 4 import { blogData } from '@/next.json.mjs'; 5 + import { defaultLocale } from '@/next.locales.mjs'; 6 7 // loads all the data from the blog-posts-data.json file 8 const websiteFeeds = generateWebsiteFeeds(blogData); ··· 28 // Note that differently from the App Router these don't get built at the build time 29 // only if the export is already set for static export 30 export const generateStaticParams = () => 31 + [...websiteFeeds.keys()].map(feed => ({ feed, locale: defaultLocale.code })); 32 33 // Enforces that this route is used as static rendering 34 // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
+42
app/layout.tsx
···
··· 1 + import { Analytics } from '@vercel/analytics/react'; 2 + import { Source_Sans_3 } from 'next/font/google'; 3 + import { useLocale } from 'next-intl'; 4 + import type { FC, PropsWithChildren } from 'react'; 5 + 6 + import BaseLayout from '@/layouts/BaseLayout'; 7 + import { VERCEL_ENV } from '@/next.constants.mjs'; 8 + import { availableLocalesMap, defaultLocale } from '@/next.locales.mjs'; 9 + import { LocaleProvider } from '@/providers/localeProvider'; 10 + import { ThemeProvider } from '@/providers/themeProvider'; 11 + 12 + import '@/styles/old/index.css'; 13 + 14 + const sourceSans = Source_Sans_3({ 15 + weight: ['400', '600'], 16 + display: 'fallback', 17 + subsets: ['latin'], 18 + }); 19 + 20 + const RootLayout: FC<PropsWithChildren> = ({ children }) => { 21 + const locale = useLocale(); 22 + 23 + const { langDir, hrefLang } = availableLocalesMap[locale] || defaultLocale; 24 + 25 + return ( 26 + <html className={sourceSans.className} dir={langDir} lang={hrefLang}> 27 + <body> 28 + <LocaleProvider> 29 + <ThemeProvider> 30 + <BaseLayout>{children}</BaseLayout> 31 + </ThemeProvider> 32 + </LocaleProvider> 33 + 34 + {VERCEL_ENV && <Analytics />} 35 + 36 + <a rel="me" href="https://social.lfx.dev/@nodejs" /> 37 + </body> 38 + </html> 39 + ); 40 + }; 41 + 42 + export default RootLayout;
+18
app/not-found.tsx
···
··· 1 + import { useTranslations } from 'next-intl'; 2 + import type { FC } from 'react'; 3 + 4 + const NotFound: FC = () => { 5 + const t = useTranslations(); 6 + 7 + return ( 8 + <div className="container"> 9 + <h2>{t('pages.404.title')}</h2> 10 + <h3>{t('pages.404.description')}</h3> 11 + </div> 12 + ); 13 + }; 14 + 15 + // This is a fallback Not Found Page that in theory should never be requested 16 + export const dynamic = 'force-dynamic'; 17 + 18 + export default NotFound;
+10 -16
app/sitemap.ts
··· 1 import type { MetadataRoute } from 'next'; 2 3 import { 4 - STATIC_ROUTES_IGNORES, 5 - DYNAMIC_GENERATED_ROUTES, 6 BASE_PATH, 7 BASE_URL, 8 EXTERNAL_LINKS_SITEMAP, 9 } from '@/next.constants.mjs'; 10 - import { allPaths } from '@/next.dynamic.mjs'; 11 - import { defaultLocale } from '@/next.locales.mjs'; 12 13 // This is the combination of the Application Base URL and Base PATH 14 const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; ··· 16 // This allows us to generate a `sitemap.xml` file dynamically based on the needs of the Node.js Website 17 // Next.js Sitemap Generation doesn't support `alternate` refs yet 18 // @see https://github.com/vercel/next.js/discussions/55646 19 - const sitemap = (): MetadataRoute.Sitemap => { 20 - // Retrieves all the dynamic generated paths 21 - const dynamicRoutes = DYNAMIC_GENERATED_ROUTES(); 22 23 - // Retrieves all the static paths for the default locale (English) 24 - // and filter out the routes that should be ignored 25 - const staticPaths = [...allPaths.get(defaultLocale.code)!] 26 - .filter(route => STATIC_ROUTES_IGNORES.every(e => !e(route))) 27 - .map(route => route.routeWithLocale); 28 29 - // The current date of this request 30 const currentDate = new Date().toISOString(); 31 32 - const appRoutes = [...dynamicRoutes, ...staticPaths] 33 - .sort() 34 - .map(route => `${baseUrlAndPath}/${route}`); 35 36 return [...appRoutes, ...EXTERNAL_LINKS_SITEMAP].map(route => ({ 37 url: route,
··· 1 import type { MetadataRoute } from 'next'; 2 3 import { 4 BASE_PATH, 5 BASE_URL, 6 EXTERNAL_LINKS_SITEMAP, 7 } from '@/next.constants.mjs'; 8 + import { dynamicRouter } from '@/next.dynamic.mjs'; 9 + import { availableLocaleCodes } from '@/next.locales.mjs'; 10 11 // This is the combination of the Application Base URL and Base PATH 12 const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; ··· 14 // This allows us to generate a `sitemap.xml` file dynamically based on the needs of the Node.js Website 15 // Next.js Sitemap Generation doesn't support `alternate` refs yet 16 // @see https://github.com/vercel/next.js/discussions/55646 17 + const sitemap = async (): Promise<MetadataRoute.Sitemap> => { 18 + const paths: string[] = []; 19 20 + for (const locale of availableLocaleCodes) { 21 + const routesForLanguage = await dynamicRouter.getRoutesByLanguage(locale); 22 + 23 + paths.push(...routesForLanguage.map(route => `${locale}/${route}`)); 24 + } 25 26 const currentDate = new Date().toISOString(); 27 28 + const appRoutes = paths.sort().map(route => `${baseUrlAndPath}/${route}`); 29 30 return [...appRoutes, ...EXTERNAL_LINKS_SITEMAP].map(route => ({ 31 url: route,
+24
client-context.ts
···
··· 1 + import { cache } from 'react'; 2 + 3 + import type { ClientSharedServerContext } from './types'; 4 + 5 + // This allows us to have Server-Side Context's of the shared "contextual" data 6 + // which includes the frontmatter, the current pathname from the dynamic segments 7 + // and the current headings of the current markdown context 8 + export const getClientContext = cache(() => { 9 + const serverSharedContext: ClientSharedServerContext = { 10 + frontmatter: {}, 11 + pathname: '', 12 + headings: [], 13 + }; 14 + 15 + return serverSharedContext; 16 + }); 17 + 18 + // This is used by the dynamic router to define on the request 19 + // the current set of information we use (shared) 20 + export const setClientContext = (data: ClientSharedServerContext) => { 21 + getClientContext().frontmatter = data.frontmatter; 22 + getClientContext().pathname = data.pathname; 23 + getClientContext().headings = data.headings; 24 + };
+21 -28
components/Common/ActiveLocalizedLink/__tests__/index.test.mjs
··· 1 import { render, screen } from '@testing-library/react'; 2 - import { IntlProvider } from 'react-intl'; 3 4 import ActiveLocalizedLink from '..'; 5 6 describe('ActiveLocalizedLink', () => { 7 it('renders as localized link', () => { 8 render( 9 - <IntlProvider locale="en" onError={() => {}}> 10 - <ActiveLocalizedLink 11 - className="link" 12 - activeClassName="active" 13 - href="/link" 14 - > 15 - Link 16 - </ActiveLocalizedLink> 17 - </IntlProvider> 18 ); 19 20 expect(screen.findByText('Link')).resolves.toHaveAttribute( ··· 25 26 it('ignores active class when href not matches location.href', () => { 27 render( 28 - <IntlProvider locale="en" onError={() => {}}> 29 - <ActiveLocalizedLink 30 - className="link" 31 - activeClassName="active" 32 - href="/not-link" 33 - > 34 - Link 35 - </ActiveLocalizedLink> 36 - </IntlProvider> 37 ); 38 39 expect(screen.findByText('Link')).resolves.toHaveAttribute('class', 'link'); ··· 41 42 it('sets active class when href matches location.href', () => { 43 render( 44 - <IntlProvider locale="en" onError={() => {}}> 45 - <ActiveLocalizedLink 46 - className="link" 47 - activeClassName="active" 48 - href="/link" 49 - > 50 - Link 51 - </ActiveLocalizedLink> 52 - </IntlProvider> 53 ); 54 55 expect(screen.findByText('Link')).resolves.toHaveAttribute(
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import ActiveLocalizedLink from '..'; 4 5 describe('ActiveLocalizedLink', () => { 6 it('renders as localized link', () => { 7 render( 8 + <ActiveLocalizedLink 9 + className="link" 10 + activeClassName="active" 11 + href="/link" 12 + > 13 + Link 14 + </ActiveLocalizedLink> 15 ); 16 17 expect(screen.findByText('Link')).resolves.toHaveAttribute( ··· 22 23 it('ignores active class when href not matches location.href', () => { 24 render( 25 + <ActiveLocalizedLink 26 + className="link" 27 + activeClassName="active" 28 + href="/not-link" 29 + > 30 + Link 31 + </ActiveLocalizedLink> 32 ); 33 34 expect(screen.findByText('Link')).resolves.toHaveAttribute('class', 'link'); ··· 36 37 it('sets active class when href matches location.href', () => { 38 render( 39 + <ActiveLocalizedLink 40 + className="link" 41 + activeClassName="active" 42 + href="/link" 43 + > 44 + Link 45 + </ActiveLocalizedLink> 46 ); 47 48 expect(screen.findByText('Link')).resolves.toHaveAttribute(
+11 -37
components/Common/ActiveLocalizedLink/index.tsx
··· 1 import classNames from 'classnames'; 2 - import type Link from 'next/link'; 3 - import { useRouter } from 'next/router'; 4 - import { useState, useEffect } from 'react'; 5 import type { ComponentProps, FC } from 'react'; 6 7 - import LocalizedLink from '@/components/LocalizedLink'; 8 9 type ActiveLocalizedLinkProps = ComponentProps<typeof Link> & { 10 activeClassName: string; ··· 14 children, 15 activeClassName, 16 className, 17 ...props 18 }) => { 19 - const { asPath, isReady } = useRouter(); 20 - 21 - const [computedClassName, setComputedClassName] = useState(className); 22 - 23 - useEffect(() => { 24 - // Check if the router fields are updated client-side 25 - if (isReady) { 26 - const currentHref = (props.as || props.href).toString(); 27 - 28 - // Dynamic route will be matched via props.as 29 - // Static route will be matched via props.href 30 - const linkURL = new URL(currentHref, location.href); 31 - 32 - // Using URL().pathname to get rid of query and hash 33 - const currentPathName = new URL(asPath, location.href).pathname; 34 35 - const newClassName = classNames(className, { 36 - [activeClassName]: linkURL.pathname === currentPathName, 37 - }); 38 39 - if (newClassName !== computedClassName) { 40 - setComputedClassName(newClassName); 41 - } 42 - } 43 - }, [ 44 - asPath, 45 - isReady, 46 - props.as, 47 - props.href, 48 - activeClassName, 49 - className, 50 - computedClassName, 51 - ]); 52 53 return ( 54 - <LocalizedLink className={computedClassName} {...props}> 55 {children} 56 - </LocalizedLink> 57 ); 58 }; 59
··· 1 + 'use client'; 2 + 3 import classNames from 'classnames'; 4 import type { ComponentProps, FC } from 'react'; 5 6 + import { Link, usePathname } from '@/navigation.mjs'; 7 8 type ActiveLocalizedLinkProps = ComponentProps<typeof Link> & { 9 activeClassName: string; ··· 13 children, 14 activeClassName, 15 className, 16 + href = '', 17 ...props 18 }) => { 19 + const pathname = usePathname(); 20 21 + const linkURL = new URL(href.toString(), location.href); 22 23 + const finalClassName = classNames(className, { 24 + [activeClassName]: linkURL.pathname === pathname, 25 + }); 26 27 return ( 28 + <Link className={finalClassName} href={href} {...props}> 29 {children} 30 + </Link> 31 ); 32 }; 33
+3 -4
components/Common/Badge/index.tsx
··· 1 import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon'; 2 - import type Link from 'next/link'; 3 import type { ComponentProps, FC, PropsWithChildren } from 'react'; 4 5 - import LocalizedLink from '@/components/LocalizedLink'; 6 7 import styles from './index.module.css'; 8 ··· 17 children, 18 ...args 19 }) => ( 20 - <LocalizedLink className={`${styles.wrapper} ${styles[kind]}`} {...args}> 21 {badgeText && <span className={styles.badge}>{badgeText}</span>} 22 <span className={styles.message}>{children}</span> 23 <ArrowRightIcon className={styles.icon} /> 24 - </LocalizedLink> 25 ); 26 27 export default Badge;
··· 1 import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon'; 2 import type { ComponentProps, FC, PropsWithChildren } from 'react'; 3 4 + import { Link } from '@/navigation.mjs'; 5 6 import styles from './index.module.css'; 7 ··· 16 children, 17 ...args 18 }) => ( 19 + <Link className={`${styles.wrapper} ${styles[kind]}`} {...args}> 20 {badgeText && <span className={styles.badge}>{badgeText}</span>} 21 <span className={styles.message}>{children}</span> 22 <ArrowRightIcon className={styles.icon} /> 23 + </Link> 24 ); 25 26 export default Badge;
+2 -3
components/Common/Banner/index.tsx
··· 1 import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; 2 import type { FC } from 'react'; 3 4 - import LocalizedLink from '@/components/LocalizedLink'; 5 6 import styles from './index.module.css'; 7 ··· 13 14 const Banner: FC<BannerProps> = ({ type, text, url = '' }) => ( 15 <div className={`${styles.banner} ${styles[type] || styles.default}`}> 16 - {(url.length > 0 && <LocalizedLink href={url}>{text}</LocalizedLink>) || 17 - text} 18 {url.length > 0 && <ArrowUpRightIcon />} 19 </div> 20 );
··· 1 import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; 2 import type { FC } from 'react'; 3 4 + import { Link } from '@/navigation.mjs'; 5 6 import styles from './index.module.css'; 7 ··· 13 14 const Banner: FC<BannerProps> = ({ type, text, url = '' }) => ( 15 <div className={`${styles.banner} ${styles[type] || styles.default}`}> 16 + {(url.length > 0 && <Link href={url}>{text}</Link>) || text} 17 {url.length > 0 && <ArrowUpRightIcon />} 18 </div> 19 );
+11 -14
components/Common/BlogPostCard/__tests__/index.test.mjs
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import BlogPostCard from '@/components/Common/BlogPostCard'; 4 - import { LocaleProvider } from '@/providers/localeProvider'; 5 6 function renderBlogPostCard({ 7 title = 'Blog post title', ··· 11 date = new Date(), 12 }) { 13 render( 14 - <LocaleProvider> 15 - <BlogPostCard 16 - title={title} 17 - type={type} 18 - description={description} 19 - authors={authors} 20 - date={date} 21 - /> 22 - </LocaleProvider> 23 ); 24 25 return { title, type, description, authors, date }; ··· 56 }); 57 58 it.each([ 59 - { label: 'Vulnerabilities', type: 'vulnerability' }, 60 - { label: 'Announcements', type: 'announcement' }, 61 - { label: 'Releases', type: 'release' }, 62 ])( 63 'Renders "%label" text when passing it the type "%type"', 64 ({ label, type }) => { ··· 111 112 renderBlogPostCard({ date }); 113 114 - const dateTimeFormat = new Intl.DateTimeFormat(navigator.language, { 115 day: 'numeric', 116 month: 'short', 117 year: 'numeric',
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import BlogPostCard from '@/components/Common/BlogPostCard'; 4 5 function renderBlogPostCard({ 6 title = 'Blog post title', ··· 10 date = new Date(), 11 }) { 12 render( 13 + <BlogPostCard 14 + title={title} 15 + type={type} 16 + description={description} 17 + authors={authors} 18 + date={date} 19 + /> 20 ); 21 22 return { title, type, description, authors, date }; ··· 53 }); 54 55 it.each([ 56 + { label: 'components.common.card.vulnerability', type: 'vulnerability' }, 57 + { label: 'components.common.card.announcement', type: 'announcement' }, 58 + { label: 'components.common.card.release', type: 'release' }, 59 ])( 60 'Renders "%label" text when passing it the type "%type"', 61 ({ label, type }) => { ··· 108 109 renderBlogPostCard({ date }); 110 111 + const dateTimeFormat = new Intl.DateTimeFormat('en-US', { 112 day: 'numeric', 113 month: 'short', 114 year: 'numeric',
+11 -9
components/Common/BlogPostCard/index.module.css
··· 41 } 42 43 .author { 44 - @apply text-sm 45 - font-semibold 46 - text-neutral-900 47 - dark:text-white; 48 - } 49 50 - .date { 51 - @apply text-sm 52 - text-neutral-800 53 - dark:text-neutral-200; 54 }
··· 41 } 42 43 .author { 44 + p { 45 + @apply text-sm 46 + font-semibold 47 + text-neutral-900 48 + dark:text-white; 49 + } 50 51 + time { 52 + @apply text-sm 53 + text-neutral-800 54 + dark:text-neutral-200; 55 + } 56 }
+12 -23
components/Common/BlogPostCard/index.tsx
··· 1 import { useMemo } from 'react'; 2 import type { ComponentProps, FC } from 'react'; 3 - import { FormattedMessage } from 'react-intl'; 4 5 import AvatarGroup from '@/components/Common/AvatarGroup'; 6 import Preview from '@/components/Common/Preview'; 7 8 import styles from './index.module.css'; 9 - 10 - const dateTimeFormat = new Intl.DateTimeFormat(navigator.language, { 11 - day: 'numeric', 12 - month: 'short', 13 - year: 'numeric', 14 - }); 15 16 type Author = { 17 fullName: string; ··· 33 authors, 34 date, 35 }) => { 36 const avatars = useMemo( 37 () => 38 authors.map(({ fullName, src }) => ({ ··· 43 [authors] 44 ); 45 46 - const formattedDate = useMemo( 47 - () => ({ 48 - ISOString: date.toISOString(), 49 - short: dateTimeFormat.format(date), 50 - }), 51 - [date] 52 - ); 53 - 54 return ( 55 <article className={styles.container}> 56 <Preview ··· 59 height="auto" 60 className={styles.preview} 61 /> 62 - <p className={styles.subtitle}> 63 - <FormattedMessage id={`components.common.card.${type}`} /> 64 - </p> 65 <p aria-hidden="true" className={styles.title}> 66 {title} 67 </p> 68 <p className={styles.description}>{description}</p> 69 <footer className={styles.footer}> 70 <AvatarGroup avatars={avatars} /> 71 - <div> 72 - <p className={styles.author}>{avatars.join(', ')}</p> 73 - <time className={styles.date} dateTime={formattedDate.ISOString}> 74 - {formattedDate.short} 75 - </time> 76 </div> 77 </footer> 78 </article>
··· 1 + import { useTranslations } from 'next-intl'; 2 import { useMemo } from 'react'; 3 import type { ComponentProps, FC } from 'react'; 4 5 import AvatarGroup from '@/components/Common/AvatarGroup'; 6 import Preview from '@/components/Common/Preview'; 7 + import { Time } from '@/components/Common/Time'; 8 9 import styles from './index.module.css'; 10 11 type Author = { 12 fullName: string; ··· 28 authors, 29 date, 30 }) => { 31 + const t = useTranslations(); 32 + 33 const avatars = useMemo( 34 () => 35 authors.map(({ fullName, src }) => ({ ··· 40 [authors] 41 ); 42 43 return ( 44 <article className={styles.container}> 45 <Preview ··· 48 height="auto" 49 className={styles.preview} 50 /> 51 + <p className={styles.subtitle}>{t(`components.common.card.${type}`)}</p> 52 <p aria-hidden="true" className={styles.title}> 53 {title} 54 </p> 55 <p className={styles.description}>{description}</p> 56 <footer className={styles.footer}> 57 <AvatarGroup avatars={avatars} /> 58 + <div className={styles.author}> 59 + <p>{avatars.join(', ')}</p> 60 + 61 + <Time 62 + date={date} 63 + format={{ day: 'numeric', month: 'short', year: 'numeric' }} 64 + /> 65 </div> 66 </footer> 67 </article>
+3 -5
components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx
··· 1 import HomeIcon from '@heroicons/react/24/outline/HomeIcon'; 2 import type { ComponentProps, FC } from 'react'; 3 - import { useIntl } from 'react-intl'; 4 5 import BreadcrumbLink from '@/components/Common/Breadcrumbs/BreadcrumbLink'; 6 ··· 16 href = '/', 17 ...props 18 }) => { 19 - const { formatMessage } = useIntl(); 20 21 - const navigateToHome = formatMessage({ 22 - id: 'components.common.breadcrumbs.navigateToHome', 23 - }); 24 25 return ( 26 <BreadcrumbLink href={href} {...props}>
··· 1 import HomeIcon from '@heroicons/react/24/outline/HomeIcon'; 2 + import { useTranslations } from 'next-intl'; 3 import type { ComponentProps, FC } from 'react'; 4 5 import BreadcrumbLink from '@/components/Common/Breadcrumbs/BreadcrumbLink'; 6 ··· 16 href = '/', 17 ...props 18 }) => { 19 + const t = useTranslations(); 20 21 + const navigateToHome = t('components.common.breadcrumbs.navigateToHome'); 22 23 return ( 24 <BreadcrumbLink href={href} {...props}>
+4 -4
components/Common/Breadcrumbs/BreadcrumbLink/index.tsx
··· 1 import classNames from 'classnames'; 2 import type { ComponentProps, FC } from 'react'; 3 4 - import LocalizedLink from '@/components/LocalizedLink'; 5 6 import styles from './index.module.css'; 7 8 type BreadcrumbLinkProps = { 9 active?: boolean; 10 - } & ComponentProps<typeof LocalizedLink>; 11 12 const BreadcrumbLink: FC<BreadcrumbLinkProps> = ({ 13 href, 14 active, 15 ...props 16 }) => ( 17 - <LocalizedLink 18 itemScope 19 itemType="http://schema.org/Thing" 20 itemProp="item" ··· 29 {...props} 30 > 31 <span itemProp="name">{props.children}</span> 32 - </LocalizedLink> 33 ); 34 35 export default BreadcrumbLink;
··· 1 import classNames from 'classnames'; 2 import type { ComponentProps, FC } from 'react'; 3 4 + import { Link } from '@/navigation.mjs'; 5 6 import styles from './index.module.css'; 7 8 type BreadcrumbLinkProps = { 9 active?: boolean; 10 + } & ComponentProps<typeof Link>; 11 12 const BreadcrumbLink: FC<BreadcrumbLinkProps> = ({ 13 href, 14 active, 15 ...props 16 }) => ( 17 + <Link 18 itemScope 19 itemType="http://schema.org/Thing" 20 itemProp="item" ··· 29 {...props} 30 > 31 <span itemProp="name">{props.children}</span> 32 + </Link> 33 ); 34 35 export default BreadcrumbLink;
+25 -21
components/Common/CrossLink/index.tsx
··· 1 import classNames from 'classnames'; 2 import type { FC } from 'react'; 3 - import { FormattedMessage } from 'react-intl'; 4 5 import PrevNextArrow from '@/components/Common/PrevNextArrow'; 6 - import LocalizedLink from '@/components/LocalizedLink'; 7 8 import styles from './index.module.css'; 9 ··· 13 url: string; 14 }; 15 16 - const CrossLink: FC<CrossLinkProps> = ({ type, text, url }) => ( 17 - <LocalizedLink className={styles.crossLink} href={url}> 18 - <span 19 - className={classNames(styles.header, { 20 - [styles.reverse]: type === 'next', 21 - })} 22 - > 23 - <PrevNextArrow className={styles.icon} type={type} /> 24 - <FormattedMessage id={`components.common.crossLink.${type}`} /> 25 - </span> 26 27 - <span 28 - className={classNames(styles.content, { 29 - [styles.reverse]: type === 'next', 30 - })} 31 - > 32 - {text} 33 - </span> 34 - </LocalizedLink> 35 - ); 36 37 export default CrossLink;
··· 1 import classNames from 'classnames'; 2 + import { useTranslations } from 'next-intl'; 3 import type { FC } from 'react'; 4 5 import PrevNextArrow from '@/components/Common/PrevNextArrow'; 6 + import { Link } from '@/navigation.mjs'; 7 8 import styles from './index.module.css'; 9 ··· 13 url: string; 14 }; 15 16 + const CrossLink: FC<CrossLinkProps> = ({ type, text, url }) => { 17 + const t = useTranslations(); 18 19 + return ( 20 + <Link className={styles.crossLink} href={url}> 21 + <span 22 + className={classNames(styles.header, { 23 + [styles.reverse]: type === 'next', 24 + })} 25 + > 26 + <PrevNextArrow className={styles.icon} type={type} /> 27 + {t(`components.common.crossLink.${type}`)} 28 + </span> 29 + 30 + <span 31 + className={classNames(styles.content, { 32 + [styles.reverse]: type === 'next', 33 + })} 34 + > 35 + {text} 36 + </span> 37 + </Link> 38 + ); 39 + }; 40 41 export default CrossLink;
+3 -5
components/Common/LanguageDropDown/index.tsx
··· 1 import { LanguageIcon } from '@heroicons/react/24/outline'; 2 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; 3 import classNames from 'classnames'; 4 import type { FC } from 'react'; 5 - import { useIntl } from 'react-intl'; 6 7 import type { LocaleConfig } from '@/types'; 8 ··· 21 currentLanguage, 22 availableLanguages, 23 }) => { 24 - const { formatMessage } = useIntl(); 25 26 - const ariaLabel = formatMessage({ 27 - id: 'components.common.languageDropdown.label', 28 - }); 29 30 return ( 31 <DropdownMenu.Root>
··· 1 import { LanguageIcon } from '@heroicons/react/24/outline'; 2 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; 3 import classNames from 'classnames'; 4 + import { useTranslations } from 'next-intl'; 5 import type { FC } from 'react'; 6 7 import type { LocaleConfig } from '@/types'; 8 ··· 21 currentLanguage, 22 availableLanguages, 23 }) => { 24 + const t = useTranslations(); 25 26 + const ariaLabel = t('components.common.languageDropdown.label'); 27 28 return ( 29 <DropdownMenu.Root>
+3 -6
components/Common/MetaBar/index.stories.tsx
··· 1 import { CodeBracketIcon } from '@heroicons/react/24/outline'; 2 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 3 import Image from 'next/image'; 4 - import { FormattedMessage } from 'react-intl'; 5 6 import AvatarGroup from '@/components/Common/AvatarGroup'; 7 import MetaBar from '@/components/Common/MetaBar'; 8 - import LocalizedLink from '@/components/LocalizedLink'; 9 10 type Story = StoryObj<typeof MetaBar>; 11 type Meta = MetaObj<typeof MetaBar>; ··· 53 height={16} 54 data-on-dark 55 /> 56 - <LocalizedLink href="/contribute"> 57 - <FormattedMessage id="components.metabar.contributeText" /> 58 - </LocalizedLink> 59 </> 60 ), 61 'components.metabar.viewAs': ( 62 <> 63 <CodeBracketIcon /> 64 - <LocalizedLink href="/json">JSON</LocalizedLink> 65 </> 66 ), 67 },
··· 1 import { CodeBracketIcon } from '@heroicons/react/24/outline'; 2 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 3 import Image from 'next/image'; 4 5 import AvatarGroup from '@/components/Common/AvatarGroup'; 6 import MetaBar from '@/components/Common/MetaBar'; 7 + import { Link } from '@/navigation.mjs'; 8 9 type Story = StoryObj<typeof MetaBar>; 10 type Meta = MetaObj<typeof MetaBar>; ··· 52 height={16} 53 data-on-dark 54 /> 55 + <Link href="/contribute">Edit this page</Link> 56 </> 57 ), 58 'components.metabar.viewAs': ( 59 <> 60 <CodeBracketIcon /> 61 + <Link href="/json">JSON</Link> 62 </> 63 ), 64 },
+8 -10
components/Common/MetaBar/index.tsx
··· 1 import type { Heading } from '@vcarl/remark-headings'; 2 import { Fragment, useMemo } from 'react'; 3 import type { FC } from 'react'; 4 - import { FormattedMessage } from 'react-intl'; 5 6 - import LocalizedLink from '@/components/LocalizedLink'; 7 8 import styles from './index.module.css'; 9 ··· 16 }; 17 18 const MetaBar: FC<MetaBarProps> = ({ items, headings }) => { 19 // The default depth of headings to display in the table of contents. 20 const { depth = 2, items: headingItems = [] } = headings || {}; 21 ··· 29 <dl> 30 {Object.entries(items).map(([key, value]) => ( 31 <Fragment key={key}> 32 - <dt> 33 - <FormattedMessage id={key} /> 34 - </dt> 35 <dd>{value}</dd> 36 </Fragment> 37 ))} 38 {heading.length > 0 && ( 39 <Fragment key="tableOfContents"> 40 - <dt> 41 - <FormattedMessage id="components.metabar.tableOfContents" /> 42 - </dt> 43 <dd> 44 <ol> 45 {heading.map(head => ( 46 <li key={head.value}> 47 - <LocalizedLink href={`#${head?.data?.id || head.value}`}> 48 {head.value} 49 - </LocalizedLink> 50 </li> 51 ))} 52 </ol>
··· 1 import type { Heading } from '@vcarl/remark-headings'; 2 + import { useTranslations } from 'next-intl'; 3 import { Fragment, useMemo } from 'react'; 4 import type { FC } from 'react'; 5 6 + import { Link } from '@/navigation.mjs'; 7 8 import styles from './index.module.css'; 9 ··· 16 }; 17 18 const MetaBar: FC<MetaBarProps> = ({ items, headings }) => { 19 + const t = useTranslations(); 20 + 21 // The default depth of headings to display in the table of contents. 22 const { depth = 2, items: headingItems = [] } = headings || {}; 23 ··· 31 <dl> 32 {Object.entries(items).map(([key, value]) => ( 33 <Fragment key={key}> 34 + <dt>{t(key)}</dt> 35 <dd>{value}</dd> 36 </Fragment> 37 ))} 38 {heading.length > 0 && ( 39 <Fragment key="tableOfContents"> 40 + <dt>{t('components.metabar.tableOfContents')}</dt> 41 <dd> 42 <ol> 43 {heading.map(head => ( 44 <li key={head.value}> 45 + <Link href={`#${head?.data?.id || head.value}`}> 46 {head.value} 47 + </Link> 48 </li> 49 ))} 50 </ol>
+1 -2
components/Common/Notification/index.stories.tsx
··· 1 import { CodeBracketIcon } from '@heroicons/react/24/solid'; 2 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 3 - import { FormattedMessage } from 'react-intl'; 4 5 import Notification from '@/components/Common/Notification'; 6 ··· 28 children: ( 29 <div className="flex items-center gap-3"> 30 <CodeBracketIcon className="h-4 w-4" /> 31 - <FormattedMessage id="components.common.codebox.copied" /> 32 </div> 33 ), 34 },
··· 1 import { CodeBracketIcon } from '@heroicons/react/24/solid'; 2 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 3 4 import Notification from '@/components/Common/Notification'; 5 ··· 27 children: ( 28 <div className="flex items-center gap-3"> 29 <CodeBracketIcon className="h-4 w-4" /> 30 + Copied to clipboard! 31 </div> 32 ), 33 },
+6 -9
components/Common/Pagination/PaginationListItem/__tests__/index.test.mjs
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import PaginationListItem from '@/components/Common/Pagination/PaginationListItem'; 4 - import { LocaleProvider } from '@/providers/localeProvider'; 5 6 function renderPaginationListItem({ 7 url, ··· 10 totalPages, 11 }) { 12 render( 13 - <LocaleProvider> 14 - <PaginationListItem 15 - url={url} 16 - pageNumber={pageNumber} 17 - currentPage={currentPage} 18 - totalPages={totalPages} 19 - /> 20 - </LocaleProvider> 21 ); 22 } 23
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import PaginationListItem from '@/components/Common/Pagination/PaginationListItem'; 4 5 function renderPaginationListItem({ 6 url, ··· 9 totalPages, 10 }) { 11 render( 12 + <PaginationListItem 13 + url={url} 14 + pageNumber={pageNumber} 15 + currentPage={currentPage} 16 + totalPages={totalPages} 17 + /> 18 ); 19 } 20
+6 -9
components/Common/Pagination/PaginationListItem/index.tsx
··· 1 import type { FC } from 'react'; 2 - import { useIntl } from 'react-intl'; 3 4 - import LocalizedLink from '@/components/LocalizedLink'; 5 6 import styles from './index.module.css'; 7 ··· 19 currentPage, 20 totalPages, 21 }) => { 22 - const { formatMessage } = useIntl(); 23 24 return ( 25 <li key={pageNumber} aria-setsize={totalPages} aria-posinset={pageNumber}> 26 - <LocalizedLink 27 href={url} 28 - aria-label={formatMessage( 29 - { id: 'components.common.pagination.pageLabel' }, 30 - { pageNumber } 31 - )} 32 className={styles.listItem} 33 {...(pageNumber === currentPage && { 'aria-current': 'page' })} 34 > 35 <span>{pageNumber}</span> 36 - </LocalizedLink> 37 </li> 38 ); 39 };
··· 1 + import { useTranslations } from 'next-intl'; 2 import type { FC } from 'react'; 3 4 + import { Link } from '@/navigation.mjs'; 5 6 import styles from './index.module.css'; 7 ··· 19 currentPage, 20 totalPages, 21 }) => { 22 + const t = useTranslations(); 23 24 return ( 25 <li key={pageNumber} aria-setsize={totalPages} aria-posinset={pageNumber}> 26 + <Link 27 href={url} 28 + aria-label={t('components.common.pagination.pageLabel', { pageNumber })} 29 className={styles.listItem} 30 {...(pageNumber === currentPage && { 'aria-current': 'page' })} 31 > 32 <span>{pageNumber}</span> 33 + </Link> 34 </li> 35 ); 36 };
+25 -12
components/Common/Pagination/__tests__/index.test.mjs
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import Pagination from '@/components/Common/Pagination'; 4 - import { LocaleProvider } from '@/providers/localeProvider'; 5 6 function renderPagination({ 7 currentPage = 1, ··· 13 .map(item => ({ url: `${item.url}-${Math.random()}` })); 14 15 render( 16 - <LocaleProvider> 17 - <Pagination 18 - currentPage={currentPage} 19 - pages={parsedPages} 20 - currentPageSiblingsCount={currentPageSiblingsCount} 21 - /> 22 - </LocaleProvider> 23 ); 24 25 return { ··· 35 36 expect(screen.getByRole('navigation')).toBeVisible(); 37 38 - expect(screen.getByRole('button', { name: /prev/i })).toBeVisible(); 39 40 - expect(screen.getByRole('button', { name: /next/i })).toBeVisible(); 41 }); 42 43 it('Renders the passed pages and current page', () => { ··· 153 pages: 2, 154 }); 155 156 - expect(screen.getByRole('button', { name: /prev/i })).toBeDisabled(); 157 }); 158 159 it('Disables "Next" button when the currentPage is equal to the last page', () => { ··· 162 pages: 2, 163 }); 164 165 - expect(screen.getByRole('button', { name: /next/i })).toBeDisabled(); 166 }); 167 }); 168 });
··· 1 import { render, screen } from '@testing-library/react'; 2 3 import Pagination from '@/components/Common/Pagination'; 4 5 function renderPagination({ 6 currentPage = 1, ··· 12 .map(item => ({ url: `${item.url}-${Math.random()}` })); 13 14 render( 15 + <Pagination 16 + currentPage={currentPage} 17 + pages={parsedPages} 18 + currentPageSiblingsCount={currentPageSiblingsCount} 19 + /> 20 ); 21 22 return { ··· 32 33 expect(screen.getByRole('navigation')).toBeVisible(); 34 35 + expect( 36 + screen.getByRole('button', { 37 + name: 'components.common.pagination.prevAriaLabel', 38 + }) 39 + ).toBeVisible(); 40 41 + expect( 42 + screen.getByRole('button', { 43 + name: 'components.common.pagination.nextAriaLabel', 44 + }) 45 + ).toBeVisible(); 46 }); 47 48 it('Renders the passed pages and current page', () => { ··· 158 pages: 2, 159 }); 160 161 + expect( 162 + screen.getByRole('button', { 163 + name: 'components.common.pagination.prevAriaLabel', 164 + }) 165 + ).toBeDisabled(); 166 }); 167 168 it('Disables "Next" button when the currentPage is equal to the last page', () => { ··· 171 pages: 2, 172 }); 173 174 + expect( 175 + screen.getByRole('button', { 176 + name: 'components.common.pagination.nextAriaLabel', 177 + }) 178 + ).toBeDisabled(); 179 }); 180 }); 181 });
+7 -19
components/Common/Pagination/index.tsx
··· 1 import { ArrowRightIcon, ArrowLeftIcon } from '@heroicons/react/20/solid'; 2 import type { FC } from 'react'; 3 - import { FormattedMessage, useIntl } from 'react-intl'; 4 5 import Button from '@/components/Common/Button'; 6 import { useGetPageElements } from '@/components/Common/Pagination/useGetPageElements'; ··· 23 pages, 24 currentPageSiblingsCount = 1, 25 }) => { 26 - const { formatMessage } = useIntl(); 27 28 const parsedPages = useGetPageElements( 29 currentPage, ··· 33 34 return ( 35 <nav 36 - aria-label={formatMessage({ 37 - id: 'components.common.pagination.defaultLabel', 38 - })} 39 className={styles.pagination} 40 > 41 <Button 42 type="button" 43 - aria-label={formatMessage({ 44 - id: 'components.common.pagination.prevAriaLabel', 45 - })} 46 disabled={currentPage === 1} 47 variant="secondary" 48 className={styles.previousButton} 49 > 50 <ArrowLeftIcon className={styles.arrowIcon} /> 51 - <FormattedMessage 52 - id="components.common.pagination.prev" 53 - tagName="span" 54 - /> 55 </Button> 56 <ol className={styles.list}>{parsedPages}</ol> 57 <Button 58 type="button" 59 - aria-label={formatMessage({ 60 - id: 'components.common.pagination.nextAriaLabel', 61 - })} 62 disabled={currentPage === pages.length} 63 variant="secondary" 64 className={styles.nextButton} 65 > 66 - <FormattedMessage 67 - id="components.common.pagination.next" 68 - tagName="span" 69 - /> 70 <ArrowRightIcon className={styles.arrowIcon} /> 71 </Button> 72 </nav>
··· 1 import { ArrowRightIcon, ArrowLeftIcon } from '@heroicons/react/20/solid'; 2 + import { useTranslations } from 'next-intl'; 3 import type { FC } from 'react'; 4 5 import Button from '@/components/Common/Button'; 6 import { useGetPageElements } from '@/components/Common/Pagination/useGetPageElements'; ··· 23 pages, 24 currentPageSiblingsCount = 1, 25 }) => { 26 + const t = useTranslations(); 27 28 const parsedPages = useGetPageElements( 29 currentPage, ··· 33 34 return ( 35 <nav 36 + aria-label={t('components.common.pagination.defaultLabel')} 37 className={styles.pagination} 38 > 39 <Button 40 type="button" 41 + aria-label={t('components.common.pagination.prevAriaLabel')} 42 disabled={currentPage === 1} 43 variant="secondary" 44 className={styles.previousButton} 45 > 46 <ArrowLeftIcon className={styles.arrowIcon} /> 47 + <span>{t('components.common.pagination.prev')}</span> 48 </Button> 49 <ol className={styles.list}>{parsedPages}</ol> 50 <Button 51 type="button" 52 + aria-label={t('components.common.pagination.nextAriaLabel')} 53 disabled={currentPage === pages.length} 54 variant="secondary" 55 className={styles.nextButton} 56 > 57 + <span>{t('components.common.pagination.next')}</span> 58 <ArrowRightIcon className={styles.arrowIcon} /> 59 </Button> 60 </nav>
+19 -23
components/Common/Preview/index.tsx
··· 2 import Image from 'next/image'; 3 import type { CSSProperties, ComponentProps, FC, ReactNode } from 'react'; 4 5 - import { useRouter } from '@/hooks/useRouter'; 6 7 import styles from './index.module.css'; 8 ··· 19 height = 630, 20 width = 1200, 21 ...props 22 - }) => { 23 - const { basePath } = useRouter(); 24 - 25 - return ( 26 - <div 27 - {...props} 28 - style={{ width, height, ...props.style }} 29 - className={classNames(styles.root, styles[type], props.className)} 30 - > 31 - <div className={styles.container}> 32 - <Image 33 - className={styles.logo} 34 - priority 35 - width="71" 36 - height="80" 37 - src={`${basePath}/static/images/logos/js-white.svg`} 38 - alt="Node.js" 39 - /> 40 - <h2>{title}</h2> 41 - </div> 42 </div> 43 - ); 44 - }; 45 46 export default Preview;
··· 2 import Image from 'next/image'; 3 import type { CSSProperties, ComponentProps, FC, ReactNode } from 'react'; 4 5 + import { BASE_PATH } from '@/next.constants.mjs'; 6 7 import styles from './index.module.css'; 8 ··· 19 height = 630, 20 width = 1200, 21 ...props 22 + }) => ( 23 + <div 24 + {...props} 25 + style={{ width, height, ...props.style }} 26 + className={classNames(styles.root, styles[type], props.className)} 27 + > 28 + <div className={styles.container}> 29 + <Image 30 + className={styles.logo} 31 + priority 32 + width="71" 33 + height="80" 34 + src={`${BASE_PATH}/static/images/logos/js-white.svg`} 35 + alt="Node.js" 36 + /> 37 + <h2>{title}</h2> 38 </div> 39 + </div> 40 + ); 41 42 export default Preview;
+1 -7
components/Common/ThemeToggle/__tests__/index.test.mjs
··· 1 import { render, screen } from '@testing-library/react'; 2 import userEvent from '@testing-library/user-event'; 3 4 - import { LocaleProvider } from '@/providers/localeProvider'; 5 - 6 import ThemeToggle from '../'; 7 8 let mockCurrentTheme = 'light'; ··· 17 beforeEach(() => { 18 mockCurrentTheme = 'light'; 19 20 - render( 21 - <LocaleProvider> 22 - <ThemeToggle onClick={toggleTheme} /> 23 - </LocaleProvider> 24 - ); 25 toggle = screen.getByRole('button'); 26 }); 27
··· 1 import { render, screen } from '@testing-library/react'; 2 import userEvent from '@testing-library/user-event'; 3 4 import ThemeToggle from '../'; 5 6 let mockCurrentTheme = 'light'; ··· 15 beforeEach(() => { 16 mockCurrentTheme = 'light'; 17 18 + render(<ThemeToggle onClick={toggleTheme} />); 19 toggle = screen.getByRole('button'); 20 }); 21
+3 -5
components/Common/ThemeToggle/index.tsx
··· 1 import { MoonIcon, SunIcon } from '@heroicons/react/24/outline'; 2 import type { FC, MouseEvent } from 'react'; 3 - import { useIntl } from 'react-intl'; 4 5 import styles from './index.module.css'; 6 ··· 9 }; 10 11 const ThemeToggle: FC<ThemeToggleProps> = ({ onClick = () => {} }) => { 12 - const { formatMessage } = useIntl(); 13 14 - const ariaLabel = formatMessage({ 15 - id: 'components.header.buttons.toggleDarkMode', 16 - }); 17 18 return ( 19 <button
··· 1 import { MoonIcon, SunIcon } from '@heroicons/react/24/outline'; 2 + import { useTranslations } from 'next-intl'; 3 import type { FC, MouseEvent } from 'react'; 4 5 import styles from './index.module.css'; 6 ··· 9 }; 10 11 const ThemeToggle: FC<ThemeToggleProps> = ({ onClick = () => {} }) => { 12 + const t = useTranslations(); 13 14 + const ariaLabel = t('components.header.buttons.toggleDarkMode'); 15 16 return ( 17 <button
+6 -3
components/Common/Time/index.tsx
··· 1 import type { FC } from 'react'; 2 import { useMemo } from 'react'; 3 - import { FormattedDate } from 'react-intl'; 4 5 - type TimeProps = { date: string | Date; format: Intl.DateTimeFormatOptions }; 6 7 export const Time: FC<TimeProps> = ({ date, format }) => { 8 const dateObject = useMemo(() => new Date(date), [date]); 9 10 return ( 11 <time dateTime={dateObject.toISOString()}> 12 - <FormattedDate value={dateObject} {...format} timeZone="UTC" /> 13 </time> 14 ); 15 };
··· 1 + import type { DateTimeFormatOptions } from 'next-intl'; 2 + import { useFormatter } from 'next-intl'; 3 import type { FC } from 'react'; 4 import { useMemo } from 'react'; 5 6 + type TimeProps = { date: string | Date; format: DateTimeFormatOptions }; 7 8 export const Time: FC<TimeProps> = ({ date, format }) => { 9 + const formatter = useFormatter(); 10 + 11 const dateObject = useMemo(() => new Date(date), [date]); 12 13 return ( 14 <time dateTime={dateObject.toISOString()}> 15 + {formatter.dateTime(dateObject, format)} 16 </time> 17 ); 18 };
+7 -15
components/Docs/NodeApiVersionLinks.tsx
··· 1 - import { useMemo } from 'react'; 2 - 3 - import { useNodeReleases } from '@/hooks/useNodeReleases'; 4 import { DOCS_URL } from '@/next.constants.mjs'; 5 6 const NodeApiVersionLinks = () => { 7 - const { releases } = useNodeReleases(); 8 - 9 - const mappedReleases = useMemo( 10 - () => 11 - // Gets all major releases without the 0x release as those are divided on 0.12x and 0.10x 12 - releases.slice(0, -1).map(({ major }) => ( 13 - <li key={major}> 14 - <a href={`${DOCS_URL}latest-v${major}.x/api/`}>Node.js {major}.x</a> 15 - </li> 16 - )), 17 - [releases] 18 - ); 19 20 return ( 21 <ul>
··· 1 import { DOCS_URL } from '@/next.constants.mjs'; 2 + import { releaseData } from '@/next.json.mjs'; 3 4 const NodeApiVersionLinks = () => { 5 + // Gets all major releases without the 0x release as those are divided on 0.12x and 0.10x 6 + const mappedReleases = releaseData.slice(0, -1).map(({ major }) => ( 7 + <li key={major}> 8 + <a href={`${DOCS_URL}latest-v${major}.x/api/`}>Node.js {major}.x</a> 9 + </li> 10 + )); 11 12 return ( 13 <ul>
+8 -6
components/Downloads/DownloadList.tsx
··· 1 import type { FC } from 'react'; 2 - import { FormattedMessage } from 'react-intl'; 3 4 - import LocalizedLink from '@/components/LocalizedLink'; 5 - import { useNavigation } from '@/hooks/useNavigation'; 6 import type { NodeRelease } from '@/types'; 7 8 const DownloadList: FC<NodeRelease> = ({ versionWithPrefix }) => { 9 - const { getSideNavigation } = useNavigation(); 10 11 const [, ...downloadNavigation] = getSideNavigation('download', { 12 shaSums: { nodeVersion: versionWithPrefix }, ··· 18 <ul> 19 {downloadNavigation.map((item, key) => ( 20 <li key={key}> 21 - <LocalizedLink href={item.link}>{item.text}</LocalizedLink> 22 {item.key === 'shaSums' && ( 23 <a href="https://github.com/nodejs/node#verifying-binaries"> 24 - <FormattedMessage id="components.downloadList.links.shaSums.howToVerify" /> 25 </a> 26 )} 27 </li>
··· 1 + import { useTranslations } from 'next-intl'; 2 import type { FC } from 'react'; 3 4 + import { useSiteNavigation } from '@/hooks/server'; 5 + import { Link } from '@/navigation.mjs'; 6 import type { NodeRelease } from '@/types'; 7 8 const DownloadList: FC<NodeRelease> = ({ versionWithPrefix }) => { 9 + const t = useTranslations(); 10 + 11 + const { getSideNavigation } = useSiteNavigation(); 12 13 const [, ...downloadNavigation] = getSideNavigation('download', { 14 shaSums: { nodeVersion: versionWithPrefix }, ··· 20 <ul> 21 {downloadNavigation.map((item, key) => ( 22 <li key={key}> 23 + <Link href={item.link}>{item.text}</Link> 24 {item.key === 'shaSums' && ( 25 <a href="https://github.com/nodejs/node#verifying-binaries"> 26 + {t('components.downloadList.links.shaSums.howToVerify')} 27 </a> 28 )} 29 </li>
+7 -7
components/Downloads/DownloadReleasesTable.tsx
··· 1 import type { FC } from 'react'; 2 - import { FormattedMessage } from 'react-intl'; 3 4 - import { useNodeReleases } from '@/hooks/useNodeReleases'; 5 import { getNodeApiLink } from '@/util/getNodeApiLink'; 6 import { getNodejsChangelog } from '@/util/getNodeJsChangelog'; 7 8 const DownloadReleasesTable: FC = () => { 9 - const { releases } = useNodeReleases(); 10 11 return ( 12 <table id="tbVersions" className="download-table full-width"> ··· 25 </tr> 26 </thead> 27 <tbody> 28 - {releases.map(release => ( 29 <tr key={release.major}> 30 <td data-label="Version">Node.js {release.version}</td> 31 <td data-label="LTS">{release.codename}</td> ··· 39 <a 40 href={`https://nodejs.org/download/release/${release.versionWithPrefix}`} 41 > 42 - <FormattedMessage id="components.downloadReleasesTable.releases" /> 43 </a> 44 <a href={getNodejsChangelog(release.versionWithPrefix)}> 45 - <FormattedMessage id="components.downloadReleasesTable.changelog" /> 46 </a> 47 <a href={getNodeApiLink(release.versionWithPrefix)}> 48 - <FormattedMessage id="components.downloadReleasesTable.docs" /> 49 </a> 50 </td> 51 </tr>
··· 1 + import { useTranslations } from 'next-intl'; 2 import type { FC } from 'react'; 3 4 + import { releaseData } from '@/next.json.mjs'; 5 import { getNodeApiLink } from '@/util/getNodeApiLink'; 6 import { getNodejsChangelog } from '@/util/getNodeJsChangelog'; 7 8 const DownloadReleasesTable: FC = () => { 9 + const t = useTranslations(); 10 11 return ( 12 <table id="tbVersions" className="download-table full-width"> ··· 25 </tr> 26 </thead> 27 <tbody> 28 + {releaseData.map(release => ( 29 <tr key={release.major}> 30 <td data-label="Version">Node.js {release.version}</td> 31 <td data-label="LTS">{release.codename}</td> ··· 39 <a 40 href={`https://nodejs.org/download/release/${release.versionWithPrefix}`} 41 > 42 + {t('components.downloadReleasesTable.releases')} 43 </a> 44 <a href={getNodejsChangelog(release.versionWithPrefix)}> 45 + {t('components.downloadReleasesTable.changelog')} 46 </a> 47 <a href={getNodeApiLink(release.versionWithPrefix)}> 48 + {t('components.downloadReleasesTable.docs')} 49 </a> 50 </td> 51 </tr>
+37 -33
components/Downloads/PrimaryDownloadMatrix.tsx
··· 1 import classNames from 'classnames'; 2 import type { FC } from 'react'; 3 import semVer from 'semver'; 4 5 - import LocalizedLink from '@/components/LocalizedLink'; 6 - import { useDetectOS } from '@/hooks/useDetectOS'; 7 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 8 import { DIST_URL } from '@/next.constants.mjs'; 9 - import type { LegacyDownloadsFrontMatter, NodeRelease } from '@/types'; 10 11 - // @TODO: Instead of using a static list it should be created dynamically. This is done on `nodejs.dev` 12 - // since this is a temporary solution and going to be fixed in the future. 13 const PrimaryDownloadMatrix: FC<NodeRelease> = ({ 14 version, 15 versionWithPrefix, 16 isLts, 17 npm, 18 }) => { 19 - const { frontMatter } = useLayoutContext(); 20 21 - const { bitness } = useDetectOS(); 22 - 23 - const { downloads } = frontMatter as LegacyDownloadsFrontMatter; 24 const hasWindowsArm64 = semVer.satisfies(version, '>= 19.9.0'); 25 26 const getIsVersionClassName = (isCurrent: boolean) => ··· 37 <div className="download-hero full-width"> 38 <ul className="no-padding download-version-toggle"> 39 <li> 40 - <LocalizedLink 41 className={getIsVersionClassName(isLts)} 42 href="/download/" 43 title={`${downloads['display-hint']} ${downloads.lts}`} 44 > 45 <div className="title">{downloads.lts}</div> 46 <div className="tag">{downloads['tagline-lts']}</div> 47 - </LocalizedLink> 48 </li> 49 <li> 50 - <LocalizedLink 51 className={getIsVersionClassName(!isLts)} 52 - href="/download/current/" 53 title={`${downloads['display-hint']} ${downloads.current}`} 54 > 55 <div className="title">{downloads.current}</div> 56 <div className="tag">{downloads['tagline-current']}</div> 57 - </LocalizedLink> 58 </li> 59 </ul> 60 <ul className="no-padding download-platform"> 61 <li> 62 - <a 63 - href={`${DIST_URL}${versionWithPrefix}/node-${versionWithPrefix}-x${bitness}.msi`} 64 - data-version={versionWithPrefix} 65 - > 66 - <svg 67 - className="download-logo" 68 - width="50" 69 - height="50" 70 - viewBox="0 0 50 50" 71 - focusable="false" 72 - > 73 - <path d="M1.589 23.55L1.572 8.24l18.839-2.558V23.55zM23.55 5.225l25.112-3.654V23.55H23.55zM48.669 26.69l-.006 21.979-25.112-3.533V26.69zM20.41 44.736l-18.824-2.58-.001-15.466H20.41z" /> 74 - </svg> 75 - {downloads.WindowsInstaller} 76 - <p className="small color-lightgray"> 77 - node-{versionWithPrefix}-x{bitness}.msi 78 - </p> 79 - </a> 80 </li> 81 <li> 82 <a
··· 1 + 'use client'; 2 + 3 import classNames from 'classnames'; 4 import type { FC } from 'react'; 5 import semVer from 'semver'; 6 7 + import { WithCurrentOS } from '@/components/withCurrentOS'; 8 + import { useClientContext } from '@/hooks'; 9 + import { Link } from '@/navigation.mjs'; 10 import { DIST_URL } from '@/next.constants.mjs'; 11 + import type { NodeRelease } from '@/types'; 12 13 + // @TODO: Legacy Component to be removed in the Website Redesign 14 const PrimaryDownloadMatrix: FC<NodeRelease> = ({ 15 version, 16 versionWithPrefix, 17 isLts, 18 npm, 19 }) => { 20 + const { 21 + frontmatter: { downloads }, 22 + } = useClientContext(); 23 24 const hasWindowsArm64 = semVer.satisfies(version, '>= 19.9.0'); 25 26 const getIsVersionClassName = (isCurrent: boolean) => ··· 37 <div className="download-hero full-width"> 38 <ul className="no-padding download-version-toggle"> 39 <li> 40 + <Link 41 className={getIsVersionClassName(isLts)} 42 href="/download/" 43 title={`${downloads['display-hint']} ${downloads.lts}`} 44 > 45 <div className="title">{downloads.lts}</div> 46 <div className="tag">{downloads['tagline-lts']}</div> 47 + </Link> 48 </li> 49 <li> 50 + <Link 51 className={getIsVersionClassName(!isLts)} 52 + href="/download/current" 53 title={`${downloads['display-hint']} ${downloads.current}`} 54 > 55 <div className="title">{downloads.current}</div> 56 <div className="tag">{downloads['tagline-current']}</div> 57 + </Link> 58 </li> 59 </ul> 60 <ul className="no-padding download-platform"> 61 <li> 62 + <WithCurrentOS> 63 + {({ os }) => ( 64 + <a 65 + href={`${DIST_URL}${versionWithPrefix}/node-${versionWithPrefix}-x${os.bitness}.msi`} 66 + data-version={versionWithPrefix} 67 + > 68 + <svg 69 + className="download-logo" 70 + width="50" 71 + height="50" 72 + viewBox="0 0 50 50" 73 + focusable="false" 74 + > 75 + <path d="M1.589 23.55L1.572 8.24l18.839-2.558V23.55zM23.55 5.225l25.112-3.654V23.55H23.55zM48.669 26.69l-.006 21.979-25.112-3.533V26.69zM20.41 44.736l-18.824-2.58-.001-15.466H20.41z" /> 76 + </svg> 77 + {downloads.WindowsInstaller} 78 + <p className="small color-lightgray"> 79 + node-{versionWithPrefix}-x{os.bitness}.msi 80 + </p> 81 + </a> 82 + )} 83 + </WithCurrentOS> 84 </li> 85 <li> 86 <a
+8 -10
components/Downloads/SecondaryDownloadMatrix.tsx
··· 1 import type { FC } from 'react'; 2 3 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 4 import { DIST_URL } from '@/next.constants.mjs'; 5 - import { WithNodeRelease } from '@/providers/withNodeRelease'; 6 - import type { LegacyDownloadsFrontMatter, NodeRelease } from '@/types'; 7 - 8 - import DownloadList from './DownloadList'; 9 10 - // @TODO: Instead of using a static list it should be created dynamically. This is done on `nodejs.dev` 11 - // since this is a temporary solution and going to be fixed in the future. 12 const SecondaryDownloadMatrix: FC<NodeRelease> = ({ 13 versionWithPrefix, 14 status, 15 }) => { 16 - const { frontMatter } = useLayoutContext(); 17 - 18 - const { additional } = frontMatter as LegacyDownloadsFrontMatter; 19 20 return ( 21 <section>
··· 1 import type { FC } from 'react'; 2 3 + import DownloadList from '@/components/Downloads/DownloadList'; 4 + import { WithNodeRelease } from '@/components/withNodeRelease'; 5 + import { useClientContext } from '@/hooks/server'; 6 import { DIST_URL } from '@/next.constants.mjs'; 7 + import type { NodeRelease } from '@/types'; 8 9 + // @TODO: Legacy Component to be removed in the Website Redesign 10 const SecondaryDownloadMatrix: FC<NodeRelease> = ({ 11 versionWithPrefix, 12 status, 13 }) => { 14 + const { 15 + frontmatter: { additional }, 16 + } = useClientContext(); 17 18 return ( 19 <section>
+34 -44
components/Footer.tsx
··· 1 import type { FC } from 'react'; 2 - import { FormattedMessage } from 'react-intl'; 3 4 - import LocalizedLink from './LocalizedLink'; 5 6 type FooterProps = { className?: string }; 7 8 // Note.: We don't expect to translate these items as we're going to replace with `nodejs/nodejs.dev` footer 9 const Footer: FC<FooterProps> = ({ className }) => ( 10 - <> 11 - <a href="#" id="scroll-to-top"> 12 - <span> 13 - &uarr; <FormattedMessage id="components.footer.scrollToTop.button" /> 14 - </span> 15 - </a> 16 - <footer className={className}> 17 - <div className="container"> 18 - <div className="openjsfoundation-footer"> 19 - <p> 20 - Copyright <a href="https://openjsf.org">OpenJS Foundation</a> and 21 - Node.js contributors. All rights reserved. The{' '} 22 - <a href="https://openjsf.org">OpenJS Foundation</a> has registered 23 - trademarks and uses trademarks. For a list of trademarks of the{' '} 24 - <a href="https://openjsf.org">OpenJS Foundation</a>, please see our{' '} 25 - <a href="https://trademark-policy.openjsf.org">Trademark Policy</a>{' '} 26 - and <a href="https://trademark-list.openjsf.org">Trademark List</a>. 27 - Trademarks and logos not indicated on the{' '} 28 - <a href="https://trademark-list.openjsf.org"> 29 - list of OpenJS Foundation trademarks 30 - </a>{' '} 31 - are trademarks&trade; or registered&reg; trademarks of their 32 - respective holders. Use of them does not imply any affiliation with 33 - or endorsement by them. 34 - </p> 35 - <p className="openjsfoundation-footer-links"> 36 - <a href="https://openjsf.org">The OpenJS Foundation</a> 37 - &nbsp;|&nbsp; 38 - <a href="https://trademark-policy.openjsf.org">Trademark Policy</a> 39 - &nbsp;|&nbsp; 40 - <a href="https://privacy-policy.openjsf.org">Privacy Policy</a> 41 - &nbsp;|&nbsp; 42 - <a href="https://code-of-conduct.openjsf.org">Code of Conduct</a> 43 - &nbsp;|&nbsp; 44 - <LocalizedLink href="/about/security-reporting"> 45 - Security Reporting 46 - </LocalizedLink> 47 - </p> 48 - <div className="openjsfoundation-footer-edit"></div> 49 - </div> 50 </div> 51 - </footer> 52 - </> 53 ); 54 55 export default Footer;
··· 1 import type { FC } from 'react'; 2 3 + import { Link } from '@/navigation.mjs'; 4 5 type FooterProps = { className?: string }; 6 7 // Note.: We don't expect to translate these items as we're going to replace with `nodejs/nodejs.dev` footer 8 const Footer: FC<FooterProps> = ({ className }) => ( 9 + <footer className={className}> 10 + <div className="container"> 11 + <div className="openjsfoundation-footer"> 12 + <p> 13 + Copyright <a href="https://openjsf.org">OpenJS Foundation</a> and 14 + Node.js contributors. All rights reserved. The{' '} 15 + <a href="https://openjsf.org">OpenJS Foundation</a> has registered 16 + trademarks and uses trademarks. For a list of trademarks of the{' '} 17 + <a href="https://openjsf.org">OpenJS Foundation</a>, please see our{' '} 18 + <a href="https://trademark-policy.openjsf.org">Trademark Policy</a>{' '} 19 + and <a href="https://trademark-list.openjsf.org">Trademark List</a>. 20 + Trademarks and logos not indicated on the{' '} 21 + <a href="https://trademark-list.openjsf.org"> 22 + list of OpenJS Foundation trademarks 23 + </a>{' '} 24 + are trademarks&trade; or registered&reg; trademarks of their 25 + respective holders. Use of them does not imply any affiliation with or 26 + endorsement by them. 27 + </p> 28 + <p className="openjsfoundation-footer-links"> 29 + <a href="https://openjsf.org">The OpenJS Foundation</a> 30 + &nbsp;|&nbsp; 31 + <a href="https://trademark-policy.openjsf.org">Trademark Policy</a> 32 + &nbsp;|&nbsp; 33 + <a href="https://privacy-policy.openjsf.org">Privacy Policy</a> 34 + &nbsp;|&nbsp; 35 + <a href="https://code-of-conduct.openjsf.org">Code of Conduct</a> 36 + &nbsp;|&nbsp; 37 + <Link href="/about/security-reporting">Security Reporting</Link> 38 + </p> 39 + <div className="openjsfoundation-footer-edit"></div> 40 </div> 41 + </div> 42 + </footer> 43 ); 44 45 export default Footer;
+26 -31
components/Header.tsx
··· 1 import classNames from 'classnames'; 2 import Image from 'next/image'; 3 import { useTheme } from 'next-themes'; 4 import { useState } from 'react'; 5 - import { useIntl } from 'react-intl'; 6 7 - import { useLocale } from '@/hooks/useLocale'; 8 - import { useNavigation } from '@/hooks/useNavigation'; 9 - import { useRouter } from '@/hooks/useRouter'; 10 - 11 - import LocalizedLink from './LocalizedLink'; 12 13 const Header = () => { 14 - const { availableLocales, isCurrentLocaleRoute } = useLocale(); 15 - const { navigationItems } = useNavigation(); 16 - const { formatMessage } = useIntl(); 17 - const { asPath, basePath } = useRouter(); 18 const { theme, setTheme } = useTheme(); 19 20 - const [showLangPicker, setShowLangPicker] = useState(false); 21 22 const getLinkClassName = (href: string) => 23 classNames({ active: isCurrentLocaleRoute(href, href !== '/') }); 24 25 - const toggleLanguage = formatMessage({ 26 - id: 'components.header.buttons.toggleLanguage', 27 - }); 28 - 29 - const toggleDarkMode = formatMessage({ 30 - id: 'components.header.buttons.toggleDarkMode', 31 - }); 32 - 33 - const currentRouteLocalized = (locale: string) => 34 - asPath.replace(/^\/[a-zA-Z-]+/, `/${locale}`); 35 36 return ( 37 <header aria-label="Primary"> 38 <div className="container"> 39 - <LocalizedLink href="/" className="logo"> 40 <Image 41 priority 42 width="111" 43 height="33" 44 - src={`${basePath}/static/images/logo.svg`} 45 alt="Node.js" 46 /> 47 - </LocalizedLink> 48 49 <nav aria-label="primary"> 50 <ul className="list-divider-pipe"> 51 {navigationItems.map((item, key) => ( 52 <li key={key} className={getLinkClassName(item.link)}> 53 - <LocalizedLink href={item.link}>{item.text}</LocalizedLink> 54 </li> 55 ))} 56 </ul> ··· 69 width="28" 70 height="28" 71 className="dark-image" 72 - src={`${basePath}/static/images/light-mode.svg`} 73 alt="Dark Theme Switcher" 74 /> 75 ··· 78 width="28" 79 height="28" 80 className="light-image" 81 - src={`${basePath}/static/images/dark-mode.svg`} 82 alt="Dark Theme Switcher" 83 /> 84 </button> ··· 96 priority 97 width="25" 98 height="28" 99 - src={`${basePath}/static/images/language-picker.svg`} 100 alt="Language Switcher" 101 /> 102 </button> ··· 106 <ul className="lang-picker"> 107 {availableLocales.map(locale => ( 108 <li key={locale.code}> 109 - <a 110 title={locale.name} 111 - href={currentRouteLocalized(locale.code)} 112 > 113 {locale.localName} 114 - </a> 115 </li> 116 ))} 117 </ul>
··· 1 + 'use client'; 2 + 3 import classNames from 'classnames'; 4 import Image from 'next/image'; 5 + import { useTranslations } from 'next-intl'; 6 import { useTheme } from 'next-themes'; 7 import { useState } from 'react'; 8 9 + import { useIsCurrentPathname, useSiteNavigation } from '@/hooks'; 10 + import { Link, usePathname } from '@/navigation.mjs'; 11 + import { BASE_PATH } from '@/next.constants.mjs'; 12 + import { availableLocales } from '@/next.locales.mjs'; 13 14 const Header = () => { 15 + const { navigationItems } = useSiteNavigation(); 16 + const { isCurrentLocaleRoute } = useIsCurrentPathname(); 17 + const [showLangPicker, setShowLangPicker] = useState(false); 18 const { theme, setTheme } = useTheme(); 19 20 + const pathname = usePathname(); 21 + const t = useTranslations(); 22 23 const getLinkClassName = (href: string) => 24 classNames({ active: isCurrentLocaleRoute(href, href !== '/') }); 25 26 + const toggleLanguage = t('components.header.buttons.toggleLanguage'); 27 + const toggleDarkMode = t('components.header.buttons.toggleDarkMode'); 28 29 return ( 30 <header aria-label="Primary"> 31 <div className="container"> 32 + <Link href="/" className="logo"> 33 <Image 34 priority 35 width="111" 36 height="33" 37 + src={`${BASE_PATH}/static/images/logo.svg`} 38 alt="Node.js" 39 /> 40 + </Link> 41 42 <nav aria-label="primary"> 43 <ul className="list-divider-pipe"> 44 {navigationItems.map((item, key) => ( 45 <li key={key} className={getLinkClassName(item.link)}> 46 + <Link href={item.link}>{item.text}</Link> 47 </li> 48 ))} 49 </ul> ··· 62 width="28" 63 height="28" 64 className="dark-image" 65 + src={`${BASE_PATH}/static/images/light-mode.svg`} 66 alt="Dark Theme Switcher" 67 /> 68 ··· 71 width="28" 72 height="28" 73 className="light-image" 74 + src={`${BASE_PATH}/static/images/dark-mode.svg`} 75 alt="Dark Theme Switcher" 76 /> 77 </button> ··· 89 priority 90 width="25" 91 height="28" 92 + src={`${BASE_PATH}/static/images/language-picker.svg`} 93 alt="Language Switcher" 94 /> 95 </button> ··· 99 <ul className="lang-picker"> 100 {availableLocales.map(locale => ( 101 <li key={locale.code}> 102 + <Link 103 title={locale.name} 104 + locale={locale.code} 105 + href={pathname} 106 + onClick={() => setShowLangPicker(false)} 107 > 108 {locale.localName} 109 + </Link> 110 </li> 111 ))} 112 </ul>
+1 -4
components/Home/Banner.tsx
··· 1 - import { useSiteConfig } from '@/hooks/useSiteConfig'; 2 import { dateIsBetween } from '@/util/dateIsBetween'; 3 4 const Banner = () => { 5 - const siteConfig = useSiteConfig(); 6 - 7 - // Note.: This is hardcoded and going to be replaced by the `nodejs/nodejs.dev` codebase 8 if (siteConfig.websiteBanners && siteConfig.websiteBanners['index']) { 9 const indexBanner = siteConfig.websiteBanners['index']; 10
··· 1 + import { siteConfig } from '@/next.json.mjs'; 2 import { dateIsBetween } from '@/util/dateIsBetween'; 3 4 const Banner = () => { 5 if (siteConfig.websiteBanners && siteConfig.websiteBanners['index']) { 6 const indexBanner = siteConfig.websiteBanners['index']; 7
+18 -18
components/Home/HomeDownloadButton.tsx
··· 1 import type { FC } from 'react'; 2 - import { FormattedMessage, useIntl } from 'react-intl'; 3 4 - import LocalizedLink from '@/components/LocalizedLink'; 5 - import { useDetectOS } from '@/hooks/useDetectOS'; 6 import { DIST_URL } from '@/next.constants.mjs'; 7 import type { NodeRelease } from '@/types'; 8 import { downloadUrlByOS } from '@/util/downloadUrlByOS'; ··· 15 isLts, 16 }) => { 17 const { os, bitness } = useDetectOS(); 18 - const { formatMessage } = useIntl(); 19 20 const nodeDownloadLink = downloadUrlByOS(versionWithPrefix, os, bitness); 21 const nodeApiLink = `${DIST_URL}latest-v${major}.x/docs/api/`; 22 const nodeAllDownloadsLink = `/download${isLts ? '/' : '/current'}`; 23 24 - const downloadFile = formatMessage( 25 - { id: 'components.home.homeDownloadButton.download' }, 26 - { version, isLts } 27 - ); 28 29 return ( 30 <div className="home-downloadblock"> ··· 36 > 37 {downloadFile} 38 39 - <FormattedMessage 40 - id="components.home.homeDownloadButton.tagline" 41 - tagName="small" 42 - values={{ isLts }} 43 - /> 44 </a> 45 46 <ul className="list-divider-pipe home-secondary-links"> 47 <li> 48 - <LocalizedLink href={nodeAllDownloadsLink}> 49 - <FormattedMessage id="components.home.homeDownloadButton.otherDownloads" /> 50 - </LocalizedLink> 51 </li> 52 53 <li> 54 <a href={getNodejsChangelog(versionWithPrefix)}> 55 - <FormattedMessage id="components.home.homeDownloadButton.changelog" /> 56 </a> 57 </li> 58 59 <li> 60 <a href={nodeApiLink}> 61 - <FormattedMessage id="components.home.homeDownloadButton.apiDocs" /> 62 </a> 63 </li> 64 </ul>
··· 1 + 'use client'; 2 + 3 + import { useTranslations } from 'next-intl'; 4 import type { FC } from 'react'; 5 6 + import { useDetectOS } from '@/hooks'; 7 + import { Link } from '@/navigation.mjs'; 8 import { DIST_URL } from '@/next.constants.mjs'; 9 import type { NodeRelease } from '@/types'; 10 import { downloadUrlByOS } from '@/util/downloadUrlByOS'; ··· 17 isLts, 18 }) => { 19 const { os, bitness } = useDetectOS(); 20 + const t = useTranslations(); 21 22 const nodeDownloadLink = downloadUrlByOS(versionWithPrefix, os, bitness); 23 const nodeApiLink = `${DIST_URL}latest-v${major}.x/docs/api/`; 24 const nodeAllDownloadsLink = `/download${isLts ? '/' : '/current'}`; 25 26 + const downloadFile = t('components.home.homeDownloadButton.download', { 27 + version, 28 + isLts, 29 + }); 30 31 return ( 32 <div className="home-downloadblock"> ··· 38 > 39 {downloadFile} 40 41 + <small> 42 + {t('components.home.homeDownloadButton.tagline', { isLts })} 43 + </small> 44 </a> 45 46 <ul className="list-divider-pipe home-secondary-links"> 47 <li> 48 + <Link href={nodeAllDownloadsLink}> 49 + {t('components.home.homeDownloadButton.otherDownloads')} 50 + </Link> 51 </li> 52 53 <li> 54 <a href={getNodejsChangelog(versionWithPrefix)}> 55 + {t('components.home.homeDownloadButton.changelog')} 56 </a> 57 </li> 58 59 <li> 60 <a href={nodeApiLink}> 61 + {t('components.home.homeDownloadButton.apiDocs')} 62 </a> 63 </li> 64 </ul>
-103
components/HtmlHead.tsx
··· 1 - import Head from 'next/head'; 2 - import type { FC } from 'react'; 3 - 4 - import { useLocale } from '@/hooks/useLocale'; 5 - import { useRouter } from '@/hooks/useRouter'; 6 - import { useSiteConfig } from '@/hooks/useSiteConfig'; 7 - import { BASE_URL, BASE_PATH } from '@/next.constants.mjs'; 8 - import type { LegacyFrontMatter } from '@/types'; 9 - 10 - // This is the combination of the Application Base URL and Base PATH 11 - const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; 12 - 13 - type HeaderProps = { frontMatter: LegacyFrontMatter }; 14 - 15 - const HtmlHead: FC<HeaderProps> = ({ frontMatter }) => { 16 - const siteConfig = useSiteConfig(); 17 - const { asPath } = useRouter(); 18 - const { availableLocales, currentLocale, defaultLocale } = useLocale(); 19 - 20 - const canonicalLink = `${baseUrlAndPath}${asPath}`; 21 - 22 - const pageTitle = frontMatter.title 23 - ? `${frontMatter.title} | ${siteConfig.title}` 24 - : siteConfig.title; 25 - 26 - return ( 27 - <Head> 28 - <title>{pageTitle}</title> 29 - 30 - <meta name="theme-color" content={siteConfig.accentColor}></meta> 31 - 32 - <meta name="robots" content={frontMatter.robots || 'index, follow'} /> 33 - 34 - <meta name="viewport" content="width=device-width, initial-scale=1" /> 35 - <meta name="description" content={siteConfig.description} /> 36 - 37 - <meta property="og:title" content={pageTitle} /> 38 - <meta property="og:site_name" content={siteConfig.title} /> 39 - <meta property="og:description" content={siteConfig.description} /> 40 - 41 - <meta 42 - property="og:image" 43 - content={`${baseUrlAndPath}${siteConfig.featuredImage}`} 44 - /> 45 - 46 - <meta property="og:image:type" content={siteConfig.og.imgType} /> 47 - <meta property="og:image:width" content={siteConfig.og.imgWidth} /> 48 - <meta property="og:image:height" content={siteConfig.og.imgHeight} /> 49 - 50 - <meta name="twitter:card" content={siteConfig.twitter.card} /> 51 - <meta name="twitter:site" content={siteConfig.twitter.username} /> 52 - <meta name="twitter:title" content={pageTitle} /> 53 - 54 - <meta 55 - name="twitter:image" 56 - content={`${baseUrlAndPath}${siteConfig.twitter.img}`} 57 - /> 58 - 59 - <meta name="twitter:image:alt" content={siteConfig.twitter.imgAlt} /> 60 - 61 - <link rel="canonical" href={canonicalLink} /> 62 - 63 - <link 64 - rel="icon" 65 - href={`${baseUrlAndPath}${siteConfig.favicon}`} 66 - type="image/png" 67 - /> 68 - 69 - <link 70 - rel="alternate" 71 - hrefLang="x-default" 72 - href={canonicalLink.replace( 73 - `/${currentLocale.code}`, 74 - `/${defaultLocale.code}` 75 - )} 76 - /> 77 - 78 - {availableLocales.map(locale => ( 79 - <link 80 - key={locale.code} 81 - rel="alternate" 82 - hrefLang={locale.code} 83 - href={canonicalLink.replace( 84 - `/${currentLocale.code}`, 85 - `/${locale.code}` 86 - )} 87 - /> 88 - ))} 89 - 90 - {siteConfig.rssFeeds.map(feed => ( 91 - <link 92 - key={feed.file} 93 - title={feed.title} 94 - href={`${baseUrlAndPath}/en/feed/${feed.file}`} 95 - rel="alternate" 96 - type="application/rss+xml" 97 - /> 98 - ))} 99 - </Head> 100 - ); 101 - }; 102 - 103 - export default HtmlHead;
···
-47
components/LocalizedLink.tsx
··· 1 - import Link from 'next/link'; 2 - import { useMemo } from 'react'; 3 - import type { FC, ComponentProps, HTMLAttributes } from 'react'; 4 - 5 - import { useLocale } from '@/hooks/useLocale'; 6 - import { linkWithLocale } from '@/util/linkWithLocale'; 7 - 8 - // This is a wrapper on HTML's `a` tag 9 - const HtmlLink: FC<HTMLAttributes<HTMLAnchorElement>> = ({ 10 - children, 11 - ...extra 12 - }) => <a {...extra}>{children}</a>; 13 - 14 - // This is Next.js's Link Component but with pre-fetch disabled 15 - const NextLink: FC<ComponentProps<typeof Link>> = ({ children, ...extra }) => ( 16 - <Link {...extra} prefetch={false}> 17 - {children} 18 - </Link> 19 - ); 20 - 21 - const LocalizedLink: FC<ComponentProps<typeof Link>> = ({ 22 - href, 23 - children, 24 - ...extra 25 - }) => { 26 - const { currentLocale } = useLocale(); 27 - 28 - const { Component, finalHref } = useMemo(() => { 29 - if (!href || !href.toString().startsWith('/')) { 30 - return { Component: HtmlLink, finalHref: href }; 31 - } 32 - 33 - const addLocaleToHref = linkWithLocale(currentLocale.code); 34 - 35 - return { Component: NextLink, finalHref: addLocaleToHref(href) }; 36 - // We only need to check if the toString() variant of URL has changed 37 - // eslint-disable-next-line react-hooks/exhaustive-deps 38 - }, [currentLocale.code, href]); 39 - 40 - return ( 41 - <Component {...extra} href={finalHref}> 42 - {children} 43 - </Component> 44 - ); 45 - }; 46 - 47 - export default LocalizedLink;
···
+21 -17
components/Pagination.tsx
··· 1 import type { FC } from 'react'; 2 - import { FormattedMessage } from 'react-intl'; 3 4 - import LocalizedLink from './LocalizedLink'; 5 6 - type PaginationProps = { prevSlug?: number; nextSlug?: number }; 7 8 - const Pagination: FC<PaginationProps> = ({ nextSlug, prevSlug }) => ( 9 - <nav aria-label="pagination" className="pagination"> 10 - {nextSlug && ( 11 - <LocalizedLink href={`/blog/year-${nextSlug}`}> 12 - &lt; <FormattedMessage id="components.pagination.next" /> 13 - </LocalizedLink> 14 - )} 15 16 - {prevSlug && ( 17 - <LocalizedLink href={`/blog/year-${prevSlug}`}> 18 - <FormattedMessage id="components.pagination.previous" /> &gt; 19 - </LocalizedLink> 20 - )} 21 - </nav> 22 - ); 23 24 export default Pagination;
··· 1 + import { useTranslations } from 'next-intl'; 2 import type { FC } from 'react'; 3 4 + import { Link } from '@/navigation.mjs'; 5 6 + type PaginationProps = { prev?: number; next?: number }; 7 8 + const Pagination: FC<PaginationProps> = ({ next, prev }) => { 9 + const t = useTranslations(); 10 11 + return ( 12 + <nav aria-label="pagination" className="pagination"> 13 + {next && ( 14 + <Link href={`/blog/year-${next}`}> 15 + &lt; {t('components.pagination.next')} 16 + </Link> 17 + )} 18 + 19 + {prev && ( 20 + <Link href={`/blog/year-${prev}`}> 21 + {t('components.pagination.previous')} &gt; 22 + </Link> 23 + )} 24 + </nav> 25 + ); 26 + }; 27 28 export default Pagination;
components/Sections/Footer/index.module.css components/Containers/Footer/index.module.css
+1 -1
components/Sections/Footer/index.stories.tsx components/Containers/Footer/index.stories.tsx
··· 1 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 2 3 - import Footer from './index'; 4 5 type Story = StoryObj<typeof Footer>; 6 type Meta = MetaObj<typeof Footer>;
··· 1 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 2 3 + import Footer from '@/components/Containers/Footer'; 4 5 type Story = StoryObj<typeof Footer>; 6 type Meta = MetaObj<typeof Footer>;
+9 -9
components/Sections/Footer/index.tsx components/Containers/Footer/index.tsx
··· 1 import classNames from 'classnames'; 2 import Image from 'next/image'; 3 import type { FC } from 'react'; 4 - import { FormattedMessage } from 'react-intl'; 5 6 - import NavItem from '@/components/Sections/NavItem'; 7 - import { useSiteConfig } from '@/hooks/useSiteConfig'; 8 9 import styles from './index.module.css'; 10 11 const Footer: FC = () => { 12 - const { footerLinks, socialLinks } = useSiteConfig(); 13 14 - const openJSlink = footerLinks.at(-1)!; 15 16 return ( 17 <footer className={styles.footer}> 18 <div className={styles.sectionPrimary}> 19 - {footerLinks.slice(0, -1).map(item => ( 20 <NavItem type="footer" href={item.link} key={item.link}> 21 - <FormattedMessage id={item.text} /> 22 </NavItem> 23 ))} 24 </div> 25 <div className={styles.sectionSecondary}> 26 <NavItem type="footer" href={openJSlink.link}> 27 - &copy; <FormattedMessage id={openJSlink.text} /> 28 </NavItem> 29 <div className={styles.social}> 30 - {socialLinks.map(link => { 31 const navClass = classNames({ 32 [styles.darkImage]: link.kind === 'dark', 33 [styles.lightImage]: link.kind === 'light',
··· 1 import classNames from 'classnames'; 2 import Image from 'next/image'; 3 + import { useTranslations } from 'next-intl'; 4 import type { FC } from 'react'; 5 6 + import NavItem from '@/components/Containers/NavItem'; 7 + import { siteConfig } from '@/next.json.mjs'; 8 9 import styles from './index.module.css'; 10 11 const Footer: FC = () => { 12 + const t = useTranslations(); 13 14 + const openJSlink = siteConfig.footerLinks.at(-1)!; 15 16 return ( 17 <footer className={styles.footer}> 18 <div className={styles.sectionPrimary}> 19 + {siteConfig.footerLinks.slice(0, -1).map(item => ( 20 <NavItem type="footer" href={item.link} key={item.link}> 21 + {t(item.text)} 22 </NavItem> 23 ))} 24 </div> 25 <div className={styles.sectionSecondary}> 26 <NavItem type="footer" href={openJSlink.link}> 27 + &copy; {t(openJSlink.text)} 28 </NavItem> 29 <div className={styles.social}> 30 + {siteConfig.socialLinks.map(link => { 31 const navClass = classNames({ 32 [styles.darkImage]: link.kind === 'dark', 33 [styles.lightImage]: link.kind === 'light',
components/Sections/NavItem/index.module.css components/Containers/NavItem/index.module.css
+1 -1
components/Sections/NavItem/index.stories.tsx components/Containers/NavItem/index.stories.tsx
··· 1 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 2 3 - import NavItem from './index'; 4 5 type Story = StoryObj<typeof NavItem>; 6 type Meta = MetaObj<typeof NavItem>;
··· 1 import type { Meta as MetaObj, StoryObj } from '@storybook/react'; 2 3 + import NavItem from '@/components/Containers/NavItem'; 4 5 type Story = StoryObj<typeof NavItem>; 6 type Meta = MetaObj<typeof NavItem>;
components/Sections/NavItem/index.tsx components/Containers/NavItem/index.tsx
+10 -9
components/SideNavigation.tsx
··· 1 import classNames from 'classnames'; 2 import type { FC } from 'react'; 3 4 - import { useLocale } from '@/hooks/useLocale'; 5 - import { useNavigation } from '@/hooks/useNavigation'; 6 import type { NavigationKeys } from '@/types'; 7 8 - import LocalizedLink from './LocalizedLink'; 9 - 10 type SideNavigationProps = { 11 navigationKey: NavigationKeys; 12 - context?: Record<string, Record<string, string | JSX.Element | undefined>>; 13 }; 14 15 const SideNavigation: FC<SideNavigationProps> = ({ 16 navigationKey, 17 context, 18 }) => { 19 - const { getSideNavigation } = useNavigation(); 20 - const { isCurrentLocaleRoute } = useLocale(); 21 22 const sideNavigationItems = getSideNavigation(navigationKey, context); 23 ··· 29 <ul> 30 {sideNavigationItems.map(item => ( 31 <li key={item.key} className={getLinkClasses(item.link, item.level)}> 32 - <LocalizedLink href={item.link}>{item.text}</LocalizedLink> 33 34 {item.items.length > 0 && ( 35 <ul> 36 {item.items.map(({ link, level, text, key }) => ( 37 <li key={key} className={getLinkClasses(link, level)}> 38 - <LocalizedLink href={link}>{text}</LocalizedLink> 39 </li> 40 ))} 41 </ul>
··· 1 + 'use client'; 2 + 3 import classNames from 'classnames'; 4 + import type { RichTranslationValues } from 'next-intl'; 5 import type { FC } from 'react'; 6 7 + import { useIsCurrentPathname, useSiteNavigation } from '@/hooks'; 8 + import { Link } from '@/navigation.mjs'; 9 import type { NavigationKeys } from '@/types'; 10 11 type SideNavigationProps = { 12 navigationKey: NavigationKeys; 13 + context?: Record<string, RichTranslationValues>; 14 }; 15 16 const SideNavigation: FC<SideNavigationProps> = ({ 17 navigationKey, 18 context, 19 }) => { 20 + const { getSideNavigation } = useSiteNavigation(); 21 + const { isCurrentLocaleRoute } = useIsCurrentPathname(); 22 23 const sideNavigationItems = getSideNavigation(navigationKey, context); 24 ··· 30 <ul> 31 {sideNavigationItems.map(item => ( 32 <li key={item.key} className={getLinkClasses(item.link, item.level)}> 33 + <Link href={item.link}>{item.text}</Link> 34 35 {item.items.length > 0 && ( 36 <ul> 37 {item.items.map(({ link, level, text, key }) => ( 38 <li key={key} className={getLinkClasses(link, level)}> 39 + <Link href={link}>{text}</Link> 40 </li> 41 ))} 42 </ul>
+35
components/__mocks__/next-intl.mjs
···
··· 1 + import Link from 'next/link'; 2 + import { redirect, usePathname, useRouter } from 'next/navigation'; 3 + 4 + export const useMessages = () => ({}); 5 + 6 + export const useNow = () => new Date(); 7 + 8 + export const useTimeZone = () => 'Etc/UTC'; 9 + 10 + export const useTranslations = () => { 11 + const t = key => key; 12 + 13 + t.rich = key => key; 14 + t.markup = key => key; 15 + 16 + return t; 17 + }; 18 + 19 + export const useFormatter = () => { 20 + const formatter = format => new Intl.DateTimeFormat('en-US', format); 21 + 22 + return { 23 + date: (date, format) => formatter(format).format(date), 24 + dateTime: (date, format) => formatter(format).format(date), 25 + }; 26 + }; 27 + 28 + export const NextIntlClientProvider = ({ children }) => children; 29 + 30 + export const createSharedPathnamesNavigation = () => ({ 31 + Link: Link, 32 + redirect: redirect, 33 + usePathname: usePathname, 34 + useRouter: useRouter, 35 + });
+14
components/mdxRenderer.tsx
···
··· 1 + import type { MDXComponents, MDXContent } from 'mdx/types'; 2 + import type { FC } from 'react'; 3 + 4 + import { htmlComponents, mdxComponents } from '@/next.mdx.use.mjs'; 5 + 6 + // Combine all MDX Components to be used 7 + const combinedComponents: MDXComponents = { 8 + ...htmlComponents, 9 + ...mdxComponents, 10 + }; 11 + 12 + export const MDXRenderer: FC<{ Component: MDXContent }> = ({ Component }) => ( 13 + <Component components={combinedComponents} /> 14 + );
+15
components/withCurrentOS.tsx
···
··· 1 + 'use client'; 2 + 3 + import type { FC } from 'react'; 4 + 5 + import { useDetectOS } from '@/hooks'; 6 + 7 + type WithCurrentOS = { 8 + children: FC<{ os: ReturnType<typeof useDetectOS> }>; 9 + }; 10 + 11 + export const WithCurrentOS: FC<WithCurrentOS> = ({ children: Component }) => { 12 + const osData = useDetectOS(); 13 + 14 + return <Component os={osData} />; 15 + };
+35
components/withLayout.tsx
···
··· 1 + import { useMemo, type FC, type PropsWithChildren } from 'react'; 2 + 3 + import AboutLayout from '@/layouts/AboutLayout'; 4 + import BlogCategoryLayout from '@/layouts/BlogCategoryLayout'; 5 + import BlogPostLayout from '@/layouts/BlogPostLayout'; 6 + import ContributeLayout from '@/layouts/ContributeLayout'; 7 + import DefaultLayout from '@/layouts/DefaultLayout'; 8 + import DocsLayout from '@/layouts/DocsLayout'; 9 + import DownloadLayout from '@/layouts/DownloadLayout'; 10 + import IndexLayout from '@/layouts/IndexLayout'; 11 + import LearnLayout from '@/layouts/LearnLayout'; 12 + import type { LegacyLayouts } from '@/types'; 13 + 14 + const layoutComponents = { 15 + 'docs.hbs': DocsLayout, 16 + 'about.hbs': AboutLayout, 17 + 'blog-categpry.hbs': BlogCategoryLayout, 18 + 'blog-post.hbs': BlogPostLayout, 19 + 'contribute.hbs': ContributeLayout, 20 + 'download.hbs': DownloadLayout, 21 + 'index.hbs': IndexLayout, 22 + 'learn.hbs': LearnLayout, 23 + 'page.hbs': DefaultLayout, 24 + } satisfies Record<string, FC>; 25 + 26 + type WithLayoutProps = PropsWithChildren<{ layout: LegacyLayouts }>; 27 + 28 + export const WithLayout: FC<WithLayoutProps> = ({ layout, children }) => { 29 + const LayoutComponent = useMemo( 30 + () => layoutComponents[layout] || DefaultLayout, 31 + [layout] 32 + ); 33 + 34 + return <LayoutComponent>{children}</LayoutComponent>; 35 + };
+20
components/withNodeRelease.tsx
···
··· 1 + import type { FC } from 'react'; 2 + 3 + import { releaseData } from '@/next.json.mjs'; 4 + import type { NodeRelease, NodeReleaseStatus } from '@/types'; 5 + 6 + type WithNodeReleaseProps = { 7 + status: NodeReleaseStatus[] | NodeReleaseStatus; 8 + children: FC<{ release: NodeRelease }>; 9 + }; 10 + 11 + export const WithNodeRelease: FC<WithNodeReleaseProps> = ({ 12 + status, 13 + children: Component, 14 + }) => { 15 + const matchingRelease = releaseData.find(release => 16 + [status].flat().includes(release.status) 17 + ); 18 + 19 + return <Component release={matchingRelease!} />; 20 + };
+4 -16
hooks/__tests__/useCopyToClipboard.test.mjs
··· 1 import { render, fireEvent, screen, act } from '@testing-library/react'; 2 - import { FormattedMessage } from 'react-intl'; 3 - import { IntlProvider } from 'react-intl'; 4 5 - import { useCopyToClipboard } from '../useCopyToClipboard'; 6 7 const mockWriteText = jest.fn(); 8 const originalNavigator = { ...window.navigator }; 9 - 10 - const testMessages = { 11 - 'components.common.shellBox.copy': 12 - '{copied, select, true {copied}other {copy}}', 13 - }; 14 15 describe('useCopyToClipboard', () => { 16 beforeEach(() => { ··· 47 const [copied, copyText] = useCopyToClipboard(); 48 49 return ( 50 - <IntlProvider locale="en" messages={testMessages} onError={() => {}}> 51 - <button onClick={() => copyText(textToCopy)} type="button"> 52 - <FormattedMessage 53 - id="components.common.shellBox.copy" 54 - values={{ copied }} 55 - /> 56 - </button> 57 - </IntlProvider> 58 ); 59 }; 60
··· 1 import { render, fireEvent, screen, act } from '@testing-library/react'; 2 3 + import { useCopyToClipboard } from '..'; 4 5 const mockWriteText = jest.fn(); 6 const originalNavigator = { ...window.navigator }; 7 8 describe('useCopyToClipboard', () => { 9 beforeEach(() => { ··· 40 const [copied, copyText] = useCopyToClipboard(); 41 42 return ( 43 + <button onClick={() => copyText(textToCopy)} type="button"> 44 + {copied ? 'copied' : 'copy'} 45 + </button> 46 ); 47 }; 48
+1 -1
hooks/__tests__/useDetectOS.test.mjs
··· 1 import { renderHook, waitFor } from '@testing-library/react'; 2 3 - import { useDetectOS } from '../useDetectOS'; 4 5 const windowsUserAgent = 6 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36';
··· 1 import { renderHook, waitFor } from '@testing-library/react'; 2 3 + import { useDetectOS } from '..'; 4 5 const windowsUserAgent = 6 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36';
+1 -1
hooks/__tests__/useMediaQuery.test.mjs
··· 1 import { renderHook } from '@testing-library/react'; 2 3 - import { useMediaQuery } from '../useMediaQuery'; 4 5 describe('useMediaQuery', () => { 6 it('should check for matchMedia support', () => {
··· 1 import { renderHook } from '@testing-library/react'; 2 3 + import { useMediaQuery } from '..'; 4 5 describe('useMediaQuery', () => { 6 it('should check for matchMedia support', () => {
+1
hooks/index.ts
···
··· 1 + export * from './react-client';
+8
hooks/react-client/index.ts
···
··· 1 + export { default as useCopyToClipboard } from './useCopyToClipboard'; 2 + export { default as useDetectOS } from './useDetectOS'; 3 + export { default as useIsCurrentPathname } from './useIsCurrentPathname'; 4 + export { default as useMediaQuery } from './useMediaQuery'; 5 + export { default as useNotification } from './useNotification'; 6 + export { default as useClientContext } from './useClientContext'; 7 + export { default as useBlogData } from './useBlogData'; 8 + export { default as useSiteNavigation } from './useSiteNavigation';
+13
hooks/react-client/useBlogData.ts
···
··· 1 + 'use client'; 2 + 3 + import useClientContext from '@/hooks/react-client/useClientContext'; 4 + import { useBaseBlogData } from '@/hooks/useBaseBlogData'; 5 + 6 + const useBlogData = () => { 7 + const { pathname } = useClientContext(); 8 + const data = useBaseBlogData(pathname); 9 + 10 + return data; 11 + }; 12 + 13 + export default useBlogData;
+16
hooks/react-client/useClientContext.ts
···
··· 1 + 'use client'; 2 + 3 + import { useContext } from 'react'; 4 + 5 + import { usePathname } from '@/navigation.mjs'; 6 + import { MatterContext } from '@/providers/matterProvider'; 7 + import type { ClientSharedServerContext } from '@/types'; 8 + 9 + const useClientContext = (): ClientSharedServerContext => { 10 + const { matter: frontmatter, headings } = useContext(MatterContext); 11 + const pathname = usePathname(); 12 + 13 + return { pathname: pathname || '', frontmatter, headings }; 14 + }; 15 + 16 + export default useClientContext;
+14
hooks/react-client/useIsCurrentPathname.ts
···
··· 1 + 'use client'; 2 + 3 + import useClientContext from '@/hooks/react-client/useClientContext'; 4 + 5 + const useIsCurrentPathname = () => { 6 + const { pathname } = useClientContext(); 7 + 8 + return { 9 + isCurrentLocaleRoute: (route: string, allowSubPath?: boolean) => 10 + allowSubPath ? pathname.startsWith(route) : route === pathname, 11 + }; 12 + }; 13 + 14 + export default useIsCurrentPathname;
+9
hooks/react-client/useNotification.ts
···
··· 1 + 'use client'; 2 + 3 + import { useContext } from 'react'; 4 + 5 + import { NotificationDispatch } from '@/providers/notificationProvider'; 6 + 7 + const useNotification = () => useContext(NotificationDispatch); 8 + 9 + export default useNotification;
+5
hooks/react-client/useSiteNavigation.tsx
···
··· 1 + 'use client'; 2 + 3 + import useBaseSiteNavigation from '@/hooks/useBaseSiteNavigation'; 4 + 5 + export default useBaseSiteNavigation;
+8
hooks/react-server/index.ts
···
··· 1 + export { default as useCopyToClipboard } from './useCopyToClipboard'; 2 + export { default as useDetectOS } from './useDetectOS'; 3 + export { default as useIsCurrentPathname } from './useIsCurrentPathname'; 4 + export { default as useMediaQuery } from './useMediaQuery'; 5 + export { default as useNotification } from './useNotification'; 6 + export { default as useClientContext } from './useClientContext'; 7 + export { default as useBlogData } from './useBlogData'; 8 + export { default as useSiteNavigation } from './useSiteNavigation';
+11
hooks/react-server/useBlogData.ts
···
··· 1 + import useClientContext from '@/hooks/react-server/useClientContext'; 2 + import { useBaseBlogData } from '@/hooks/useBaseBlogData'; 3 + 4 + const useBlogData = () => { 5 + const { pathname } = useClientContext(); 6 + const data = useBaseBlogData(pathname); 7 + 8 + return data; 9 + }; 10 + 11 + export default useBlogData;
+5
hooks/react-server/useClientContext.ts
···
··· 1 + import { getClientContext } from '@/client-context'; 2 + 3 + const useClientContext = () => getClientContext(); 4 + 5 + export default useClientContext;
+5
hooks/react-server/useCopyToClipboard.ts
···
··· 1 + const useCopyToClipboard = () => { 2 + throw new Error('Attempted to call useCopyToClipboard from RSC'); 3 + }; 4 + 5 + export default useCopyToClipboard;
+5
hooks/react-server/useDetectOS.ts
···
··· 1 + const useDetectOS = () => { 2 + throw new Error('Attempted to call useDetectOS from RSC'); 3 + }; 4 + 5 + export default useDetectOS;
+17
hooks/react-server/useIsCurrentPathname.ts
···
··· 1 + import useClientContext from '@/hooks/react-server/useClientContext'; 2 + 3 + const useIsCurrentPathname = () => { 4 + const { pathname } = useClientContext(); 5 + 6 + return { 7 + isCurrentLocaleRoute: (route: string, allowSubPath?: boolean) => { 8 + const asPathJustPath = pathname.replace(/[#|?].*$/, ''); 9 + 10 + return allowSubPath 11 + ? asPathJustPath.startsWith(route) 12 + : route === asPathJustPath; 13 + }, 14 + }; 15 + }; 16 + 17 + export default useIsCurrentPathname;
+5
hooks/react-server/useMediaQuery.ts
···
··· 1 + const useMediaQuery = () => { 2 + throw new Error('Attempted to call useMediaQuery from RSC'); 3 + }; 4 + 5 + export default useMediaQuery;
+5
hooks/react-server/useNotification.ts
···
··· 1 + const useNotification = () => { 2 + throw new Error('Attempted to call useNotification from RSC'); 3 + }; 4 + 5 + export default useNotification;
+3
hooks/react-server/useSiteNavigation.tsx
···
··· 1 + import useBaseSiteNavigation from '@/hooks/useBaseSiteNavigation'; 2 + 3 + export default useBaseSiteNavigation;
+1
hooks/server.ts
···
··· 1 + export * from './react-server';
+52
hooks/useBaseBlogData.ts
···
··· 1 + import { useMemo } from 'react'; 2 + 3 + import { blogData } from '@/next.json.mjs'; 4 + 5 + export const useBaseBlogData = (pathname: string) => { 6 + const { posts, pagination, categories } = blogData; 7 + 8 + const getPostsByCategory = (category: string) => 9 + posts.filter(post => post.category === category); 10 + 11 + const getPostsByYear = (year: string) => 12 + posts.filter(post => new Date(post.date).getFullYear() === Number(year)); 13 + 14 + const getPagination = (currentYear: number | string) => { 15 + const _currentYear = Number(currentYear); 16 + 17 + return { 18 + next: pagination.includes(_currentYear + 1) 19 + ? _currentYear + 1 20 + : undefined, 21 + prev: pagination.includes(_currentYear - 1) 22 + ? _currentYear - 1 23 + : undefined, 24 + }; 25 + }; 26 + 27 + const currentCategory = useMemo(() => { 28 + // We split the pathname to retrieve the blog category from it 29 + // since the URL is usually /{languageCode}/blog/{category} 30 + // the third path piece is usually the category name 31 + const [, _pathname, category] = pathname.split('/'); 32 + 33 + if (_pathname === 'blog' && category && category.length) { 34 + return category; 35 + } 36 + 37 + // if either the pathname does not match to a blog page 38 + // which should not happen (as this hook should only be used in blog pages) 39 + // or if there is no category in the URL we return the current year as category name 40 + // which is always the default category (for example, the blog index) 41 + return `year-${new Date().getFullYear()}`; 42 + }, [pathname]); 43 + 44 + return { 45 + posts, 46 + categories, 47 + currentCategory, 48 + getPostsByCategory, 49 + getPostsByYear, 50 + getPagination, 51 + }; 52 + };
-56
hooks/useBlogData.ts
··· 1 - import { useCallback, useContext, useMemo } from 'react'; 2 - 3 - import { BlogDataContext } from '@/providers/blogDataProvider'; 4 - 5 - import { useRouter } from './useRouter'; 6 - 7 - export const useBlogData = () => { 8 - const { asPath } = useRouter(); 9 - 10 - const { posts, pagination, categories } = useContext(BlogDataContext); 11 - 12 - const getPostsByCategory = useCallback( 13 - (category: string) => posts.filter(post => post.category === category), 14 - [posts] 15 - ); 16 - 17 - const getPostsByYear = useCallback( 18 - (year: number) => 19 - posts.filter(post => new Date(post.date).getFullYear() === year), 20 - [posts] 21 - ); 22 - 23 - const getPagination = useCallback( 24 - (currentYear: number) => ({ 25 - next: pagination.includes(currentYear + 1) ? currentYear + 1 : undefined, 26 - prev: pagination.includes(currentYear - 1) ? currentYear - 1 : undefined, 27 - }), 28 - [pagination] 29 - ); 30 - 31 - const currentCategory = useMemo(() => { 32 - // We split the pathname to retrieve the blog category from it 33 - // since the URL is usually /{languageCode}/blog/{category} 34 - // the third path piece is usually the category name 35 - const [, , pathname, category] = asPath.split('/'); 36 - 37 - if (pathname === 'blog' && category && category.length) { 38 - return category; 39 - } 40 - 41 - // if either the pathname does not match to a blog page 42 - // which should not happen (as this hook should only be used in blog pages) 43 - // or if there is no category in the URL we return the current year as category name 44 - // which is always the default category (for example, the blog index) 45 - return new Date().getFullYear().toString(); 46 - }, [asPath]); 47 - 48 - return { 49 - posts, 50 - categories, 51 - currentCategory, 52 - getPostsByCategory, 53 - getPostsByYear, 54 - getPagination, 55 - }; 56 - };
···
+5 -1
hooks/useCopyToClipboard.ts hooks/react-client/useCopyToClipboard.ts
··· 1 import { useEffect, useState } from 'react'; 2 3 const copyToClipboard = (value: string | undefined) => { ··· 11 .catch(() => false); 12 }; 13 14 - export const useCopyToClipboard = () => { 15 const [copied, setCopied] = useState(false); 16 17 const copyText = (text: string | undefined) => ··· 29 30 return [copied, copyText] as const; 31 };
··· 1 + 'use client'; 2 + 3 import { useEffect, useState } from 'react'; 4 5 const copyToClipboard = (value: string | undefined) => { ··· 13 .catch(() => false); 14 }; 15 16 + const useCopyToClipboard = () => { 17 const [copied, setCopied] = useState(false); 18 19 const copyText = (text: string | undefined) => ··· 31 32 return [copied, copyText] as const; 33 }; 34 + 35 + export default useCopyToClipboard;
+5 -1
hooks/useDetectOS.ts hooks/react-client/useDetectOS.ts
··· 1 import { useEffect, useState } from 'react'; 2 3 import type { UserOS } from '@/types/userOS'; ··· 9 bitness: number; 10 }; 11 12 - export const useDetectOS = () => { 13 const [userOSState, setUserOSState] = useState<UserOSState>({ 14 os: 'OTHER', 15 bitness: 86, ··· 33 34 return userOSState; 35 };
··· 1 + 'use client'; 2 + 3 import { useEffect, useState } from 'react'; 4 5 import type { UserOS } from '@/types/userOS'; ··· 11 bitness: number; 12 }; 13 14 + const useDetectOS = () => { 15 const [userOSState, setUserOSState] = useState<UserOSState>({ 16 os: 'OTHER', 17 bitness: 86, ··· 35 36 return userOSState; 37 }; 38 + 39 + export default useDetectOS;
-5
hooks/useLayoutContext.ts
··· 1 - import { useContext } from 'react'; 2 - 3 - import { LayoutContext } from '@/providers/layoutProvider'; 4 - 5 - export const useLayoutContext = () => useContext(LayoutContext);
···
-32
hooks/useLocale.ts
··· 1 - import { useContext } from 'react'; 2 - 3 - import { LocaleContext } from '@/providers/localeProvider'; 4 - import { linkWithLocale } from '@/util/linkWithLocale'; 5 - 6 - import { useRouter } from './useRouter'; 7 - 8 - export const useLocale = () => { 9 - const { asPath } = useRouter(); 10 - 11 - const { currentLocale, availableLocales, defaultLocale } = 12 - useContext(LocaleContext); 13 - 14 - const localizedLink = linkWithLocale(currentLocale.code); 15 - 16 - const localisedPath = (route: string) => 17 - localizedLink(route).replace(/[#|?].*$/, ''); 18 - 19 - return { 20 - availableLocales, 21 - currentLocale, 22 - defaultLocale, 23 - isCurrentLocaleRoute: (route: string, allowSubPath?: boolean) => { 24 - const localisedRoute = localisedPath(route); 25 - const asPathJustPath = asPath.replace(/[#|?].*$/, ''); 26 - 27 - return allowSubPath 28 - ? asPathJustPath.startsWith(localisedRoute) 29 - : localisedRoute === asPathJustPath; 30 - }, 31 - }; 32 - };
···
+6 -2
hooks/useMediaQuery.ts hooks/react-client/useMediaQuery.ts
··· 1 import { useState, useEffect } from 'react'; 2 3 const mediaQueryChangeSubscribe = (mq: MediaQueryList, handler: () => void) => { ··· 19 } 20 }; 21 22 - export function useMediaQuery(query: string): boolean | undefined { 23 const [matches, setMatches] = useState<boolean>(); 24 25 useEffect(() => { ··· 37 }, [query]); 38 39 return matches; 40 - }
··· 1 + 'use client'; 2 + 3 import { useState, useEffect } from 'react'; 4 5 const mediaQueryChangeSubscribe = (mq: MediaQueryList, handler: () => void) => { ··· 21 } 22 }; 23 24 + const useMediaQuery = (query: string): boolean | undefined => { 25 const [matches, setMatches] = useState<boolean>(); 26 27 useEffect(() => { ··· 39 }, [query]); 40 41 return matches; 42 + }; 43 + 44 + export default useMediaQuery;
+30 -16
hooks/useNavigation.tsx hooks/useBaseSiteNavigation.tsx
··· 1 - import { FormattedMessage } from 'react-intl'; 2 3 import { siteNavigation } from '@/next.json.mjs'; 4 import type { NavigationEntry, NavigationKeys } from '@/types'; 5 6 - // Translation Context for FormattedMessage 7 - type Context = Record<string, Record<string, string | JSX.Element | undefined>>; 8 - 9 // These are mapped navigation entries. Navigation Entries can have sub-entries 10 type MappedItems = { 11 - text: JSX.Element; 12 link: string; 13 key: string; 14 level: number; ··· 17 18 // Provides Context replacement for variables within the Link. This is also something that is not going 19 // to happen in the future with `nodejs/nodejs.dev` codebase 20 - const replaceLinkWithContext = ( 21 - link: string, 22 - context: Record<string, string | JSX.Element | undefined> 23 - ) => 24 Object.entries(context).reduce( 25 (finalLink, [find, replace]) => 26 finalLink.replace( ··· 30 link 31 ); 32 33 - export const useNavigation = () => { 34 const mapNavigationEntries = ( 35 entries: Record<string, NavigationEntry>, 36 - context?: Context, 37 level = 0 38 ): MappedItems[] => { 39 const getContext = (key: string) => (context && context[key]) || {}; 40 41 - const getFormattedMessage = (translationId: string, key: string) => ( 42 - <FormattedMessage id={translationId} values={getContext(key)} /> 43 - ); 44 45 return Object.entries(entries).map(([key, item]) => ({ 46 text: getFormattedMessage(item.translationId, key), ··· 53 })); 54 }; 55 56 return { 57 - navigationItems: mapNavigationEntries(siteNavigation), 58 - getSideNavigation: (section: NavigationKeys, context?: Context) => { 59 const { items, translationId, link } = siteNavigation[section]; 60 61 return mapNavigationEntries( ··· 65 }, 66 }; 67 };
··· 1 + import { useTranslations } from 'next-intl'; 2 + import type { RichTranslationValues } from 'next-intl'; 3 + import { useMemo } from 'react'; 4 5 import { siteNavigation } from '@/next.json.mjs'; 6 import type { NavigationEntry, NavigationKeys } from '@/types'; 7 8 // These are mapped navigation entries. Navigation Entries can have sub-entries 9 type MappedItems = { 10 + text: ReturnType<ReturnType<typeof useTranslations>['rich']>; 11 link: string; 12 key: string; 13 level: number; ··· 16 17 // Provides Context replacement for variables within the Link. This is also something that is not going 18 // to happen in the future with `nodejs/nodejs.dev` codebase 19 + const replaceLinkWithContext = (link: string, context: RichTranslationValues) => 20 Object.entries(context).reduce( 21 (finalLink, [find, replace]) => 22 finalLink.replace( ··· 26 link 27 ); 28 29 + const useBaseSiteNavigation = () => { 30 + const t = useTranslations(); 31 + 32 const mapNavigationEntries = ( 33 entries: Record<string, NavigationEntry>, 34 + context?: Record<string, RichTranslationValues>, 35 level = 0 36 ): MappedItems[] => { 37 const getContext = (key: string) => (context && context[key]) || {}; 38 39 + const getFormattedMessage = (translationId: string, key: string) => 40 + t.rich(translationId, getContext(key)); 41 42 return Object.entries(entries).map(([key, item]) => ({ 43 text: getFormattedMessage(item.translationId, key), ··· 50 })); 51 }; 52 53 + const rootNavigationItems = useMemo( 54 + () => 55 + Object.entries(siteNavigation).reduce( 56 + (acc, [key, { translationId, link }]) => ({ 57 + ...acc, 58 + [key]: { translationId, link }, 59 + }), 60 + {} 61 + ), 62 + [] 63 + ); 64 + 65 return { 66 + navigationItems: mapNavigationEntries(rootNavigationItems), 67 + getSideNavigation: ( 68 + section: NavigationKeys, 69 + context?: Record<string, RichTranslationValues> 70 + ) => { 71 const { items, translationId, link } = siteNavigation[section]; 72 73 return mapNavigationEntries( ··· 77 }, 78 }; 79 }; 80 + 81 + export default useBaseSiteNavigation;
-21
hooks/useNodeReleases.ts
··· 1 - import { useCallback, useContext } from 'react'; 2 - 3 - import { NodeReleasesContext } from '@/providers/nodeReleasesProvider'; 4 - import type { NodeReleaseStatus } from '@/types'; 5 - 6 - export const useNodeReleases = () => { 7 - const releases = useContext(NodeReleasesContext); 8 - 9 - const getReleaseByStatus = useCallback( 10 - (status: NodeReleaseStatus) => 11 - releases.find(release => release.status === status), 12 - [releases] 13 - ); 14 - 15 - const getLatestIsLtsRelease = useCallback( 16 - () => releases.find(release => release.isLts), 17 - [releases] 18 - ); 19 - 20 - return { releases, getReleaseByStatus, getLatestIsLtsRelease }; 21 - };
···
-5
hooks/useNotification.ts
··· 1 - import { useContext } from 'react'; 2 - 3 - import { NotificationDispatch } from '@/providers/notificationProvider'; 4 - 5 - export const useNotification = () => useContext(NotificationDispatch);
···
-27
hooks/useRouter.ts
··· 1 - import { useRouter as useNextRouter } from 'next/router'; 2 - import type { NextRouter } from 'next/router'; 3 - import { useMemo } from 'react'; 4 - 5 - import { 6 - availableLocales, 7 - getCurrentLocale, 8 - defaultLocale, 9 - } from '@/next.locales.mjs'; 10 - 11 - // Maps all available locales by only their Language Code 12 - const mappedLocalesByCode = availableLocales.map(l => l.code); 13 - 14 - export const useRouter = (): NextRouter => { 15 - const router = useNextRouter(); 16 - 17 - return useMemo(() => { 18 - const currentLocale = getCurrentLocale(router.asPath, router.query); 19 - 20 - return { 21 - ...router, 22 - locale: currentLocale.code, 23 - locales: mappedLocalesByCode, 24 - defaultLocale: defaultLocale.code, 25 - }; 26 - }, [router]); 27 - };
···
-9
hooks/useSiteConfig.ts
··· 1 - import { useContext } from 'react'; 2 - 3 - import { SiteContext } from '@/providers/siteProvider'; 4 - 5 - export const useSiteConfig = () => { 6 - const siteConfigContext = useContext(SiteContext); 7 - 8 - return siteConfigContext; 9 - };
···
+11
i18n.tsx
···
··· 1 + import type { RichTranslationValues } from 'next-intl'; 2 + import { getRequestConfig } from 'next-intl/server'; 3 + 4 + export const defaultRichTextValues: RichTranslationValues = { 5 + graySpan: c => <span className="small color-lightgray">{c}</span>, 6 + }; 7 + 8 + export default getRequestConfig(async ({ locale }) => ({ 9 + messages: (await import(`./i18n/locales/${locale}.json`)).default, 10 + timeZone: 'Etc/UTC', 11 + }));
+192 -108
i18n/locales/ar.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/de.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/en.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/es.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/fr.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/id.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
-42
i18n/locales/index.mjs
··· 1 - // Next.js React Intl Translation Manifest 2 - // In order to add new Translations to this application, please add the new locale 3 - // to the `i18n/config.json` file and then add the new locale file to this directory 4 - // and import it below. 5 - 6 - import ar from './ar.json' assert { type: 'json' }; 7 - import de from './de.json' assert { type: 'json' }; 8 - import en from './en.json' assert { type: 'json' }; 9 - import es from './es.json' assert { type: 'json' }; 10 - import fr from './fr.json' assert { type: 'json' }; 11 - import id from './id.json' assert { type: 'json' }; 12 - import it from './it.json' assert { type: 'json' }; 13 - import ja from './ja.json' assert { type: 'json' }; 14 - import ka from './ka.json' assert { type: 'json' }; 15 - import ko from './ko.json' assert { type: 'json' }; 16 - import ptBr from './pt-br.json' assert { type: 'json' }; 17 - import ru from './ru.json' assert { type: 'json' }; 18 - import tr from './tr.json' assert { type: 'json' }; 19 - import uk from './uk.json' assert { type: 'json' }; 20 - import zhCn from './zh-cn.json' assert { type: 'json' }; 21 - import zhTw from './zh-tw.json' assert { type: 'json' }; 22 - 23 - // This is the default export of the React Intl Locales and contains all the current locales 24 - // eslint-disable-next-line import/no-anonymous-default-export 25 - export default { 26 - ar, 27 - de, 28 - en, 29 - es, 30 - fr, 31 - id, 32 - it, 33 - ja, 34 - ka, 35 - ko, 36 - 'pt-br': ptBr, 37 - ru, 38 - tr, 39 - uk, 40 - 'zh-cn': zhCn, 41 - 'zh-tw': zhTw, 42 - };
···
+192 -108
i18n/locales/it.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/ja.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/ka.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/ko.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/pt-br.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/ru.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/tr.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/uk.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/zh-cn.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+192 -108
i18n/locales/zh-tw.json
··· 1 { 2 - "components.footer.scrollToTop.button": "Scroll to top", 3 - "components.header.links.about": "About", 4 - "components.header.links.download": "Download", 5 - "components.header.links.docs": "Docs", 6 - "components.header.links.getInvolved": "Get Involved", 7 - "components.header.links.security": "Security", 8 - "components.header.links.certification": "Certification", 9 - "components.header.links.blog": "News", 10 - "components.footer.links.trademarkPolicy": "Trademark Policy", 11 - "components.footer.links.privacyPolicy": "Privacy Policy", 12 - "components.footer.links.codeOfConduct": "Code of Conduct", 13 - "components.footer.links.security": "Security Policy", 14 - "components.footer.links.openJS": "OpenJS Foundation", 15 - "components.navigation.about.links.governance": "Project Governance", 16 - "components.navigation.about.links.releases": "Previous Releases", 17 - "components.navigation.about.links.security": "Security Reporting", 18 - "components.navigation.docs.links.es6": "ES6 and beyond", 19 - "components.navigation.docs.links.apiLts": "{fullLtsNodeVersion} API {spanLts}", 20 - "components.navigation.docs.links.apiCurrent": "{fullCurrentNodeVersion} API", 21 - "components.navigation.docs.links.guides": "Guides {spanGuides}", 22 - "components.navigation.docs.links.dependencies": "Dependencies", 23 - "components.navigation.getInvolved.links.collabSummit": "Collab Summit", 24 - "components.navigation.getInvolved.links.contribute": "Contribute", 25 - "components.navigation.getInvolved.links.codeOfConduct": "Code of Conduct", 26 - "components.downloadList.links.previousReleases": "Previous Releases", 27 - "components.downloadList.links.packageManager": "Installing Node.js via package manager", 28 - "components.downloadList.links.shaSums": "Signed SHASUMS for release files", 29 - "components.downloadList.links.shaSums.howToVerify": " (How to verify)", 30 - "components.downloadList.links.allDownloads": "All download options", 31 - "components.downloadList.links.nightlyReleases": "Nightly builds", 32 - "components.downloadList.links.unofficialBuilds": "Unofficial builds", 33 - "components.downloadList.links.buildingFromSource": "Building Node.js from source on supported platforms", 34 - "components.downloadList.links.installingOnLinux": "Installing Node.js via binary archive", 35 - "components.downloadList.links.installingOnWsl": "Install on Windows Subsystem for Linux (WSL)", 36 - "components.downloadReleasesTable.changelog": "Changelog", 37 - "components.downloadReleasesTable.releases": "Releases", 38 - "components.downloadReleasesTable.docs": "Docs", 39 - "components.header.buttons.toggleLanguage": "Toggle Language", 40 - "components.header.buttons.toggleDarkMode": "Toggle dark/light mode", 41 - "components.pagination.next": "Newer | ", 42 - "components.pagination.previous": "Older", 43 - "components.common.breadcrumbs.navigateToHome": "Navigate to Home", 44 - "components.common.crossLink.previous": "Prev", 45 - "components.common.crossLink.next": "Next", 46 - "components.common.codebox.copied": "Copied to clipboard!", 47 - "components.metabar.lastUpdated": "Last Updated", 48 - "components.metabar.readingTime": "Reading Time", 49 - "components.metabar.addedIn": "Added In", 50 - "components.metabar.author": "Author", 51 - "components.metabar.authors": "Authors", 52 - "components.metabar.contribute": "Contribute", 53 - "components.metabar.contributeText": "Edit this page", 54 - "components.metabar.viewAs": "View as", 55 - "components.metabar.tableOfContents": "Table of Contents", 56 - "layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}", 57 - "layouts.blogIndex.currentYear": "News from {year}", 58 - "components.api.jsonLink.title": "View as JSON", 59 - "components.api.sourceLink": "Source Code:", 60 - "pages.404.title": "404: Page could not be found", 61 - "pages.404.description": "ENOENT: no such file or directory", 62 - "components.common.pagination.prev": "Previous", 63 - "components.common.pagination.prevAriaLabel": "Previous page", 64 - "components.common.pagination.next": "Next", 65 - "components.common.pagination.nextAriaLabel": "Next page", 66 - "components.common.pagination.defaultLabel": "Pagination", 67 - "components.common.pagination.pageLabel": "Go to page {pageNumber}", 68 - "components.common.languageDropdown.label": "Choose Language", 69 - "components.common.card.announcement": "Announcements", 70 - "components.common.card.release": "Releases", 71 - "components.common.card.vulnerability": "Vulnerabilities", 72 - "components.home.homeDownloadButton.download": "{version} {isLts, select, true {LTS} other {Current}}", 73 - "components.home.homeDownloadButton.tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 74 - "components.home.homeDownloadButton.changelog": "Changelog", 75 - "components.home.homeDownloadButton.otherDownloads": "Other Downloads", 76 - "components.home.homeDownloadButton.apiDocs": "API Docs", 77 - "components.header.links.learn": "Learn", 78 - "components.header.links.learn.gettingStarted": "Getting Started", 79 - "components.header.links.learn.gettingStarted.introductionToNodejs": "Introduction to Node.js", 80 - "components.header.links.learn.gettingStarted.howToInstallNodejs": "How to install Node.js", 81 - "components.header.links.learn.gettingStarted.howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 82 - "components.header.links.learn.gettingStarted.differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 83 - "components.header.links.learn.gettingStarted.theV8JavascriptEngine": "The V8 JavaScript Engine", 84 - "components.header.links.learn.gettingStarted.anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 85 - "components.header.links.learn.gettingStarted.ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 86 - "components.header.links.learn.gettingStarted.nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 87 - "components.header.links.learn.gettingStarted.nodejsWithTypescript": "Node.js with TypeScript", 88 - "components.header.links.learn.gettingStarted.nodejsWithWebassembly": "Node.js with WebAssembly", 89 - "components.header.links.learn.asynchronousWork": "Asynchronous Work", 90 - "components.header.links.learn.asynchronousWork.asynchronousFlowControl": "Asynchronous flow control", 91 - "components.header.links.learn.asynchronousWork.overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 92 - "components.header.links.learn.asynchronousWork.javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 93 - "components.header.links.learn.asynchronousWork.discoverJavaScriptTimers": "Discover JavaScript Timers", 94 - "components.header.links.learn.asynchronousWork.understandingProcessnexttick": "Understanding process.nextTick()", 95 - "components.header.links.learn.asynchronousWork.understandingSetimmediate": "Understanding setImmediate()", 96 - "components.header.links.learn.asynchronousWork.theNodejsEventEmitter": "The Node.js Event emitter", 97 - "components.header.links.learn.manipulatingFiles": "Manipulating Files", 98 - "components.header.links.learn.manipulatingFiles.nodejsFileStats": "Node.js file stats", 99 - "components.header.links.learn.manipulatingFiles.nodejsFilePaths": "Node.js File Paths", 100 - "components.header.links.learn.manipulatingFiles.workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 101 - "components.header.links.learn.manipulatingFiles.readingFilesWithNodejs": "Reading files with Node.js", 102 - "components.header.links.learn.manipulatingFiles.writingFilesWithNodejs": "Writing files with Node.js", 103 - "components.header.links.learn.manipulatingFiles.workingWithFoldersInNodejs": "Working with folders in Node.js", 104 - "components.header.links.learn.commandLine": "Command Line", 105 - "components.header.links.learn.commandLine.runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 106 - "components.header.links.learn.commandLine.howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 107 - "components.header.links.learn.commandLine.howToUseTheNodejsRepl": "How to use the Node.js REPL", 108 - "components.header.links.learn.commandLine.outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 109 - "components.header.links.learn.commandLine.acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 110 }
··· 1 { 2 + "components": { 3 + "footer": { 4 + "scrollToTop": { 5 + "button": "Scroll to top" 6 + }, 7 + "links": { 8 + "trademarkPolicy": "Trademark Policy", 9 + "privacyPolicy": "Privacy Policy", 10 + "codeOfConduct": "Code of Conduct", 11 + "security": "Security Policy", 12 + "openJS": "OpenJS Foundation" 13 + } 14 + }, 15 + "header": { 16 + "links": { 17 + "about": "About", 18 + "download": "Download", 19 + "docs": "Docs", 20 + "getInvolved": "Get Involved", 21 + "security": "Security", 22 + "certification": "Certification", 23 + "blog": "News", 24 + "learn": { 25 + "title": "Learn", 26 + "gettingStarted": { 27 + "title": "Getting Started", 28 + "introductionToNodejs": "Introduction to Node.js", 29 + "howToInstallNodejs": "How to install Node.js", 30 + "howMuchJavascriptDoYouNeedToKnowToUseNodejs": "How much JavaScript do you need to know to use Node.js?", 31 + "differencesBetweenNodejsAndTheBrowser": "Differences between Node.js and the Browser", 32 + "theV8JavascriptEngine": "The V8 JavaScript Engine", 33 + "anIntroductionToTheNpmPackageManager": "An introduction to the NPM package manager", 34 + "ecmascript2015Es6AndBeyond": "ECMAScript 2015 (ES6) and beyond", 35 + "nodejsTheDifferenceBetweenDevelopmentAndProduction": "Node.js, the difference between development and production", 36 + "nodejsWithTypescript": "Node.js with TypeScript", 37 + "nodejsWithWebassembly": "Node.js with WebAssembly" 38 + }, 39 + "asynchronousWork": { 40 + "title": "Asynchronous Work", 41 + "asynchronousFlowControl": "Asynchronous flow control", 42 + "overviewOfBlockingVsNonBlocking": "Overview of Blocking vs Non-Blocking", 43 + "javascriptAsynchronousProgrammingAndCallbacks": "JavaScript Asynchronous Programming and Callbacks", 44 + "discoverJavaScriptTimers": "Discover JavaScript Timers", 45 + "understandingProcessnexttick": "Understanding process.nextTick()", 46 + "understandingSetimmediate": "Understanding setImmediate()", 47 + "theNodejsEventEmitter": "The Node.js Event emitter" 48 + }, 49 + "manipulatingFiles": { 50 + "title": "Manipulating Files", 51 + "nodejsFileStats": "Node.js file stats", 52 + "nodejsFilePaths": "Node.js File Paths", 53 + "workingWithFileDescriptorsInNodejs": "Working with file descriptors in Node.js", 54 + "readingFilesWithNodejs": "Reading files with Node.js", 55 + "writingFilesWithNodejs": "Writing files with Node.js", 56 + "workingWithFoldersInNodejs": "Working with folders in Node.js" 57 + }, 58 + "commandLine": { 59 + "title": "Command Line", 60 + "runNodejsScriptsFromTheCommandLine": "Run Node.js scripts from the command line", 61 + "howToReadEnvironmentVariablesFromNodejs": "How to read environment variables from Node.js", 62 + "howToUseTheNodejsRepl": "How to use the Node.js REPL", 63 + "outputToTheCommandLineUsingNodejs": "Output to the command line using Node.js", 64 + "acceptInputFromTheCommandLineInNodejs": "Accept input from the command line in Node.js" 65 + } 66 + } 67 + }, 68 + "buttons": { 69 + "toggleLanguage": "Toggle Language", 70 + "toggleDarkMode": "Toggle dark/light mode" 71 + } 72 + }, 73 + "navigation": { 74 + "about": { 75 + "links": { 76 + "governance": "Project Governance", 77 + "releases": "Previous Releases", 78 + "security": "Security Reporting" 79 + } 80 + }, 81 + "docs": { 82 + "links": { 83 + "es6": "ES6 and beyond", 84 + "apiLts": "{fullLtsNodeVersion} API <graySpan>LTS</graySpan>", 85 + "apiCurrent": "{fullCurrentNodeVersion} API", 86 + "guides": "Guides <graySpan>ARCHIVED</graySpan>", 87 + "dependencies": "Dependencies" 88 + } 89 + }, 90 + "getInvolved": { 91 + "links": { 92 + "collabSummit": "Collab Summit", 93 + "contribute": "Contribute", 94 + "codeOfConduct": "Code of Conduct" 95 + } 96 + } 97 + }, 98 + "downloadList": { 99 + "links": { 100 + "previousReleases": "Previous Releases", 101 + "packageManager": "Installing Node.js via package manager", 102 + "shaSums": { 103 + "title": "Signed SHASUMS for release files", 104 + "howToVerify": " (How to verify)" 105 + }, 106 + "allDownloads": "All download options", 107 + "nightlyReleases": "Nightly builds", 108 + "unofficialBuilds": "Unofficial builds", 109 + "buildingFromSource": "Building Node.js from source on supported platforms", 110 + "installingOnLinux": "Installing Node.js via binary archive", 111 + "installingOnWsl": "Install on Windows Subsystem for Linux (WSL)" 112 + } 113 + }, 114 + "downloadReleasesTable": { 115 + "changelog": "Changelog", 116 + "releases": "Releases", 117 + "docs": "Docs" 118 + }, 119 + "pagination": { 120 + "next": "Newer | ", 121 + "previous": "Older" 122 + }, 123 + "common": { 124 + "breadcrumbs": { 125 + "navigateToHome": "Navigate to Home" 126 + }, 127 + "crossLink": { 128 + "previous": "Prev", 129 + "next": "Next" 130 + }, 131 + "codebox": { 132 + "copied": "Copied to clipboard!" 133 + }, 134 + "pagination": { 135 + "prev": "Previous", 136 + "prevAriaLabel": "Previous page", 137 + "next": "Next", 138 + "nextAriaLabel": "Next page", 139 + "defaultLabel": "Pagination", 140 + "pageLabel": "Go to page {pageNumber}" 141 + }, 142 + "languageDropdown": { 143 + "label": "Choose Language" 144 + }, 145 + "card": { 146 + "announcement": "Announcements", 147 + "release": "Releases", 148 + "vulnerability": "Vulnerabilities" 149 + } 150 + }, 151 + "metabar": { 152 + "lastUpdated": "Last Updated", 153 + "readingTime": "Reading Time", 154 + "addedIn": "Added In", 155 + "author": "Author", 156 + "authors": "Authors", 157 + "contribute": "Contribute", 158 + "contributeText": "Edit this page", 159 + "viewAs": "View as", 160 + "tableOfContents": "Table of Contents" 161 + }, 162 + "api": { 163 + "jsonLink": { 164 + "title": "View as JSON" 165 + }, 166 + "sourceLink": "Source Code:" 167 + }, 168 + "home": { 169 + "homeDownloadButton": { 170 + "download": "{version} {isLts, select, true {LTS} other {Current}}", 171 + "tagline": "{isLts, select, true {Recommended For Most Users} other {Latest Features}}", 172 + "changelog": "Changelog", 173 + "otherDownloads": "Other Downloads", 174 + "apiDocs": "API Docs" 175 + } 176 + } 177 + }, 178 + "layouts": { 179 + "blogPost": { 180 + "author": { 181 + "byLine": "{author, select, null {} other {By {author}, }}" 182 + } 183 + }, 184 + "blogIndex": { 185 + "currentYear": "News from {year}" 186 + } 187 + }, 188 + "pages": { 189 + "404": { 190 + "title": "404: Page could not be found", 191 + "description": "ENOENT: no such file or directory" 192 + } 193 + } 194 }
+1
jest.config.mjs
··· 14 reporters: ['default', 'jest-junit'], 15 moduleNameMapper: { 16 'next/router': '<rootDir>/components/__mocks__/next-router.mjs', 17 }, 18 }; 19
··· 14 reporters: ['default', 'jest-junit'], 15 moduleNameMapper: { 16 'next/router': '<rootDir>/components/__mocks__/next-router.mjs', 17 + 'next-intl': '<rootDir>/components/__mocks__/next-intl.mjs', 18 }, 19 }; 20
+4 -8
layouts/AboutLayout.tsx
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 - import BaseLayout from './BaseLayout'; 6 - 7 const AboutLayout: FC<PropsWithChildren> = ({ children }) => ( 8 - <BaseLayout> 9 - <div className="has-side-nav container"> 10 - <SideNavigation navigationKey="about" /> 11 - <article dir="auto">{children}</article> 12 - </div> 13 - </BaseLayout> 14 ); 15 16 export default AboutLayout;
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 const AboutLayout: FC<PropsWithChildren> = ({ children }) => ( 6 + <div className="has-side-nav container"> 7 + <SideNavigation navigationKey="about" /> 8 + <article dir="auto">{children}</article> 9 + </div> 10 ); 11 12 export default AboutLayout;
+67
layouts/BlogCategoryLayout.tsx
···
··· 1 + import { useTranslations } from 'next-intl'; 2 + import { useMemo } from 'react'; 3 + import type { FC } from 'react'; 4 + 5 + import { Time } from '@/components/Common/Time'; 6 + import Pagination from '@/components/Pagination'; 7 + import { useClientContext, useBlogData } from '@/hooks/server'; 8 + import { Link } from '@/navigation.mjs'; 9 + import type { BlogPost } from '@/types'; 10 + 11 + const BlogCategoryLayout: FC = () => { 12 + const t = useTranslations(); 13 + const { getPagination, getPostsByYear, getPostsByCategory, currentCategory } = 14 + useBlogData(); 15 + 16 + const { frontmatter } = useClientContext(); 17 + 18 + const { posts, pagination, title } = useMemo(() => { 19 + if (currentCategory.startsWith('year-')) { 20 + const categoryWithoutPrefix = currentCategory.replace('year-', ''); 21 + 22 + return { 23 + posts: getPostsByYear(categoryWithoutPrefix), 24 + pagination: getPagination(categoryWithoutPrefix), 25 + title: t('layouts.blogIndex.currentYear', { 26 + year: categoryWithoutPrefix, 27 + }), 28 + }; 29 + } 30 + 31 + return { 32 + posts: getPostsByCategory(currentCategory), 33 + pagination: undefined, 34 + title: frontmatter.title, 35 + }; 36 + }, [ 37 + currentCategory, 38 + frontmatter.title, 39 + getPagination, 40 + getPostsByCategory, 41 + getPostsByYear, 42 + t, 43 + ]); 44 + 45 + return ( 46 + <div className="container" dir="auto"> 47 + <h2>{title}</h2> 48 + 49 + <ul className="blog-index"> 50 + {posts.map((post: BlogPost) => ( 51 + <li key={post.slug}> 52 + <Time 53 + date={post.date} 54 + format={{ month: 'short', day: '2-digit' }} 55 + /> 56 + 57 + <Link href={post.slug}>{post.title}</Link> 58 + </li> 59 + ))} 60 + </ul> 61 + 62 + <Pagination {...pagination} /> 63 + </div> 64 + ); 65 + }; 66 + 67 + export default BlogCategoryLayout;
-63
layouts/BlogIndexLayout.tsx
··· 1 - import { useMemo } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - import { FormattedMessage } from 'react-intl'; 4 - 5 - import { Time } from '@/components/Common/Time'; 6 - import LocalizedLink from '@/components/LocalizedLink'; 7 - import Pagination from '@/components/Pagination'; 8 - import { useBlogData } from '@/hooks/useBlogData'; 9 - import type { BlogPost } from '@/types'; 10 - 11 - import BaseLayout from './BaseLayout'; 12 - 13 - const BlogIndexLayout: FC<PropsWithChildren> = ({ children }) => { 14 - const { getPagination, getPostsByYear, currentCategory } = useBlogData(); 15 - 16 - const currentYear = useMemo( 17 - () => 18 - Number( 19 - currentCategory.startsWith('year-') 20 - ? currentCategory.replace('year-', '') 21 - : new Date().getFullYear() 22 - ), 23 - [currentCategory] 24 - ); 25 - 26 - const { posts, pagination } = useMemo(() => { 27 - return { 28 - posts: getPostsByYear(currentYear), 29 - pagination: getPagination(currentYear), 30 - }; 31 - }, [currentYear, getPagination, getPostsByYear]); 32 - 33 - return ( 34 - <BaseLayout> 35 - <div className="container" dir="auto"> 36 - <FormattedMessage 37 - id="layouts.blogIndex.currentYear" 38 - values={{ year: currentYear }} 39 - tagName="h2" 40 - /> 41 - 42 - <ul className="blog-index"> 43 - {posts.map((post: BlogPost) => ( 44 - <li key={post.slug}> 45 - <Time 46 - date={post.date} 47 - format={{ month: 'short', day: '2-digit' }} 48 - /> 49 - 50 - <LocalizedLink href={post.slug}>{post.title}</LocalizedLink> 51 - </li> 52 - ))} 53 - </ul> 54 - 55 - <Pagination prevSlug={pagination.prev} nextSlug={pagination.next} /> 56 - 57 - {children} 58 - </div> 59 - </BaseLayout> 60 - ); 61 - }; 62 - 63 - export default BlogIndexLayout;
···
+21 -27
layouts/BlogPostLayout.tsx
··· 1 import type { FC, PropsWithChildren } from 'react'; 2 - import { FormattedMessage } from 'react-intl'; 3 4 import { Time } from '@/components/Common/Time'; 5 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 6 - import type { LegacyBlogFrontMatter } from '@/types'; 7 - 8 - import BaseLayout from './BaseLayout'; 9 10 const BlogPostLayout: FC<PropsWithChildren> = ({ children }) => { 11 - const { frontMatter } = useLayoutContext(); 12 13 - const { title, author, date } = frontMatter as LegacyBlogFrontMatter; 14 15 return ( 16 - <BaseLayout> 17 - <div className="container"> 18 - <article dir="auto"> 19 - <div className="blogpost-header"> 20 - <h1>{title}</h1> 21 - <span className="blogpost-meta"> 22 - <FormattedMessage 23 - id="layouts.blogPost.author.byLine" 24 - values={{ author: author || null }} 25 - /> 26 27 - <Time 28 - date={date} 29 - format={{ month: 'short', day: '2-digit', year: 'numeric' }} 30 - /> 31 - </span> 32 - </div> 33 34 - {children} 35 - </article> 36 - </div> 37 - </BaseLayout> 38 ); 39 }; 40
··· 1 + import { useTranslations } from 'next-intl'; 2 import type { FC, PropsWithChildren } from 'react'; 3 4 import { Time } from '@/components/Common/Time'; 5 + import { useClientContext } from '@/hooks/server'; 6 7 const BlogPostLayout: FC<PropsWithChildren> = ({ children }) => { 8 + const t = useTranslations(); 9 10 + const { 11 + frontmatter: { title, author, date }, 12 + } = useClientContext(); 13 14 return ( 15 + <div className="container"> 16 + <article dir="auto"> 17 + <div className="blogpost-header"> 18 + <h1>{title}</h1> 19 + <span className="blogpost-meta"> 20 + {t('layouts.blogPost.author.byLine', { author: author || null })} 21 22 + <Time 23 + date={date} 24 + format={{ month: 'short', day: '2-digit', year: 'numeric' }} 25 + /> 26 + </span> 27 + </div> 28 29 + {children} 30 + </article> 31 + </div> 32 ); 33 }; 34
-45
layouts/CategoryIndexLayout.tsx
··· 1 - import { useMemo } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import { Time } from '@/components/Common/Time'; 5 - import LocalizedLink from '@/components/LocalizedLink'; 6 - import { useBlogData } from '@/hooks/useBlogData'; 7 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 8 - import type { BlogPost } from '@/types'; 9 - 10 - import BaseLayout from './BaseLayout'; 11 - 12 - const CategoryIndexLayout: FC<PropsWithChildren> = ({ children }) => { 13 - const { frontMatter } = useLayoutContext(); 14 - const { getPostsByCategory, currentCategory } = useBlogData(); 15 - 16 - const posts = useMemo( 17 - () => getPostsByCategory(currentCategory), 18 - [currentCategory, getPostsByCategory] 19 - ); 20 - 21 - return ( 22 - <BaseLayout> 23 - <div className="container" dir="auto"> 24 - <h2>{frontMatter.title}</h2> 25 - 26 - <ul className="blog-index"> 27 - {posts.map((post: BlogPost) => ( 28 - <li key={post.slug}> 29 - <Time 30 - date={post.date} 31 - format={{ month: 'short', day: '2-digit' }} 32 - /> 33 - 34 - <LocalizedLink href={post.slug}>{post.title}</LocalizedLink> 35 - </li> 36 - ))} 37 - </ul> 38 - 39 - {children} 40 - </div> 41 - </BaseLayout> 42 - ); 43 - }; 44 - 45 - export default CategoryIndexLayout;
···
+4 -8
layouts/ContributeLayout.tsx
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 - import BaseLayout from './BaseLayout'; 6 - 7 const ContributeLayout: FC<PropsWithChildren> = ({ children }) => ( 8 - <BaseLayout> 9 - <div className="has-side-nav container"> 10 - <SideNavigation navigationKey="getInvolved" /> 11 - <article dir="auto">{children}</article> 12 - </div> 13 - </BaseLayout> 14 ); 15 16 export default ContributeLayout;
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 const ContributeLayout: FC<PropsWithChildren> = ({ children }) => ( 6 + <div className="has-side-nav container"> 7 + <SideNavigation navigationKey="getInvolved" /> 8 + <article dir="auto">{children}</article> 9 + </div> 10 ); 11 12 export default ContributeLayout;
+1 -5
layouts/DefaultLayout.tsx
··· 1 import type { FC, PropsWithChildren } from 'react'; 2 3 - import BaseLayout from './BaseLayout'; 4 - 5 const DefaultLayout: FC<PropsWithChildren> = ({ children }) => ( 6 - <BaseLayout> 7 - <div className="container">{children}</div> 8 - </BaseLayout> 9 ); 10 11 export default DefaultLayout;
··· 1 import type { FC, PropsWithChildren } from 'react'; 2 3 const DefaultLayout: FC<PropsWithChildren> = ({ children }) => ( 4 + <div className="container">{children}</div> 5 ); 6 7 export default DefaultLayout;
+15 -19
layouts/DocsLayout.tsx
··· 1 - import { useMemo } from 'react'; 2 import type { FC, PropsWithChildren } from 'react'; 3 4 import SideNavigation from '@/components/SideNavigation'; 5 - import { useNodeReleases } from '@/hooks/useNodeReleases'; 6 - 7 - import BaseLayout from './BaseLayout'; 8 9 const DocsLayout: FC<PropsWithChildren> = ({ children }) => { 10 - const { getReleaseByStatus, getLatestIsLtsRelease } = useNodeReleases(); 11 - 12 - const [lts, current] = useMemo( 13 - () => [getLatestIsLtsRelease(), getReleaseByStatus('Current')], 14 - [getLatestIsLtsRelease, getReleaseByStatus] 15 - ); 16 17 - const translationContext = { 18 apiLts: { 19 ltsNodeVersion: lts ? `v${lts.major}.x` : undefined, 20 fullLtsNodeVersion: lts ? lts.versionWithPrefix : undefined, 21 - spanLts: <span className="small color-lightgray">LTS</span>, 22 }, 23 apiCurrent: { 24 fullCurrentNodeVersion: current ? current.versionWithPrefix : undefined, 25 currentNodeVersion: current ? `v${current.major}.x` : undefined, 26 }, 27 guides: { 28 - spanGuides: <span className="small color-lightgray">ARCHIVE</span>, 29 }, 30 }; 31 32 return ( 33 - <BaseLayout> 34 - <div className="has-side-nav container"> 35 - <SideNavigation navigationKey="docs" context={translationContext} /> 36 - <article dir="auto">{children}</article> 37 - </div> 38 - </BaseLayout> 39 ); 40 }; 41
··· 1 + 'use client'; 2 + 3 + import type { RichTranslationValues } from 'next-intl'; 4 import type { FC, PropsWithChildren } from 'react'; 5 6 import SideNavigation from '@/components/SideNavigation'; 7 + import { releaseData } from '@/next.json.mjs'; 8 9 const DocsLayout: FC<PropsWithChildren> = ({ children }) => { 10 + const [lts, current] = [ 11 + releaseData.find(({ isLts }) => isLts), 12 + releaseData.find(({ status }) => status === 'Current'), 13 + ]; 14 15 + const translationContext: Record<string, RichTranslationValues> = { 16 apiLts: { 17 ltsNodeVersion: lts ? `v${lts.major}.x` : undefined, 18 fullLtsNodeVersion: lts ? lts.versionWithPrefix : undefined, 19 + graySpan: c => <span className="small color-lightgray">{c}</span>, 20 }, 21 apiCurrent: { 22 fullCurrentNodeVersion: current ? current.versionWithPrefix : undefined, 23 currentNodeVersion: current ? `v${current.major}.x` : undefined, 24 }, 25 guides: { 26 + graySpan: c => <span className="small color-lightgray">{c}</span>, 27 }, 28 }; 29 30 return ( 31 + <div className="has-side-nav container"> 32 + <SideNavigation navigationKey="docs" context={translationContext} /> 33 + <article dir="auto">{children}</article> 34 + </div> 35 ); 36 }; 37
-40
layouts/DownloadCurrentLayout.tsx
··· 1 - import type { FC, PropsWithChildren } from 'react'; 2 - 3 - import PrimaryDownloadMatrix from '@/components/Downloads/PrimaryDownloadMatrix'; 4 - import SecondaryDownloadMatrix from '@/components/Downloads/SecondaryDownloadMatrix'; 5 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 6 - import { WithNodeRelease } from '@/providers/withNodeRelease'; 7 - import type { LegacyDownloadsFrontMatter } from '@/types'; 8 - 9 - import BaseLayout from './BaseLayout'; 10 - 11 - const DownloadCurrentLayout: FC<PropsWithChildren> = ({ children }) => { 12 - const { frontMatter } = useLayoutContext(); 13 - 14 - const { downloads } = frontMatter as LegacyDownloadsFrontMatter; 15 - 16 - return ( 17 - <BaseLayout> 18 - <div className="container"> 19 - <article dir="auto"> 20 - <div className="download-header"> 21 - <h1>{downloads.headline}</h1> 22 - </div> 23 - 24 - {children} 25 - 26 - <WithNodeRelease status="Current"> 27 - {({ release }) => ( 28 - <> 29 - <PrimaryDownloadMatrix {...release} /> 30 - <SecondaryDownloadMatrix {...release} /> 31 - </> 32 - )} 33 - </WithNodeRelease> 34 - </article> 35 - </div> 36 - </BaseLayout> 37 - ); 38 - }; 39 - 40 - export default DownloadCurrentLayout;
···
+28 -17
layouts/DownloadLayout.tsx
··· 2 3 import PrimaryDownloadMatrix from '@/components/Downloads/PrimaryDownloadMatrix'; 4 import SecondaryDownloadMatrix from '@/components/Downloads/SecondaryDownloadMatrix'; 5 - import { useLayoutContext } from '@/hooks/useLayoutContext'; 6 - import { WithNodeRelease } from '@/providers/withNodeRelease'; 7 - import type { LegacyDownloadsFrontMatter } from '@/types'; 8 - 9 - import BaseLayout from './BaseLayout'; 10 11 const DownloadLayout: FC<PropsWithChildren> = ({ children }) => { 12 - const { frontMatter } = useLayoutContext(); 13 14 - const { downloads } = frontMatter as LegacyDownloadsFrontMatter; 15 16 return ( 17 - <BaseLayout> 18 - <div className="container"> 19 - <article dir="auto"> 20 - <div className="download-header"> 21 - <h1>{downloads.headline}</h1> 22 - </div> 23 24 - {children} 25 26 <WithNodeRelease status={['Active LTS', 'Maintenance LTS']}> 27 {({ release }) => ( 28 <> ··· 31 </> 32 )} 33 </WithNodeRelease> 34 - </article> 35 - </div> 36 - </BaseLayout> 37 ); 38 }; 39
··· 2 3 import PrimaryDownloadMatrix from '@/components/Downloads/PrimaryDownloadMatrix'; 4 import SecondaryDownloadMatrix from '@/components/Downloads/SecondaryDownloadMatrix'; 5 + import { WithNodeRelease } from '@/components/withNodeRelease'; 6 + import { useClientContext } from '@/hooks/server'; 7 8 const DownloadLayout: FC<PropsWithChildren> = ({ children }) => { 9 + const { 10 + frontmatter: { downloads }, 11 + pathname, 12 + } = useClientContext(); 13 14 + const isCurrentReleasePage = pathname.includes('/current'); 15 16 return ( 17 + <div className="container"> 18 + <article dir="auto"> 19 + <div className="download-header"> 20 + <h1>{downloads.headline}</h1> 21 + </div> 22 + 23 + {children} 24 25 + {isCurrentReleasePage && ( 26 + <WithNodeRelease status="Current"> 27 + {({ release }) => ( 28 + <> 29 + <PrimaryDownloadMatrix {...release} /> 30 + <SecondaryDownloadMatrix {...release} /> 31 + </> 32 + )} 33 + </WithNodeRelease> 34 + )} 35 36 + {isCurrentReleasePage || ( 37 <WithNodeRelease status={['Active LTS', 'Maintenance LTS']}> 38 {({ release }) => ( 39 <> ··· 42 </> 43 )} 44 </WithNodeRelease> 45 + )} 46 + </article> 47 + </div> 48 ); 49 }; 50
+3 -7
layouts/IndexLayout.tsx
··· 1 import type { FC, PropsWithChildren } from 'react'; 2 3 - import BaseLayout from '@/layouts/BaseLayout'; 4 - 5 const IndexLayout: FC<PropsWithChildren> = ({ children }) => ( 6 - <BaseLayout> 7 - <div className="container"> 8 - <div id="home-intro">{children}</div> 9 - </div> 10 - </BaseLayout> 11 ); 12 13 export default IndexLayout;
··· 1 import type { FC, PropsWithChildren } from 'react'; 2 3 const IndexLayout: FC<PropsWithChildren> = ({ children }) => ( 4 + <div className="container"> 5 + <div id="home-intro">{children}</div> 6 + </div> 7 ); 8 9 export default IndexLayout;
+4 -8
layouts/LearnLayout.tsx
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 - import BaseLayout from './BaseLayout'; 6 - 7 const LearnLayout: FC<PropsWithChildren> = ({ children }) => ( 8 - <BaseLayout> 9 - <div className="has-side-nav container"> 10 - <SideNavigation navigationKey="learn" /> 11 - <article dir="auto">{children}</article> 12 - </div> 13 - </BaseLayout> 14 ); 15 16 export default LearnLayout;
··· 2 3 import SideNavigation from '@/components/SideNavigation'; 4 5 const LearnLayout: FC<PropsWithChildren> = ({ children }) => ( 6 + <div className="has-side-nav container"> 7 + <SideNavigation navigationKey="learn" /> 8 + <article dir="auto">{children}</article> 9 + </div> 10 ); 11 12 export default LearnLayout;
+12 -50
middleware.ts
··· 1 - import { NextResponse } from 'next/server'; 2 - import type { NextRequest } from 'next/server'; 3 - 4 - import { availableLocales } from './next.locales.mjs'; 5 - 6 - // This Middleware is responsible for handling automatic language detection from a user's Browser 7 - // This middleware should only run on "/" requests coming to the Website 8 - export const middleware = async (request: NextRequest) => { 9 - const { pathname, search } = request.nextUrl; 10 - 11 - // This function allows us to redirect with a Locale Code 12 - const redirectWithLocale = (_locale: string) => { 13 - const redirectUrl = `/${_locale}${pathname}${search}`; 14 - 15 - return NextResponse.redirect(new URL(redirectUrl, request.url)); 16 - }; 17 - 18 - const localeCookie = request.cookies.get('NEXT_LOCALE'); 19 - 20 - // If we already have a NEXT_LOCALE Cookie, then Redirect to the stored Locale Code 21 - if (localeCookie?.value && localeCookie.value !== 'default') { 22 - // If the language by any reason is not available anymore, ignore the cookie 23 - if (availableLocales.some(locale => locale.code === localeCookie.value)) { 24 - return redirectWithLocale(localeCookie.value); 25 - } 26 - } 27 - 28 - // If not, we try to check if the Browser is sending `Accept-Language` Header 29 - const acceptedLanguagesRaw = request.headers.get('Accept-Language') || 'en'; 30 - 31 - // If present, we try to split the format into ['code', 'q=order', ...] 32 - // Where q= is the precedence of the Accepted Language 33 - // We then filter those out as we don't want them 34 - const acceptedLanguages = acceptedLanguagesRaw 35 - .split(';') 36 - .map(collection => collection.split(',')) 37 - .flat() 38 - .filter(locale => !locale.startsWith('q=')); 39 40 - // We check if we have any matching Language in the order of preference given 41 - // And if yes, we return that Locale Code 42 - const matchedLocaleCode = acceptedLanguages.find(acceptedLocale => 43 - availableLocales.some(locale => locale.code === acceptedLocale) 44 - ); 45 46 - // We create a new Response Object containing the Locale Match or the default Language 47 - const responseWithCookie = redirectWithLocale(matchedLocaleCode || 'en'); 48 49 - // Then we set a Cookie to avoid this calculation from happening on every / hit 50 - responseWithCookie.cookies.set('NEXT_LOCALE', matchedLocaleCode || 'en'); 51 52 - return responseWithCookie; 53 }; 54 - 55 - export const config = { matcher: '/' };
··· 1 + import createMiddleware from 'next-intl/middleware'; 2 3 + import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs'; 4 5 + export default createMiddleware({ 6 + // A list of all locales that are supported 7 + locales: availableLocaleCodes, 8 9 + // Used when no locale matches 10 + defaultLocale: defaultLocale.code, 11 + }); 12 13 + export const config = { 14 + // Note.: This needs to be updated when activating more locales 15 + // Format: '/(locale1|locale2|locale3|...)/:path*' 16 + matcher: ['/', '/(en)/:path*'], 17 };
+6 -6
navigation.json
··· 19 }, 20 "learn": { 21 "link": "/learn", 22 - "translationId": "components.header.links.learn", 23 "items": { 24 "gettingStarted": { 25 "link": "/learn/getting-started/introduction-to-nodejs", 26 - "translationId": "components.header.links.learn.gettingStarted", 27 "items": { 28 "introductionToNodejs": { 29 "link": "/learn/getting-started/introduction-to-nodejs", ··· 69 }, 70 "asynchronousWork": { 71 "link": "/learn/asynchronous-work/asynchronous-flow-control", 72 - "translationId": "components.header.links.learn.asynchronousWork", 73 "items": { 74 "asynchronousFlowControl": { 75 "link": "/learn/asynchronous-work/asynchronous-flow-control", ··· 103 }, 104 "manipulatingFiles": { 105 "link": "/learn/manipulating-files/nodejs-file-stats", 106 - "translationId": "components.header.links.learn.manipulatingFiles", 107 "items": { 108 "nodejsFileStats": { 109 "link": "/learn/manipulating-files/nodejs-file-stats", ··· 133 }, 134 "commandLine": { 135 "link": "/learn/command-line/run-nodejs-scripts-from-the-command-line", 136 - "translationId": "components.header.links.learn.commandLine", 137 "items": { 138 "runNodejsScriptsFromTheCommandLine": { 139 "link": "/learn/command-line/run-nodejs-scripts-from-the-command-line", ··· 165 "items": { 166 "shaSums": { 167 "link": "https://nodejs.org/dist/{nodeVersion}/SHASUMS256.txt.asc", 168 - "translationId": "components.downloadList.links.shaSums" 169 }, 170 "allDownloads": { 171 "link": "https://nodejs.org/dist/{nodeVersion}/",
··· 19 }, 20 "learn": { 21 "link": "/learn", 22 + "translationId": "components.header.links.learn.title", 23 "items": { 24 "gettingStarted": { 25 "link": "/learn/getting-started/introduction-to-nodejs", 26 + "translationId": "components.header.links.learn.gettingStarted.title", 27 "items": { 28 "introductionToNodejs": { 29 "link": "/learn/getting-started/introduction-to-nodejs", ··· 69 }, 70 "asynchronousWork": { 71 "link": "/learn/asynchronous-work/asynchronous-flow-control", 72 + "translationId": "components.header.links.learn.asynchronousWork.title", 73 "items": { 74 "asynchronousFlowControl": { 75 "link": "/learn/asynchronous-work/asynchronous-flow-control", ··· 103 }, 104 "manipulatingFiles": { 105 "link": "/learn/manipulating-files/nodejs-file-stats", 106 + "translationId": "components.header.links.learn.manipulatingFiles.title", 107 "items": { 108 "nodejsFileStats": { 109 "link": "/learn/manipulating-files/nodejs-file-stats", ··· 133 }, 134 "commandLine": { 135 "link": "/learn/command-line/run-nodejs-scripts-from-the-command-line", 136 + "translationId": "components.header.links.learn.commandLine.title", 137 "items": { 138 "runNodejsScriptsFromTheCommandLine": { 139 "link": "/learn/command-line/run-nodejs-scripts-from-the-command-line", ··· 165 "items": { 166 "shaSums": { 167 "link": "https://nodejs.org/dist/{nodeVersion}/SHASUMS256.txt.asc", 168 + "translationId": "components.downloadList.links.shaSums.title" 169 }, 170 "allDownloads": { 171 "link": "https://nodejs.org/dist/{nodeVersion}/",
+8
navigation.mjs
···
··· 1 + 'use strict'; 2 + 3 + import { createSharedPathnamesNavigation } from 'next-intl/navigation'; 4 + 5 + import { availableLocaleCodes } from './next.locales.mjs'; 6 + 7 + export const { Link, redirect, usePathname, useRouter } = 8 + createSharedPathnamesNavigation({ locales: availableLocaleCodes });
+55 -16
next-data/generateBlogPostsData.mjs
··· 1 'use strict'; 2 3 - import { readFile, writeFile } from 'node:fs/promises'; 4 import { basename, extname, join } from 'node:path'; 5 6 import graymatter from 'gray-matter'; 7 ··· 59 ['**/index.md', '**/pagination.md'] 60 ); 61 62 - // we gather all the information of all the blog posts by reading each individual file 63 - // and then parsing the frontmatter and source content and returning a minified object 64 - const postsPromise = filenames 65 - .map(name => ({ name, file: readFile(join(blogPath, name)) })) 66 - .map(({ name, file }) => file.then(source => getFrontMatter(name, source))); 67 68 - // we await for all the work to be concluded and return a nice blog posts object 69 - const posts = await Promise.all(postsPromise); 70 71 - return writeFile( 72 - jsonFilePath, 73 - JSON.stringify({ 74 - pagination: [...blogMetadata.pagination].sort(), 75 - categories: [...blogMetadata.categories].sort(), 76 - posts: posts.sort((a, b) => b.date - a.date), 77 - }) 78 - ); 79 }; 80 81 export default generateBlogPostsData;
··· 1 'use strict'; 2 3 + import { createReadStream } from 'node:fs'; 4 + import { writeFile } from 'node:fs/promises'; 5 import { basename, extname, join } from 'node:path'; 6 + import readline from 'node:readline'; 7 8 import graymatter from 'gray-matter'; 9 ··· 61 ['**/index.md', '**/pagination.md'] 62 ); 63 64 + // Writes the Blog Posts to the JSON file 65 + const writeResult = blogPosts => { 66 + return writeFile( 67 + jsonFilePath, 68 + JSON.stringify({ 69 + pagination: [...blogMetadata.pagination].sort(), 70 + categories: [...blogMetadata.categories].sort(), 71 + posts: blogPosts.sort((a, b) => b.date - a.date), 72 + }) 73 + ); 74 + }; 75 + 76 + return new Promise(resolve => { 77 + const blogPosts = []; 78 + 79 + for (const filename of filenames) { 80 + let rawFrontmatter = ''; 81 + let countOfSeparators = 0; 82 + 83 + // We create a stream for reading a file instead of reading the files 84 + const _stream = createReadStream(join(blogPath, filename)); 85 + 86 + // We create a readline interface to read the file line-by-line 87 + const _readLine = readline.createInterface({ input: _stream }); 88 + 89 + // We read line by line 90 + _readLine.on('line', line => { 91 + rawFrontmatter += `${line}\n`; 92 + 93 + // We observe the frontmatter separators 94 + if (line === '---') { 95 + countOfSeparators += 1; 96 + } 97 98 + // Once we have two separators we close the readLine and the stream 99 + if (countOfSeparators === 2) { 100 + _readLine.close(); 101 + _stream.close(); 102 + } 103 + }); 104 + 105 + // Then we parse gray-matter on the frontmatter 106 + // This allows us to only read the frontmatter part of each file 107 + // and optimise the read-process as we have thousands of markdown files 108 + _readLine.on('close', () => { 109 + blogPosts.push(getFrontMatter(filename, rawFrontmatter)); 110 111 + // Once we finish reading all fles 112 + if (blogPosts.length === filenames.length) { 113 + resolve(writeResult(blogPosts)); 114 + } 115 + }); 116 + } 117 + }); 118 }; 119 120 export default generateBlogPostsData;
+43 -11
next-data/generateNodeReleasesJson.mjs
··· 6 // this is the destination path for where the JSON file will be written 7 const jsonFilePath = join(process.cwd(), 'public/node-releases-data.json'); 8 9 const generateNodeReleasesJson = async () => { 10 const nodevuOutput = await nodevu({ fetch: fetch }); 11 12 // Filter out those without documented support 13 // Basically those not in schedule.json 14 - const majors = Object.values(nodevuOutput).filter( 15 - major => major?.support?.phases?.dates?.start 16 - ); 17 18 const nodeReleases = majors.map(major => { 19 const [latestVersion] = Object.values(major.releases); 20 21 - return { 22 - major: latestVersion.semver.major, 23 - version: latestVersion.semver.raw, 24 - codename: major.support.codename, 25 currentStart: major.support.phases.dates.start, 26 ltsStart: major.support.phases.dates.lts, 27 maintenanceStart: major.support.phases.dates.maintenance, 28 endOfLife: major.support.phases.dates.end, 29 - npm: latestVersion.dependencies.npm, 30 - v8: latestVersion.dependencies.v8, 31 - releaseDate: latestVersion.releaseDate, 32 - modules: latestVersion.modules.version, 33 }; 34 }); 35 36 return writeFile( 37 jsonFilePath,
··· 6 // this is the destination path for where the JSON file will be written 7 const jsonFilePath = join(process.cwd(), 'public/node-releases-data.json'); 8 9 + // Gets the appropriate release status for each major release 10 + const getNodeReleaseStatus = (now, support) => { 11 + const { endOfLife, maintenanceStart, ltsStart, currentStart } = support; 12 + 13 + if (endOfLife && now > new Date(endOfLife)) { 14 + return 'End-of-life'; 15 + } 16 + 17 + if (maintenanceStart && now > new Date(maintenanceStart)) { 18 + return 'Maintenance LTS'; 19 + } 20 + 21 + if (ltsStart && now > new Date(ltsStart)) { 22 + return 'Active LTS'; 23 + } 24 + 25 + if (currentStart && now >= new Date(currentStart)) { 26 + return 'Current'; 27 + } 28 + 29 + return 'Pending'; 30 + }; 31 + 32 const generateNodeReleasesJson = async () => { 33 const nodevuOutput = await nodevu({ fetch: fetch }); 34 35 // Filter out those without documented support 36 // Basically those not in schedule.json 37 + const majors = Object.values(nodevuOutput).filter(major => !!major.support); 38 39 const nodeReleases = majors.map(major => { 40 const [latestVersion] = Object.values(major.releases); 41 42 + const support = { 43 currentStart: major.support.phases.dates.start, 44 ltsStart: major.support.phases.dates.lts, 45 maintenanceStart: major.support.phases.dates.maintenance, 46 endOfLife: major.support.phases.dates.end, 47 + }; 48 + 49 + const status = getNodeReleaseStatus(new Date(), support); 50 + 51 + return { 52 + ...support, 53 + status, 54 + major: latestVersion.semver.major, 55 + version: latestVersion.semver.raw, 56 + versionWithPrefix: `v${latestVersion.semver.raw}`, 57 + codename: major.codename || '', 58 + isLts: status === 'Active LTS' || status === 'Maintenance LTS', 59 + npm: latestVersion.dependencies.npm || '', 60 + v8: latestVersion.dependencies.v8 || '', 61 + releaseDate: latestVersion.releaseDate || '', 62 + modules: latestVersion.modules.version || '', 63 }; 64 }); 65 + 66 + console.timeEnd('g'); 67 68 return writeFile( 69 jsonFilePath,
+5 -1
next.config.mjs
··· 1 'use strict'; 2 3 import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; 4 import { redirects, rewrites } from './next.rewrites.mjs'; 5 ··· 13 swcMinify: true, 14 // We don't use trailing slashes on URLs from the Node.js Website 15 trailingSlash: false, 16 // We allow the BASE_PATH to be overridden in case that the Website 17 // is being built on a subdirectory (e.g. /nodejs-website) 18 basePath: BASE_PATH, ··· 63 }, 64 }; 65 66 - export default nextConfig;
··· 1 'use strict'; 2 3 + import withNextIntl from 'next-intl/plugin'; 4 + 5 import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; 6 import { redirects, rewrites } from './next.rewrites.mjs'; 7 ··· 15 swcMinify: true, 16 // We don't use trailing slashes on URLs from the Node.js Website 17 trailingSlash: false, 18 + // We don't want to redirect with trailing slashes 19 + skipTrailingSlashRedirect: true, 20 // We allow the BASE_PATH to be overridden in case that the Website 21 // is being built on a subdirectory (e.g. /nodejs-website) 22 basePath: BASE_PATH, ··· 67 }, 68 }; 69 70 + export default withNextIntl()(nextConfig);
+51 -16
next.constants.mjs
··· 1 'use strict'; 2 3 - import { blogData } from './next.json.mjs'; 4 import { defaultLocale } from './next.locales.mjs'; 5 6 /** ··· 74 export const MD_EXTENSION_REGEX = /((\/)?(index))?\.mdx?$/i; 75 76 /** 77 - * This is a shorthand to the Default Locale if you're only interested 78 - * on the Locale Code. 79 - * 80 - * This should only be used outside of the Next.js Application itself 81 - * as within React context the `useLocale` hook should be used instead. 82 - */ 83 - export const DEFAULT_LOCALE_CODE = defaultLocale.code; 84 - 85 - /** 86 * This is a list of all static routes or pages from the Website that we do not 87 * want to allow to be statically built on our Static Export Build. 88 * 89 * @type {((route: import('./types').RouteSegment) => boolean)[]} A list of Ignored Routes by Regular Expressions 90 */ 91 export const STATIC_ROUTES_IGNORES = [ 92 // This is used to ignore is used to ignore all blog routes except for the English language 93 - route => !route.localised && /^blog\//.test(route.pathname), 94 // This is used to ignore the blog/pagination meta route 95 - route => /^blog\/pagination/.test(route.pathname), 96 ]; 97 98 /** ··· 125 * within the static export (static build) of the website. This constant usually would be used along 126 * with a matching pathname on `DYNAMIC_ROUTES_REWRITES`. 127 * 128 - * @returns {string[]} A list of all the Dynamic Routes that are generated by the Website 129 */ 130 - export const DYNAMIC_GENERATED_ROUTES = () => [ 131 - ...blogData.pagination.map(year => `en/blog/year-${year}`), 132 ]; 133 134 /*** ··· 148 /** 149 * The `localStorage` key to store the theme choice of `next-themes` 150 */ 151 - export const THEME_LOCAL_STORAGE_KEY = 'theme';
··· 1 'use strict'; 2 3 + import { blogData, siteConfig } from './next.json.mjs'; 4 import { defaultLocale } from './next.locales.mjs'; 5 6 /** ··· 74 export const MD_EXTENSION_REGEX = /((\/)?(index))?\.mdx?$/i; 75 76 /** 77 * This is a list of all static routes or pages from the Website that we do not 78 * want to allow to be statically built on our Static Export Build. 79 * 80 * @type {((route: import('./types').RouteSegment) => boolean)[]} A list of Ignored Routes by Regular Expressions 81 */ 82 export const STATIC_ROUTES_IGNORES = [ 83 + // Ignore the 404 route on Static Generation 84 + ({ pathname }) => pathname === '404', 85 // This is used to ignore is used to ignore all blog routes except for the English language 86 + ({ locale, pathname }) => 87 + locale !== defaultLocale.code && /^blog\//.test(pathname), 88 // This is used to ignore the blog/pagination meta route 89 + ({ pathname }) => /^blog\/pagination/.test(pathname), 90 ]; 91 92 /** ··· 119 * within the static export (static build) of the website. This constant usually would be used along 120 * with a matching pathname on `DYNAMIC_ROUTES_REWRITES`. 121 * 122 + * @type {string[]} A list of all the Dynamic Routes that are generated by the Website 123 */ 124 + export const DYNAMIC_GENERATED_ROUTES = [ 125 + ...blogData.pagination.map(year => `blog/year-${year}`), 126 ]; 127 128 /*** ··· 142 /** 143 * The `localStorage` key to store the theme choice of `next-themes` 144 */ 145 + export const THEME_STORAGE_KEY = 'theme'; 146 + 147 + /** 148 + * This is the default Next.js Page Metadata for all pages 149 + * 150 + * @type {import('next').Metadata} 151 + */ 152 + export const DEFAULT_METADATA = { 153 + metadataBase: new URL(`${BASE_URL}${BASE_PATH}`), 154 + title: siteConfig.title, 155 + description: siteConfig.description, 156 + robots: { index: true, follow: true }, 157 + twitter: { 158 + card: siteConfig.twitter.card, 159 + title: siteConfig.twitter.title, 160 + creator: siteConfig.twitter.username, 161 + images: { 162 + url: siteConfig.twitter.img, 163 + alt: siteConfig.twitter.imgAlt, 164 + }, 165 + }, 166 + alternates: { 167 + canonical: '', 168 + languages: { 'x-default': '' }, 169 + types: { 170 + 'application/rss+xml': 'https://nodejs.org/en/feed/blog.xml', 171 + }, 172 + }, 173 + icons: { icon: siteConfig.favicon }, 174 + openGraph: { images: siteConfig.twitter.img }, 175 + }; 176 + 177 + /** 178 + * This is the default Next.js Viewport Metadata for all pages 179 + * 180 + * @type {import('next').Viewport} 181 + */ 182 + export const DEFAULT_VIEWPORT = { 183 + themeColor: siteConfig.accentColor, 184 + width: 'device-width', 185 + initialScale: 1, 186 + };
+190 -120
next.dynamic.mjs
··· 1 'use strict'; 2 3 - import { readFileSync } from 'node:fs'; 4 import { join, normalize, sep } from 'node:path'; 5 6 import { VFile } from 'vfile'; 7 8 - import { DEFAULT_LOCALE_CODE, MD_EXTENSION_REGEX } from './next.constants.mjs'; 9 import { getMarkdownFiles } from './next.helpers.mjs'; 10 - import { availableLocales } from './next.locales.mjs'; 11 import { compileMDX } from './next.mdx.compiler.mjs'; 12 13 - // allows us to run a glob to get markdown files based on a language folder 14 - const getPathsByLanguage = async (locale = DEFAULT_LOCALE_CODE, ignored = []) => 15 - getMarkdownFiles(process.cwd(), `pages/${locale}`, ignored); 16 17 - /** 18 - * This method is responsible for generating a Collection of all available paths that 19 - * are served by the Website dynamically based on the Markdown pages on `pages/` folder. 20 - * 21 - * Each Collection is associated to its Locale Code and contains a subset of Dictionaries 22 - * that inform which pages are provided by that language and which not. 23 - * 24 - * The non-localised pages will still be served but our runtime Markdown loader `getMarkdownFile` 25 - * will recognise that the requested route should be provided via the fallback language. 26 - */ 27 - const getAllPaths = async () => { 28 - // during full static build we don't want to cover blog posts 29 - // as otherwise they will all get built as static pages during build time 30 - const sourcePages = await getPathsByLanguage(DEFAULT_LOCALE_CODE); 31 32 /** 33 - * This method is used to provide the list of pages that are provided by a given locale 34 - * and what pages require fallback to the default locale. 35 */ 36 - const mergePathsWithFallback = 37 - (locale = '') => 38 - (files = []) => 39 - sourcePages.map(filename => { 40 - // remove the index.md(x) suffix from a pathname 41 - let pathname = filename.replace(MD_EXTENSION_REGEX, ''); 42 43 - // remove trailing slash for correct Windows pathing of the index files 44 - if (pathname.length > 1 && pathname.endsWith(sep)) { 45 - pathname = pathname.substring(0, pathname.length - 1); 46 - } 47 - 48 - return { 49 - pathname: normalize(pathname), 50 - filename: filename, 51 - localised: files.includes(filename), 52 - routeWithLocale: `${locale}/${pathname}`, 53 - }; 54 - }); 55 56 /** 57 - * This creates an index with the information for each language 58 - * and the pages that they provide in relation to the source pages 59 - * and the pages that are missing in relation to the source pages. 60 * 61 - * @type {[string, import('./types').RouteSegment[]][]} 62 */ 63 - const allAvailableMarkdownPaths = availableLocales.map(({ code }) => 64 - getPathsByLanguage(code) 65 - .then(mergePathsWithFallback(code)) 66 - .then(files => [code, files]) 67 - ); 68 69 - return Promise.all(allAvailableMarkdownPaths); 70 - }; 71 72 - // A Map containing all the dynamic paths and their information 73 - export const allPaths = new Map(await getAllPaths()); 74 75 - /** 76 - * This method attempts to find a matching file in the fileystem provided originally 77 - * by `getStaticPaths` and returns the file source and filename. 78 - * 79 - * Note that this method is safe as it is always provided by paths determined by the server 80 - * that are non-localized pages that exist on the English locale. 81 - * 82 - * Hence we don't fallback for non-existing pages as it should never fall into this scenario. 83 - * Next.js will already protect against common attack vectors 84 - * such as `/../../` on the URL pathname and other methodologies 85 - * 86 - * @param {string} locale the locale code to be used 87 - * @param {string} pathname the pathname string 88 - * @returns {{ source: string, filename: string }} the source and filename 89 - * @throws {Error} if the file does not exist, which should never happen 90 - */ 91 - export const getMarkdownFile = ( 92 - locale = DEFAULT_LOCALE_CODE, 93 - pathname = '' 94 - ) => { 95 - const metadata = { source: '', filename: '' }; 96 97 - const routes = allPaths.get(locale); 98 99 - // We verify if the file exists within the list of allowed pages 100 - // which prevents any malicious attempts to access non-allowed pages 101 - // or other files that do not belong to the `sourcePages` 102 - if (routes && routes.length) { 103 - const route = routes.find(route => route.pathname === normalize(pathname)); 104 105 - if (route && route.filename) { 106 - // this determines if we should be using the fallback rendering to the default locale 107 - // or if we can use the current locale 108 - const localeToUse = !route.localised ? DEFAULT_LOCALE_CODE : locale; 109 110 - // gets the full pathname for the file (absolute path) 111 - metadata.filename = join( 112 - process.cwd(), 113 - 'pages', 114 - localeToUse, 115 - route.filename 116 ); 117 118 - // Since we always will only read files that we know exist 119 - // we don't need to handle a possibility of an error being thrown 120 - // as any other case is if we don't have file system access and that should 121 - // then be thrown and reported 122 - metadata.source = readFileSync(metadata.filename, 'utf8'); 123 } 124 - } 125 126 - return metadata; 127 - }; 128 129 - /** 130 - * This Method gathers the Markdown Source and the source filename 131 - * and processes the data (parses the markdown) and generate props 132 - * for the application to consume (`getStaticProps`) 133 - * 134 - * @returns {Promise<{ notFound: boolean, props: any; revalidate: number | boolean }>} the props for the page 135 - */ 136 - export const generateStaticProps = async (source = '', filename = '') => { 137 - // by default a page is not found if there's no source or filename 138 - const staticProps = { notFound: true, props: {}, revalidate: false }; 139 140 - // We only attempt to serialize data if the `source` has content and `filename` has content 141 - // otherwise we return a 404 since this means that it is not a valid file or a file we should care about 142 - if (source.length && filename.length) { 143 // We create a VFile (Virtual File) to be able to access some contextual 144 // data post serialization (compilation) of the source Markdown into MDX 145 const sourceAsVirtualFile = new VFile(source); ··· 149 150 // This compiles our MDX source (VFile) into a final MDX-parsed VFile 151 // that then is passed as a string to the MDXProvider which will run the MDX Code 152 - const { content, headings, frontmatter } = await compileMDX( 153 - sourceAsVirtualFile, 154 - fileExtension 155 - ); 156 157 - // Passes the compiled MDX Source to the MDX Provider and some extra data 158 - staticProps.props = { content: String(content), headings, frontmatter }; 159 - staticProps.notFound = false; 160 - } 161 162 - return staticProps; 163 };
··· 1 'use strict'; 2 3 + import { existsSync } from 'node:fs'; 4 + import { readFile } from 'node:fs/promises'; 5 import { join, normalize, sep } from 'node:path'; 6 7 + import matter from 'gray-matter'; 8 + import { cache } from 'react'; 9 import { VFile } from 'vfile'; 10 11 + import { 12 + DYNAMIC_GENERATED_ROUTES, 13 + DYNAMIC_ROUTES_IGNORES, 14 + DYNAMIC_ROUTES_REWRITES, 15 + MD_EXTENSION_REGEX, 16 + STATIC_ROUTES_IGNORES, 17 + DEFAULT_METADATA, 18 + BASE_URL, 19 + BASE_PATH, 20 + } from './next.constants.mjs'; 21 import { getMarkdownFiles } from './next.helpers.mjs'; 22 + import { siteConfig } from './next.json.mjs'; 23 + import { availableLocaleCodes, defaultLocale } from './next.locales.mjs'; 24 import { compileMDX } from './next.mdx.compiler.mjs'; 25 26 + // This is the combination of the Application Base URL and Base PATH 27 + const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; 28 29 + // This is a small utility that allows us to quickly separate locale from the remaning pathname 30 + const getPathname = (path = []) => path.join('/'); 31 + 32 + // This tests if the current pathname matches any expression that belongs 33 + // to the list of ignored routes and if it does we return `true` to indicate that 34 + const shouldIgnoreRoute = pathname => 35 + pathname.length > 0 && DYNAMIC_ROUTES_IGNORES.some(e => e.test(pathname)); 36 + 37 + // This tests if the current pathname matches any sort of rewrite rule 38 + // and if it does we return a the replacement expression for the pathname 39 + const getRouteRewrite = pathname => 40 + (pathname.length > 0 && 41 + DYNAMIC_ROUTES_REWRITES.find(([e]) => e.test(pathname))) || 42 + []; 43 + 44 + // This maps a pathname into an actual route object that can be used 45 + // we use a platform-specific separator to split the pathname 46 + // since we're using filepaths here and not URL paths 47 + const mapPathToRoute = (locale = defaultLocale.code, path = '') => ({ 48 + locale, 49 + path: path.split(sep), 50 + }); 51 + 52 + const getDynamicRouter = async () => { 53 + const cachedMarkdownFiles = new Map(); 54 + const pathnameToFilename = new Map(); 55 + 56 + const websitePages = await getMarkdownFiles( 57 + process.cwd(), 58 + `pages/${defaultLocale.code}` 59 + ); 60 + 61 + websitePages.forEach(filename => { 62 + let pathname = filename.replace(MD_EXTENSION_REGEX, ''); 63 + 64 + if (pathname.length > 1 && pathname.endsWith(sep)) { 65 + pathname = pathname.substring(0, pathname.length - 1); 66 + } 67 + 68 + pathname = normalize(pathname).replace('.', ''); 69 + 70 + // We map the pathname to the filename to be able to quickly 71 + // resolve the filename for a given pathname 72 + pathnameToFilename.set(pathname, filename); 73 + }); 74 75 /** 76 + * This method returns a list of all routes that exist for a given locale 77 + * 78 + * @param {string} locale 79 + * @returns {string[]} 80 */ 81 + const getRoutesByLanguage = async (locale = defaultLocale.code) => { 82 + const shouldIgnoreStaticRoute = pathname => 83 + STATIC_ROUTES_IGNORES.every(e => !e({ pathname, locale })); 84 85 + return [...pathnameToFilename.keys()] 86 + .filter(shouldIgnoreStaticRoute) 87 + .concat(DYNAMIC_GENERATED_ROUTES); 88 + }; 89 90 /** 91 + * This method attempts to retrieve either a localized Markdown file 92 + * or the English version of the Markdown file if no localized version exists 93 + * and then returns the contents of the file and the name of the file (not the path) 94 * 95 + * @param {string} locale 96 + * @param {string} pathname 97 + * @returns {Promise<{ source: string; filename: string }>} 98 */ 99 + const _getMarkdownFile = async (locale = '', pathname = '') => { 100 + const normalizedPathname = normalize(pathname).replace('.', ''); 101 102 + // This verifies if the given pathname actually exists on our Map 103 + // meaning that the route exists on the website and can be rendered 104 + if (pathnameToFilename.has(normalizedPathname)) { 105 + const filename = pathnameToFilename.get(normalizedPathname); 106 107 + let filePath = join(process.cwd(), 'pages'); 108 109 + // We verify if our Markdown cache already has a cache entry for a localized 110 + // version of this file, because if not, it means that either 111 + // we did not cache this file yet or there is no localized version of this file 112 + if (cachedMarkdownFiles.has(`${locale}${normalizedPathname}`)) { 113 + const fileContent = cachedMarkdownFiles.get( 114 + `${locale}${normalizedPathname}` 115 + ); 116 117 + return { source: fileContent, filename }; 118 + } 119 120 + // No cache hit exists, so we check if the localized file actually 121 + // exists within our file system and if it does we set it on the cache 122 + // and return the current fetched result; If the file does not exist 123 + // we fallback to the English source 124 + if (existsSync(join(filePath, locale, filename))) { 125 + filePath = join(filePath, locale, filename); 126 127 + const fileContent = await readFile(filePath, 'utf8'); 128 129 + cachedMarkdownFiles.set(`${locale}${normalizedPathname}`, fileContent); 130 + 131 + return { source: fileContent, filename }; 132 + } 133 + 134 + // We then attempt to retrieve the source version of the file as there is no localised version 135 + // of the file and we set it on the cache to prevent future checks of the same locale for this file 136 + const { source: fileContent } = getMarkdownFile( 137 + defaultLocale.code, 138 + pathname 139 ); 140 141 + // We set the source file on the localized cache to prevent future checks 142 + // of the same locale for this file and improve read performance 143 + cachedMarkdownFiles.set(`${locale}${normalizedPathname}`, fileContent); 144 + 145 + return { source: fileContent, filename }; 146 } 147 148 + return { filename: '', source: '' }; 149 + }; 150 151 + // Creates a Cached Version of the Markdown File Resolver 152 + const getMarkdownFile = cache(async (locale, pathname) => { 153 + return await _getMarkdownFile(locale, pathname); 154 + }); 155 156 + /** 157 + * This method runs the MDX compiler on the server-side and returns the 158 + * parsed JSX ready to be rendered on a page as a React Component 159 + * 160 + * @param {string} source 161 + * @param {string} filename 162 + */ 163 + const _getMDXContent = async (source = '', filename = '') => { 164 // We create a VFile (Virtual File) to be able to access some contextual 165 // data post serialization (compilation) of the source Markdown into MDX 166 const sourceAsVirtualFile = new VFile(source); ··· 170 171 // This compiles our MDX source (VFile) into a final MDX-parsed VFile 172 // that then is passed as a string to the MDXProvider which will run the MDX Code 173 + return compileMDX(sourceAsVirtualFile, fileExtension); 174 + }; 175 + 176 + // Creates a Cached Version of the MDX Compiler 177 + const getMDXContent = cache(async (source, filename) => { 178 + return await _getMDXContent(source, filename); 179 + }); 180 + 181 + /** 182 + * This method generates the Next.js App Router Metadata 183 + * that can be used for each page to provide metadata 184 + * 185 + * @param {string} locale 186 + * @param {string} path 187 + * @returns {import('next').Metadata} 188 + */ 189 + const _getPageMetadata = async (locale = defaultLocale.code, path = '') => { 190 + const pageMetadata = { ...DEFAULT_METADATA }; 191 + 192 + const { source = '' } = await getMarkdownFile(locale, path); 193 + 194 + const { data } = matter(source); 195 + 196 + pageMetadata.title = data.title 197 + ? `${data.title} | ${siteConfig.title}` 198 + : siteConfig.title; 199 + 200 + pageMetadata.twitter.title = pageMetadata.title; 201 + pageMetadata.alternates.canonical = `${baseUrlAndPath}/${locale}/${path}`; 202 203 + pageMetadata.alternates.languages[ 204 + 'x-default' 205 + ] = `${baseUrlAndPath}/${defaultLocale.code}/${path}`; 206 + 207 + availableLocaleCodes.forEach(currentLocale => { 208 + pageMetadata.alternates.languages[ 209 + currentLocale 210 + ] = `${baseUrlAndPath}/${currentLocale}/${path}`; 211 + }); 212 213 + return pageMetadata; 214 + }; 215 + 216 + // Creates a Cached Version of the Page Metadata Context 217 + const getPageMetadata = cache(async (locale, path) => { 218 + return await _getPageMetadata(locale, path); 219 + }); 220 + 221 + return { 222 + getRoutesByLanguage, 223 + getMarkdownFile, 224 + getMDXContent, 225 + getPathname, 226 + shouldIgnoreRoute, 227 + getRouteRewrite, 228 + mapPathToRoute, 229 + getPageMetadata, 230 + }; 231 }; 232 + 233 + export const dynamicRouter = await getDynamicRouter();
+23 -14
next.json.mjs
··· 1 'use strict'; 2 3 - import localeConfig from './i18n/config.json' assert { type: 'json' }; 4 - import siteNavigation from './navigation.json' assert { type: 'json' }; 5 - import blogData from './public/blog-posts-data.json' assert { type: 'json' }; 6 - import releaseData from './public/node-releases-data.json' assert { type: 'json' }; 7 - import siteRedirects from './redirects.json' assert { type: 'json' }; 8 - import siteConfig from './site.json' assert { type: 'json' }; 9 10 - export { 11 - siteConfig, 12 - siteNavigation, 13 - siteRedirects, 14 - localeConfig, 15 - blogData, 16 - releaseData, 17 - };
··· 1 'use strict'; 2 3 + import _localeConfig from './i18n/config.json' assert { type: 'json' }; 4 + import _siteNavigation from './navigation.json' assert { type: 'json' }; 5 + import _blogData from './public/blog-posts-data.json' assert { type: 'json' }; 6 + import _releaseData from './public/node-releases-data.json' assert { type: 'json' }; 7 + import _siteRedirects from './redirects.json' assert { type: 'json' }; 8 + import _siteConfig from './site.json' assert { type: 'json' }; 9 + 10 + /** @type {import('./types').LocaleConfig[]} */ 11 + export const localeConfig = _localeConfig; 12 + 13 + /** @type {Record<string, import('./types').NavigationEntry>} */ 14 + export const siteNavigation = _siteNavigation; 15 + 16 + /** @type {import('./types').BlogData} */ 17 + export const blogData = _blogData; 18 + 19 + /** @type {import('./types').NodeRelease[]} */ 20 + export const releaseData = _releaseData; 21 + 22 + /** @type {Record<string, import('./types').Redirect[]>} */ 23 + export const siteRedirects = _siteRedirects; 24 25 + /** @type {import('./types').SiteConfig} */ 26 + export const siteConfig = _siteConfig;
+9 -33
next.locales.mjs
··· 1 'use strict'; 2 3 - import translations from './i18n/locales/index.mjs'; 4 import { localeConfig } from './next.json.mjs'; 5 6 // As set of available and enabled locales for the website ··· 8 // of the available locales that we have enabled on the website 9 const availableLocales = localeConfig.filter(locale => locale.enabled); 10 11 // This provides the default locale information for the Next.js Application 12 // This is marked by the unique `locale.default` property on the `en` locale 13 /** @type {import('./types').LocaleConfig} */ 14 const defaultLocale = availableLocales.find(locale => locale.default); 15 16 - /** 17 - * Retrieves the Current Locale from the given route or URL Query 18 - * 19 - * This will first check the route for the locale code, if it is not found 20 - * it will then check the URL Query for the locale code, if it is not found 21 - * it will then return the default locale 22 - * 23 - * @param {string} route Current Route String 24 - * @param {import('querystring').ParsedUrlQuery} query Current Route Query 25 - * @returns {import('./types').LocaleConfig} Returns the Current Locale 26 - */ 27 - const getCurrentLocale = (route = '/', query = {}) => { 28 - const localeCode = route.split('/')[1] || query.locale || defaultLocale.code; 29 - 30 - return availableLocales.find(c => c.code === localeCode) || defaultLocale; 31 - }; 32 - 33 - /** 34 - * Retrieves the Current Translations for the given locale 35 - * 36 - * This will merge the default locale translations with the current locale translations 37 - * This allows us to only provide the translations that are different from the default locale 38 - * 39 - * @param {string} locale The current Locale Code 40 - * @returns {Record<string, string>} Returns the set of Translations for the given locale 41 - */ 42 - const getCurrentTranslations = locale => ({ 43 - ...translations[defaultLocale.code], 44 - ...translations[locale], 45 - }); 46 47 export { 48 availableLocales, 49 defaultLocale, 50 - getCurrentLocale, 51 - getCurrentTranslations, 52 };
··· 1 'use strict'; 2 3 import { localeConfig } from './next.json.mjs'; 4 5 // As set of available and enabled locales for the website ··· 7 // of the available locales that we have enabled on the website 8 const availableLocales = localeConfig.filter(locale => locale.enabled); 9 10 + // This gives an easy way of accessing all available locale codes 11 + const availableLocaleCodes = availableLocales.map(locale => locale.code); 12 + 13 // This provides the default locale information for the Next.js Application 14 // This is marked by the unique `locale.default` property on the `en` locale 15 /** @type {import('./types').LocaleConfig} */ 16 const defaultLocale = availableLocales.find(locale => locale.default); 17 18 + // Creates a Map of available locales for easy access 19 + const availableLocalesMap = Object.fromEntries( 20 + localeConfig.map(locale => [locale.code, locale]) 21 + ); 22 23 export { 24 availableLocales, 25 + availableLocaleCodes, 26 + availableLocalesMap, 27 defaultLocale, 28 };
+6 -22
next.mdx.compiler.mjs
··· 1 'use strict'; 2 3 - import { compile, runSync } from '@mdx-js/mdx'; 4 import * as jsxRuntime from 'react/jsx-runtime'; 5 import { matter } from 'vfile-matter'; 6 ··· 12 * 13 * @param {import('vfile').VFile} source 14 * @param {'md' | 'mdx'} fileExtension 15 - * @returns {Promise<{ content: import('vfile').VFile; headings: import('@vcarl/remark-headings').Heading[]; frontmatter: Record<string, any>}>} 16 */ 17 export async function compileMDX(source, fileExtension) { 18 // Parses the Frontmatter to the VFile and removes from the original source ··· 20 matter(source, { strip: true }); 21 22 // This is a minimal MDX Compiler that is lightweight and only parses the MDX 23 - const compiledSource = await compile(source, { 24 rehypePlugins: NEXT_REHYPE_PLUGINS, 25 remarkPlugins: NEXT_REMARK_PLUGINS, 26 format: fileExtension, 27 - // This instructs the MDX compiler to generate a minimal JSX-body 28 - // to be consumed within MDX's `run` method, instead of a standalone React Application 29 - outputFormat: 'function-body', 30 - // Ensure compatibility with Server Components 31 - providerImportSource: undefined, 32 }); 33 34 // Retrieve some parsed data from the VFile metadata 35 // such as frontmatter and Markdown headings 36 const { headings, matter: frontmatter } = source.data; 37 38 - return { content: compiledSource, headings, frontmatter }; 39 - } 40 - 41 - /** 42 - * This evaluates our MDX VFile into actual JSX eval'd code 43 - * which is actually used by the MDX Provider 44 - * 45 - * @param {string} source 46 - * @returns {import('mdx/types').MDXContent} 47 - */ 48 - export function runMDX(source) { 49 - const { default: content } = runSync(source, jsxRuntime); 50 - 51 - return content; 52 }
··· 1 'use strict'; 2 3 + import { evaluate } from '@mdx-js/mdx'; 4 import * as jsxRuntime from 'react/jsx-runtime'; 5 import { matter } from 'vfile-matter'; 6 ··· 12 * 13 * @param {import('vfile').VFile} source 14 * @param {'md' | 'mdx'} fileExtension 15 + * @returns {Promise<{ MDXContent: import('mdx/types').MDXContent; headings: import('@vcarl/remark-headings').Heading[]; frontmatter: Record<string, any>}>} 16 */ 17 export async function compileMDX(source, fileExtension) { 18 // Parses the Frontmatter to the VFile and removes from the original source ··· 20 matter(source, { strip: true }); 21 22 // This is a minimal MDX Compiler that is lightweight and only parses the MDX 23 + const { default: MDXContent } = await evaluate(source, { 24 rehypePlugins: NEXT_REHYPE_PLUGINS, 25 remarkPlugins: NEXT_REMARK_PLUGINS, 26 format: fileExtension, 27 + // Provide the JSX Runtime to the MDX Compiler 28 + ...jsxRuntime, 29 }); 30 31 // Retrieve some parsed data from the VFile metadata 32 // such as frontmatter and Markdown headings 33 const { headings, matter: frontmatter } = source.data; 34 35 + return { MDXContent, headings, frontmatter }; 36 }
+7 -7
next.mdx.use.mjs
··· 1 'use strict'; 2 3 - import NodeApiVersionLinks from '@/components/Docs/NodeApiVersionLinks'; 4 - import DownloadReleasesTable from '@/components/Downloads/DownloadReleasesTable'; 5 - import Banner from '@/components/Home/Banner'; 6 - import HomeDownloadButton from '@/components/Home/HomeDownloadButton'; 7 - import LocalizedLink from '@/components/LocalizedLink'; 8 - import { WithNodeRelease } from '@/providers/withNodeRelease'; 9 10 /** 11 * A full list of React Components that we want to passthrough to MDX ··· 26 * @type {import('mdx/types').MDXComponents} 27 */ 28 export const htmlComponents = { 29 - a: LocalizedLink, 30 blockquote: ({ children }) => <div className="highlight-box">{children}</div>, 31 };
··· 1 'use strict'; 2 3 + import NodeApiVersionLinks from './components/Docs/NodeApiVersionLinks'; 4 + import DownloadReleasesTable from './components/Downloads/DownloadReleasesTable'; 5 + import Banner from './components/Home/Banner'; 6 + import HomeDownloadButton from './components/Home/HomeDownloadButton'; 7 + import { WithNodeRelease } from './components/withNodeRelease'; 8 + import { Link } from './navigation.mjs'; 9 10 /** 11 * A full list of React Components that we want to passthrough to MDX ··· 26 * @type {import('mdx/types').MDXComponents} 27 */ 28 export const htmlComponents = { 29 + a: Link, 30 blockquote: ({ children }) => <div className="highlight-box">{children}</div>, 31 };
+2 -5
next.rewrites.mjs
··· 1 'use strict'; 2 3 import { siteRedirects } from './next.json.mjs'; 4 - import { availableLocales } from './next.locales.mjs'; 5 - 6 - // We only need Locale Codes for our URL redirects and rewrties 7 - const localeCodes = availableLocales.map(locale => locale.code); 8 9 // This allows us to prefix redirects with all available locale codes so that redirects are not bound to a single locale 10 // This also transforms the locale itself as a matching group that can be used for rewrites ··· 12 // Example: /:locale(ar/|ca/|de/|en/|es/|fa/|fr/|)about/security 13 // Would match /ar/about/security, /ar/about/security/ for every language code (replace "ar") and 14 // it would also match /about/security (without any language prefix) 15 - const localesMatch = `/:locale(${localeCodes.join('|')}|)?/`; 16 17 /** 18 * These are external redirects that happen before we check dynamic routes and rewrites
··· 1 'use strict'; 2 3 import { siteRedirects } from './next.json.mjs'; 4 + import { availableLocaleCodes } from './next.locales.mjs'; 5 6 // This allows us to prefix redirects with all available locale codes so that redirects are not bound to a single locale 7 // This also transforms the locale itself as a matching group that can be used for rewrites ··· 9 // Example: /:locale(ar/|ca/|de/|en/|es/|fa/|fr/|)about/security 10 // Would match /ar/about/security, /ar/about/security/ for every language code (replace "ar") and 11 // it would also match /about/security (without any language prefix) 12 + const localesMatch = `/:locale(${availableLocaleCodes.join('|')}|)?/`; 13 14 /** 15 * These are external redirects that happen before we check dynamic routes and rewrites
+97 -150
package-lock.json
··· 28 "husky": "8.0.3", 29 "lint-staged": "14.0.1", 30 "next": "~14.0.0", 31 "next-themes": "~0.2.1", 32 "postcss": "~8.4.30", 33 "postcss-calc": "~9.0.1", ··· 36 "postcss-simple-vars": "~7.0.1", 37 "react": "^18.2.0", 38 "react-dom": "^18.2.0", 39 - "react-intl": "~6.4.7", 40 "rehype-autolink-headings": "~7.0.0", 41 "rehype-slug": "~6.0.0", 42 "remark-gfm": "~4.0.0", ··· 2997 "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", 2998 "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" 2999 }, 3000 - "node_modules/@formatjs/ecma402-abstract": { 3001 - "version": "1.17.2", 3002 - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.2.tgz", 3003 - "integrity": "sha512-k2mTh0m+IV1HRdU0xXM617tSQTi53tVR2muvYOsBeYcUgEAyxV1FOC7Qj279th3fBVQ+Dj6muvNJZcHSPNdbKg==", 3004 - "dependencies": { 3005 - "@formatjs/intl-localematcher": "0.4.2", 3006 - "tslib": "^2.4.0" 3007 - } 3008 - }, 3009 - "node_modules/@formatjs/fast-memoize": { 3010 - "version": "2.2.0", 3011 - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", 3012 - "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", 3013 - "dependencies": { 3014 - "tslib": "^2.4.0" 3015 - } 3016 - }, 3017 - "node_modules/@formatjs/icu-messageformat-parser": { 3018 - "version": "2.6.2", 3019 - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.2.tgz", 3020 - "integrity": "sha512-nF/Iww7sc5h+1MBCDRm68qpHTCG4xvGzYs/x9HFcDETSGScaJ1Fcadk5U/NXjXeCtzD+DhN4BAwKFVclHfKMdA==", 3021 - "dependencies": { 3022 - "@formatjs/ecma402-abstract": "1.17.2", 3023 - "@formatjs/icu-skeleton-parser": "1.6.2", 3024 - "tslib": "^2.4.0" 3025 - } 3026 - }, 3027 - "node_modules/@formatjs/icu-skeleton-parser": { 3028 - "version": "1.6.2", 3029 - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.2.tgz", 3030 - "integrity": "sha512-VtB9Slo4ZL6QgtDFJ8Injvscf0xiDd4bIV93SOJTBjUF4xe2nAWOoSjLEtqIG+hlIs1sNrVKAaFo3nuTI4r5ZA==", 3031 - "dependencies": { 3032 - "@formatjs/ecma402-abstract": "1.17.2", 3033 - "tslib": "^2.4.0" 3034 - } 3035 - }, 3036 - "node_modules/@formatjs/intl": { 3037 - "version": "2.9.3", 3038 - "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.9.3.tgz", 3039 - "integrity": "sha512-hclPdyCF1zk2XmhgdXfl5Sd30QEdRBnIijH7Vc1AWz2K0/saVRrxuL3UYn+m3xEyfOa4yDbTWVbmXDL0XEzlsQ==", 3040 - "dependencies": { 3041 - "@formatjs/ecma402-abstract": "1.17.2", 3042 - "@formatjs/fast-memoize": "2.2.0", 3043 - "@formatjs/icu-messageformat-parser": "2.6.2", 3044 - "@formatjs/intl-displaynames": "6.5.2", 3045 - "@formatjs/intl-listformat": "7.4.2", 3046 - "intl-messageformat": "10.5.3", 3047 - "tslib": "^2.4.0" 3048 - }, 3049 - "peerDependencies": { 3050 - "typescript": "^4.7 || 5" 3051 - }, 3052 - "peerDependenciesMeta": { 3053 - "typescript": { 3054 - "optional": true 3055 - } 3056 - } 3057 - }, 3058 - "node_modules/@formatjs/intl-displaynames": { 3059 - "version": "6.5.2", 3060 - "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.5.2.tgz", 3061 - "integrity": "sha512-uC2VBlz+WydGTDDpJwMTQuPH3CUpTricr91WH1QMfz5oEHg2sB7mUERcZONE/lu8MOe1jREIx4vBciZEVTqkmA==", 3062 - "dependencies": { 3063 - "@formatjs/ecma402-abstract": "1.17.2", 3064 - "@formatjs/intl-localematcher": "0.4.2", 3065 - "tslib": "^2.4.0" 3066 - } 3067 - }, 3068 - "node_modules/@formatjs/intl-listformat": { 3069 - "version": "7.4.2", 3070 - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.4.2.tgz", 3071 - "integrity": "sha512-+6bSVudEQkf12Hh7kuKt8Xv/MyFlqdwA4V4NLnTZW8uYdF9RxlOELDD0rPaOc2++TMKIzI5o6XXwHPvpL6VrPA==", 3072 - "dependencies": { 3073 - "@formatjs/ecma402-abstract": "1.17.2", 3074 - "@formatjs/intl-localematcher": "0.4.2", 3075 - "tslib": "^2.4.0" 3076 - } 3077 - }, 3078 - "node_modules/@formatjs/intl-localematcher": { 3079 - "version": "0.4.2", 3080 - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.4.2.tgz", 3081 - "integrity": "sha512-BGdtJFmaNJy5An/Zan4OId/yR9Ih1OojFjcduX/xOvq798OgWSyDtd6Qd5jqJXwJs1ipe4Fxu9+cshic5Ox2tA==", 3082 - "dependencies": { 3083 - "tslib": "^2.4.0" 3084 - } 3085 - }, 3086 "node_modules/@heroicons/react": { 3087 "version": "2.0.18", 3088 "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", ··· 8038 "@types/unist": "*" 8039 } 8040 }, 8041 - "node_modules/@types/hoist-non-react-statics": { 8042 - "version": "3.3.1", 8043 - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", 8044 - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", 8045 - "dependencies": { 8046 - "@types/react": "*", 8047 - "hoist-non-react-statics": "^3.3.0" 8048 - } 8049 - }, 8050 "node_modules/@types/html-minifier-terser": { 8051 "version": "6.1.0", 8052 "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", ··· 8235 "node_modules/@types/prop-types": { 8236 "version": "15.7.5", 8237 "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 8238 - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 8239 }, 8240 "node_modules/@types/qs": { 8241 "version": "6.9.9", ··· 8253 "version": "18.2.24", 8254 "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", 8255 "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", 8256 "dependencies": { 8257 "@types/prop-types": "*", 8258 "@types/scheduler": "*", ··· 8271 "node_modules/@types/scheduler": { 8272 "version": "0.16.3", 8273 "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", 8274 - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" 8275 }, 8276 "node_modules/@types/semver": { 8277 "version": "7.5.3", ··· 11694 "node_modules/csstype": { 11695 "version": "3.1.2", 11696 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 11697 - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" 11698 }, 11699 "node_modules/damerau-levenshtein": { 11700 "version": "1.0.8", ··· 16140 "minimalistic-crypto-utils": "^1.0.1" 16141 } 16142 }, 16143 - "node_modules/hoist-non-react-statics": { 16144 - "version": "3.3.2", 16145 - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", 16146 - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", 16147 - "dependencies": { 16148 - "react-is": "^16.7.0" 16149 - } 16150 - }, 16151 - "node_modules/hoist-non-react-statics/node_modules/react-is": { 16152 - "version": "16.13.1", 16153 - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 16154 - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 16155 - }, 16156 "node_modules/hosted-git-info": { 16157 "version": "2.8.9", 16158 "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", ··· 16606 "node": ">= 0.4" 16607 } 16608 }, 16609 - "node_modules/intl-messageformat": { 16610 - "version": "10.5.3", 16611 - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.3.tgz", 16612 - "integrity": "sha512-TzKn1uhJBMyuKTO4zUX47SU+d66fu1W9tVzIiZrQ6hBqQQeYscBMIzKL/qEXnFbJrH9uU5VV3+T5fWib4SIcKA==", 16613 - "dependencies": { 16614 - "@formatjs/ecma402-abstract": "1.17.2", 16615 - "@formatjs/fast-memoize": "2.2.0", 16616 - "@formatjs/icu-messageformat-parser": "2.6.2", 16617 - "tslib": "^2.4.0" 16618 - } 16619 - }, 16620 "node_modules/invariant": { 16621 "version": "2.2.4", 16622 "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", ··· 25830 "version": "0.6.3", 25831 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 25832 "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 25833 - "dev": true, 25834 "engines": { 25835 "node": ">= 0.6" 25836 } ··· 25884 "sass": { 25885 "optional": true 25886 } 25887 } 25888 }, 25889 "node_modules/next-themes": { ··· 27807 "dev": true, 27808 "peerDependencies": { 27809 "react": "^16.8.4 || ^17.0.0 || ^18.0.0" 27810 - } 27811 - }, 27812 - "node_modules/react-intl": { 27813 - "version": "6.4.7", 27814 - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.4.7.tgz", 27815 - "integrity": "sha512-0hnOHAZhxTFqD1hGTxrF40qNyZJPPYiGhWIIxIz0Udz+3e3c7sdN80qlxArR+AbJ+jb5ALXZkJYH20+GPFCM0Q==", 27816 - "dependencies": { 27817 - "@formatjs/ecma402-abstract": "1.17.2", 27818 - "@formatjs/icu-messageformat-parser": "2.6.2", 27819 - "@formatjs/intl": "2.9.3", 27820 - "@formatjs/intl-displaynames": "6.5.2", 27821 - "@formatjs/intl-listformat": "7.4.2", 27822 - "@types/hoist-non-react-statics": "^3.3.1", 27823 - "@types/react": "16 || 17 || 18", 27824 - "hoist-non-react-statics": "^3.3.2", 27825 - "intl-messageformat": "10.5.3", 27826 - "tslib": "^2.4.0" 27827 - }, 27828 - "peerDependencies": { 27829 - "react": "^16.6.0 || 17 || 18", 27830 - "typescript": "^4.7 || 5" 27831 - }, 27832 - "peerDependenciesMeta": { 27833 - "typescript": { 27834 - "optional": true 27835 - } 27836 } 27837 }, 27838 "node_modules/react-is": { ··· 34456 "@types/react": { 34457 "optional": true 34458 } 34459 } 34460 }, 34461 "node_modules/use-resize-observer": {
··· 28 "husky": "8.0.3", 29 "lint-staged": "14.0.1", 30 "next": "~14.0.0", 31 + "next-intl": "^3.0.0-rc.7", 32 "next-themes": "~0.2.1", 33 "postcss": "~8.4.30", 34 "postcss-calc": "~9.0.1", ··· 37 "postcss-simple-vars": "~7.0.1", 38 "react": "^18.2.0", 39 "react-dom": "^18.2.0", 40 "rehype-autolink-headings": "~7.0.0", 41 "rehype-slug": "~6.0.0", 42 "remark-gfm": "~4.0.0", ··· 2997 "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", 2998 "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" 2999 }, 3000 "node_modules/@heroicons/react": { 3001 "version": "2.0.18", 3002 "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", ··· 7952 "@types/unist": "*" 7953 } 7954 }, 7955 "node_modules/@types/html-minifier-terser": { 7956 "version": "6.1.0", 7957 "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", ··· 8140 "node_modules/@types/prop-types": { 8141 "version": "15.7.5", 8142 "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 8143 + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", 8144 + "devOptional": true 8145 }, 8146 "node_modules/@types/qs": { 8147 "version": "6.9.9", ··· 8159 "version": "18.2.24", 8160 "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", 8161 "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", 8162 + "devOptional": true, 8163 "dependencies": { 8164 "@types/prop-types": "*", 8165 "@types/scheduler": "*", ··· 8178 "node_modules/@types/scheduler": { 8179 "version": "0.16.3", 8180 "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", 8181 + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", 8182 + "devOptional": true 8183 }, 8184 "node_modules/@types/semver": { 8185 "version": "7.5.3", ··· 11602 "node_modules/csstype": { 11603 "version": "3.1.2", 11604 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 11605 + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", 11606 + "devOptional": true 11607 }, 11608 "node_modules/damerau-levenshtein": { 11609 "version": "1.0.8", ··· 16049 "minimalistic-crypto-utils": "^1.0.1" 16050 } 16051 }, 16052 "node_modules/hosted-git-info": { 16053 "version": "2.8.9", 16054 "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", ··· 16502 "node": ">= 0.4" 16503 } 16504 }, 16505 "node_modules/invariant": { 16506 "version": "2.2.4", 16507 "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", ··· 25715 "version": "0.6.3", 25716 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 25717 "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 25718 "engines": { 25719 "node": ">= 0.6" 25720 } ··· 25768 "sass": { 25769 "optional": true 25770 } 25771 + } 25772 + }, 25773 + "node_modules/next-intl": { 25774 + "version": "3.0.0-rc.7", 25775 + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.0.0-rc.7.tgz", 25776 + "integrity": "sha512-41gDE/WhqpT79UW4qgslB+FKdiBZHGND0Yfsy8f31lARZElCsQA177tDna87Nkzi2Ji//jHoJofoqlUKPXcIxw==", 25777 + "dependencies": { 25778 + "@formatjs/intl-localematcher": "^0.2.32", 25779 + "negotiator": "^0.6.3", 25780 + "use-intl": "3.0.0-rc.6" 25781 + }, 25782 + "peerDependencies": { 25783 + "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0", 25784 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 25785 + } 25786 + }, 25787 + "node_modules/next-intl/node_modules/@formatjs/intl-localematcher": { 25788 + "version": "0.2.32", 25789 + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz", 25790 + "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==", 25791 + "dependencies": { 25792 + "tslib": "^2.4.0" 25793 } 25794 }, 25795 "node_modules/next-themes": { ··· 27713 "dev": true, 27714 "peerDependencies": { 27715 "react": "^16.8.4 || ^17.0.0 || ^18.0.0" 27716 } 27717 }, 27718 "node_modules/react-is": { ··· 34336 "@types/react": { 34337 "optional": true 34338 } 34339 + } 34340 + }, 34341 + "node_modules/use-intl": { 34342 + "version": "3.0.0-rc.6", 34343 + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.0.0-rc.6.tgz", 34344 + "integrity": "sha512-vkPHZFFTrt/GnLPVn0HdCUeU+BgtitkuONa9e+XC3IeItd6RxJ28RMbAK43QI726N/CrAjds/fGjI8BF21u6Hw==", 34345 + "dependencies": { 34346 + "@formatjs/ecma402-abstract": "^1.11.4", 34347 + "intl-messageformat": "^9.3.18" 34348 + }, 34349 + "peerDependencies": { 34350 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 34351 + } 34352 + }, 34353 + "node_modules/use-intl/node_modules/@formatjs/ecma402-abstract": { 34354 + "version": "1.11.4", 34355 + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", 34356 + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", 34357 + "dependencies": { 34358 + "@formatjs/intl-localematcher": "0.2.25", 34359 + "tslib": "^2.1.0" 34360 + } 34361 + }, 34362 + "node_modules/use-intl/node_modules/@formatjs/fast-memoize": { 34363 + "version": "1.2.1", 34364 + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", 34365 + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", 34366 + "dependencies": { 34367 + "tslib": "^2.1.0" 34368 + } 34369 + }, 34370 + "node_modules/use-intl/node_modules/@formatjs/icu-messageformat-parser": { 34371 + "version": "2.1.0", 34372 + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", 34373 + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", 34374 + "dependencies": { 34375 + "@formatjs/ecma402-abstract": "1.11.4", 34376 + "@formatjs/icu-skeleton-parser": "1.3.6", 34377 + "tslib": "^2.1.0" 34378 + } 34379 + }, 34380 + "node_modules/use-intl/node_modules/@formatjs/icu-skeleton-parser": { 34381 + "version": "1.3.6", 34382 + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", 34383 + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", 34384 + "dependencies": { 34385 + "@formatjs/ecma402-abstract": "1.11.4", 34386 + "tslib": "^2.1.0" 34387 + } 34388 + }, 34389 + "node_modules/use-intl/node_modules/@formatjs/intl-localematcher": { 34390 + "version": "0.2.25", 34391 + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", 34392 + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", 34393 + "dependencies": { 34394 + "tslib": "^2.1.0" 34395 + } 34396 + }, 34397 + "node_modules/use-intl/node_modules/intl-messageformat": { 34398 + "version": "9.13.0", 34399 + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", 34400 + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", 34401 + "dependencies": { 34402 + "@formatjs/ecma402-abstract": "1.11.4", 34403 + "@formatjs/fast-memoize": "1.2.1", 34404 + "@formatjs/icu-messageformat-parser": "2.1.0", 34405 + "tslib": "^2.1.0" 34406 } 34407 }, 34408 "node_modules/use-resize-observer": {
+2 -2
package.json
··· 18 "scripts:release-post": "cross-env NODE_NO_WARNINGS=1 node scripts/release-post/index.mjs", 19 "scripts:generate-next-data": "cross-env NODE_NO_WARNINGS=1 node scripts/generate-next-data/index.mjs", 20 "preserve": "npm run scripts:generate-next-data", 21 - "serve": "cross-env NODE_NO_WARNINGS=1 next dev --turbo", 22 "prebuild": "npm run scripts:generate-next-data", 23 "build": "cross-env NODE_NO_WARNINGS=1 next build", 24 "start": "cross-env NODE_NO_WARNINGS=1 next start", ··· 60 "husky": "8.0.3", 61 "lint-staged": "14.0.1", 62 "next": "~14.0.0", 63 "next-themes": "~0.2.1", 64 "postcss": "~8.4.30", 65 "postcss-calc": "~9.0.1", ··· 68 "postcss-simple-vars": "~7.0.1", 69 "react": "^18.2.0", 70 "react-dom": "^18.2.0", 71 - "react-intl": "~6.4.7", 72 "rehype-autolink-headings": "~7.0.0", 73 "rehype-slug": "~6.0.0", 74 "remark-gfm": "~4.0.0",
··· 18 "scripts:release-post": "cross-env NODE_NO_WARNINGS=1 node scripts/release-post/index.mjs", 19 "scripts:generate-next-data": "cross-env NODE_NO_WARNINGS=1 node scripts/generate-next-data/index.mjs", 20 "preserve": "npm run scripts:generate-next-data", 21 + "serve": "cross-env NODE_NO_WARNINGS=1 next dev", 22 "prebuild": "npm run scripts:generate-next-data", 23 "build": "cross-env NODE_NO_WARNINGS=1 next build", 24 "start": "cross-env NODE_NO_WARNINGS=1 next start", ··· 60 "husky": "8.0.3", 61 "lint-staged": "14.0.1", 62 "next": "~14.0.0", 63 + "next-intl": "^3.0.0-rc.7", 64 "next-themes": "~0.2.1", 65 "postcss": "~8.4.30", 66 "postcss-calc": "~9.0.1", ··· 69 "postcss-simple-vars": "~7.0.1", 70 "react": "^18.2.0", 71 "react-dom": "^18.2.0", 72 "rehype-autolink-headings": "~7.0.0", 73 "rehype-slug": "~6.0.0", 74 "remark-gfm": "~4.0.0",
-21
pages/404.tsx
··· 1 - import type { GetStaticProps } from 'next'; 2 - import { FormattedMessage } from 'react-intl'; 3 - 4 - import Theme from '@/theme'; 5 - 6 - const NotFound = () => ( 7 - <Theme> 8 - <h2> 9 - <FormattedMessage id="pages.404.title" /> 10 - </h2> 11 - <h3> 12 - <FormattedMessage id="pages.404.description" /> 13 - </h3> 14 - </Theme> 15 - ); 16 - 17 - export default NotFound; 18 - 19 - // We require `getStaticProps` to ensure that this component is not rendered on server-side 20 - // as the router will always treat the path as /404 despite the actual URL path not being /404 21 - export const getStaticProps: GetStaticProps = () => ({ props: {} });
···
-104
pages/[...path].tsx
··· 1 - import { sep } from 'node:path'; 2 - 3 - import type { 4 - GetStaticPaths, 5 - GetStaticPathsResult, 6 - GetStaticProps, 7 - } from 'next'; 8 - 9 - import { 10 - ENABLE_STATIC_EXPORT, 11 - STATIC_ROUTES_IGNORES, 12 - DYNAMIC_ROUTES_IGNORES, 13 - DYNAMIC_ROUTES_REWRITES, 14 - DYNAMIC_GENERATED_ROUTES, 15 - } from '@/next.constants.mjs'; 16 - import { 17 - getMarkdownFile, 18 - generateStaticProps, 19 - allPaths, 20 - } from '@/next.dynamic.mjs'; 21 - import Theme from '@/theme'; 22 - import type { DynamicStaticProps } from '@/types'; 23 - 24 - type DynamicStaticPaths = { path: string[] }; 25 - 26 - // This is a small utility that allows us to quickly separate locale from the remaning pathname 27 - const getLocaleAndPathname = ([locale, ...path]: string[] = []) => [ 28 - locale, 29 - path.join('/'), 30 - ]; 31 - 32 - // This tests if the current pathname matches any expression that belongs 33 - // to the list of ignored routes and if it does we return `true` to indicate that 34 - const shouldIgnoreRoute = (pathname: string) => 35 - (pathname.length && DYNAMIC_ROUTES_IGNORES.some(e => e.test(pathname))) || 36 - false; 37 - 38 - // This tests if the current pathname matches any sort of rewrite rule 39 - // and if it does we return a the replacement expression for the pathname 40 - const getRouteWrite = (pathname: string) => 41 - (pathname.length && 42 - DYNAMIC_ROUTES_REWRITES.find(([e]) => e.test(pathname))) || 43 - []; 44 - 45 - // This maps a pathname into an actual route object that can be used 46 - const mapPathnameToRoute = (pathname: string) => ({ 47 - // we use a platform-specific separator to split the pathname 48 - // since we're using filepaths here and not URL paths 49 - params: { path: pathname.split(sep) }, 50 - }); 51 - 52 - // This method receives the props from `getStaticProps` and renders/builds the Markdown 53 - // content on demand by loading the file on the server-side and serializing the Markdown/MDX content 54 - export const getStaticProps: GetStaticProps< 55 - DynamicStaticProps, 56 - DynamicStaticPaths 57 - > = async ({ params = { path: [] } }) => { 58 - const [locale, pathname] = getLocaleAndPathname(params.path); 59 - 60 - // Retrieves and rewriting rule if the pathname matches any rule 61 - const [, rewriteRule] = getRouteWrite(pathname); 62 - 63 - // We retrieve the source of the Markdown file by doing an educated guess 64 - // of what possible files could be the source of the page, since the extension 65 - // context is lost from `getStaticProps` as a limitation of Next.js itself 66 - const { source, filename } = getMarkdownFile( 67 - locale, 68 - rewriteRule ? rewriteRule(pathname) : pathname 69 - ); 70 - 71 - // This parses the actual Markdown content and returns a full set of props 72 - // to be passed to the base page (`DynamicPage`) which will render the Markdown 73 - const staticProps = await generateStaticProps(source, filename); 74 - 75 - // This checks if either we already determined the route does not exist or if we should ignore 76 - // this route because it's on our ignored list 77 - staticProps.notFound = staticProps.notFound || shouldIgnoreRoute(pathname); 78 - 79 - // We add the extra `params` to the props as they're used within the `DynamicPage` 80 - return staticProps; 81 - }; 82 - 83 - // This method is used to retrieve all native statically supported pages (SCR) that 84 - // we want to provide during build-time + allow fallback for dynamic pages during (ISR) 85 - export const getStaticPaths: GetStaticPaths<DynamicStaticPaths> = async () => { 86 - let paths: GetStaticPathsResult<DynamicStaticPaths>['paths'] = []; 87 - 88 - if (ENABLE_STATIC_EXPORT) { 89 - // Retrieves all the dynamic generated paths 90 - const dynamicRoutes = DYNAMIC_GENERATED_ROUTES().map(mapPathnameToRoute); 91 - 92 - // Retrieves all the static paths (from next.dynamic.mjs) 93 - const staticPaths = [...allPaths.values()] 94 - .flat() 95 - .filter(route => STATIC_ROUTES_IGNORES.every(e => !e(route))) 96 - .map(route => mapPathnameToRoute(route.routeWithLocale)); 97 - 98 - paths = staticPaths.concat(dynamicRoutes); 99 - } 100 - 101 - return { paths: paths.sort(), fallback: 'blocking' }; 102 - }; 103 - 104 - export default Theme;
···
-44
pages/_app.tsx
··· 1 - import { Analytics } from '@vercel/analytics/react'; 2 - import type { AppProps } from 'next/app'; 3 - import { Source_Sans_3 } from 'next/font/google'; 4 - 5 - import { VERCEL_ENV } from '@/next.constants.mjs'; 6 - import { BlogDataProvider } from '@/providers/blogDataProvider'; 7 - import { LocaleProvider } from '@/providers/localeProvider'; 8 - import { NodeReleasesProvider } from '@/providers/nodeReleasesProvider'; 9 - import { SiteProvider } from '@/providers/siteProvider'; 10 - import { ThemeProvider } from '@/providers/themeProvider'; 11 - 12 - import '@/styles/old/index.css'; 13 - 14 - const sourceSans = Source_Sans_3({ 15 - weight: ['400', '600'], 16 - display: 'fallback', 17 - subsets: ['latin'], 18 - }); 19 - 20 - const App = ({ Component, pageProps }: AppProps) => ( 21 - <ThemeProvider> 22 - <LocaleProvider> 23 - <SiteProvider> 24 - <NodeReleasesProvider> 25 - <BlogDataProvider> 26 - <Component {...pageProps} /> 27 - </BlogDataProvider> 28 - </NodeReleasesProvider> 29 - </SiteProvider> 30 - </LocaleProvider> 31 - 32 - {VERCEL_ENV && <Analytics />} 33 - 34 - <style jsx global> 35 - {` 36 - body { 37 - font-family: ${sourceSans.style.fontFamily}; 38 - } 39 - `} 40 - </style> 41 - </ThemeProvider> 42 - ); 43 - 44 - export default App;
···
-16
pages/_document.tsx
··· 1 - import { Html, Head, Main, NextScript } from 'next/document'; 2 - 3 - const Document = () => ( 4 - <Html> 5 - <Head /> 6 - <body> 7 - <Main /> 8 - 9 - <NextScript /> 10 - 11 - <a rel="me" href="https://social.lfx.dev/@nodejs" /> 12 - </body> 13 - </Html> 14 - ); 15 - 16 - export default Document;
···
+1 -1
pages/en/about/index.md
··· 61 which allows you to share sockets between processes to enable load balancing 62 over your cores. 63 64 - [blocking vs. non-blocking]: /docs/guides/blocking-vs-non-blocking 65 [`child_process.fork()`]: /api/child_process/ 66 [`cluster`]: https://nodejs.org/api/cluster.html 67 [event machine]: https://github.com/eventmachine/eventmachine
··· 61 which allows you to share sockets between processes to enable load balancing 62 over your cores. 63 64 + [blocking vs. non-blocking]: /learn/asynchronous-work/overview-of-blocking-vs-non-blocking 65 [`child_process.fork()`]: /api/child_process/ 66 [`cluster`]: https://nodejs.org/api/cluster.html 67 [event machine]: https://github.com/eventmachine/eventmachine
+1
pages/en/about/security-reporting.mdx
··· 80 style={{ display: 'inline-flex' }} 81 > 82 <img 83 src="https://bestpractices.coreinfrastructure.org/projects/29/badge" 84 style={{ display: 'inline' }} 85 />
··· 80 style={{ display: 'inline-flex' }} 81 > 82 <img 83 + alt="OpenSSF Badge" 84 src="https://bestpractices.coreinfrastructure.org/projects/29/badge" 85 style={{ display: 'inline' }} 86 />
+1 -1
pages/en/blog/advisory-board/index.md
··· 1 --- 2 title: Advisory Board 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Advisory Board 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/announcements/index.md
··· 1 --- 2 title: Announcements 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Announcements 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/community/index.md
··· 1 --- 2 title: Community 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Community 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/feature/index.md
··· 1 --- 2 title: Features 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Features 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/index.md
··· 1 --- 2 title: News 3 - layout: blog-index.hbs 4 ---
··· 1 --- 2 title: News 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/module/index.md
··· 1 --- 2 title: Modules 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Modules 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/npm/index.md
··· 1 --- 2 title: NPM 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: NPM 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/pagination.md
··· 1 --- 2 title: News from 3 - layout: blog-index.hbs 4 author: The Node.js Project 5 ---
··· 1 --- 2 title: News from 3 + layout: blog-categpry.hbs 4 author: The Node.js Project 5 ---
+1 -1
pages/en/blog/release/index.md
··· 1 --- 2 title: Releases 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Releases 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/uncategorized/index.md
··· 1 --- 2 title: Uncategorized 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Uncategorized 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/video/index.md
··· 1 --- 2 title: Videos 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Videos 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/vulnerability/index.md
··· 1 --- 2 title: Vulnerabilities 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Vulnerabilities 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/blog/weekly-updates/index.md
··· 1 --- 2 title: Weekly Updates 3 - layout: category-index.hbs 4 ---
··· 1 --- 2 title: Weekly Updates 3 + layout: blog-categpry.hbs 4 ---
+1 -1
pages/en/download/current.md
··· 1 --- 2 - layout: download-current.hbs 3 title: Download 4 download: Download 5 downloads:
··· 1 --- 2 + layout: download.hbs 3 title: Download 4 download: Download 5 downloads:
-17
providers/blogDataProvider.tsx
··· 1 - import { createContext } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import blogData from '@/public/blog-posts-data.json'; 5 - import type { BlogData } from '@/types'; 6 - 7 - export const BlogDataContext = createContext<BlogData>({ 8 - posts: [], 9 - pagination: [], 10 - categories: [], 11 - }); 12 - 13 - export const BlogDataProvider: FC<PropsWithChildren> = ({ children }) => ( 14 - <BlogDataContext.Provider value={blogData}> 15 - {children} 16 - </BlogDataContext.Provider> 17 - );
···
-68
providers/layoutProvider.tsx
··· 1 - import { createContext, useMemo } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import AboutLayout from '@/layouts/AboutLayout'; 5 - import BlogIndexLayout from '@/layouts/BlogIndexLayout'; 6 - import BlogPostLayout from '@/layouts/BlogPostLayout'; 7 - import CategoryIndexLayout from '@/layouts/CategoryIndexLayout'; 8 - import ContributeLayout from '@/layouts/ContributeLayout'; 9 - import DefaultLayout from '@/layouts/DefaultLayout'; 10 - import DocsLayout from '@/layouts/DocsLayout'; 11 - import DownloadCurrentLayout from '@/layouts/DownloadCurrentLayout'; 12 - import DownloadLayout from '@/layouts/DownloadLayout'; 13 - import IndexLayout from '@/layouts/IndexLayout'; 14 - import LearnLayout from '@/layouts/LearnLayout'; 15 - import type { LegacyFrontMatter, LegacyLayouts } from '@/types'; 16 - 17 - type LayoutContextProps = { 18 - frontMatter: LegacyFrontMatter; 19 - }; 20 - 21 - export const LayoutContext = createContext<LayoutContextProps>({ 22 - frontMatter: {}, 23 - }); 24 - 25 - const getLegacyLayout = (layout: LegacyLayouts) => { 26 - switch (layout) { 27 - case 'about.hbs': 28 - return AboutLayout; 29 - case 'learn.hbs': 30 - return LearnLayout; 31 - case 'blog-index.hbs': 32 - return BlogIndexLayout; 33 - case 'blog-post.hbs': 34 - return BlogPostLayout; 35 - case 'category-index.hbs': 36 - return CategoryIndexLayout; 37 - case 'contribute.hbs': 38 - return ContributeLayout; 39 - case 'docs.hbs': 40 - return DocsLayout; 41 - case 'download.hbs': 42 - return DownloadLayout; 43 - case 'download-current.hbs': 44 - return DownloadCurrentLayout; 45 - case 'index.hbs': 46 - return IndexLayout; 47 - default: 48 - return DefaultLayout; 49 - } 50 - }; 51 - 52 - type LayoutProviderProps = PropsWithChildren<LayoutContextProps>; 53 - 54 - export const LayoutProvider: FC<LayoutProviderProps> = ({ 55 - children, 56 - frontMatter, 57 - }) => { 58 - const LayoutComponent = useMemo( 59 - () => getLegacyLayout(frontMatter.layout || 'page.hbs'), 60 - [frontMatter.layout] 61 - ); 62 - 63 - return ( 64 - <LayoutContext.Provider value={{ frontMatter }}> 65 - <LayoutComponent>{children}</LayoutComponent> 66 - </LayoutContext.Provider> 67 - ); 68 - };
···
+6 -41
providers/localeProvider.tsx
··· 1 - import { createContext, useMemo } from 'react'; 2 import type { FC, PropsWithChildren } from 'react'; 3 - import { IntlProvider } from 'react-intl'; 4 - 5 - import { useRouter } from '@/hooks/useRouter'; 6 - import { 7 - defaultLocale, 8 - availableLocales, 9 - getCurrentLocale, 10 - getCurrentTranslations, 11 - } from '@/next.locales.mjs'; 12 - import type { LocaleContext as LocaleContextType } from '@/types'; 13 - 14 - // Initialises the Context with the default Localisation Data 15 - export const LocaleContext = createContext<LocaleContextType>({ 16 - currentLocale: defaultLocale, 17 - availableLocales: availableLocales, 18 - localeMessages: getCurrentTranslations(defaultLocale.code), 19 - defaultLocale: defaultLocale, 20 - }); 21 22 export const LocaleProvider: FC<PropsWithChildren> = ({ children }) => { 23 - const { query, asPath } = useRouter(); 24 - 25 - const localeData = useMemo(() => { 26 - // Retrieves the current locale information from the route and query 27 - const currentLocale = getCurrentLocale(asPath, query); 28 - 29 - return { 30 - currentLocale: currentLocale, 31 - availableLocales: availableLocales, 32 - defaultLocale: defaultLocale, 33 - localeMessages: getCurrentTranslations(currentLocale.code), 34 - }; 35 - }, [asPath, query]); 36 37 return ( 38 - <LocaleContext.Provider value={localeData}> 39 - <IntlProvider 40 - locale={localeData.currentLocale.hrefLang} 41 - messages={localeData.localeMessages} 42 - onError={() => null} 43 - > 44 - {children} 45 - </IntlProvider> 46 - </LocaleContext.Provider> 47 ); 48 };
··· 1 + import { useMessages, NextIntlClientProvider, useTimeZone } from 'next-intl'; 2 import type { FC, PropsWithChildren } from 'react'; 3 4 export const LocaleProvider: FC<PropsWithChildren> = ({ children }) => { 5 + const messages = useMessages(); 6 + const timezone = useTimeZone(); 7 8 return ( 9 + <NextIntlClientProvider messages={messages} timeZone={timezone}> 10 + {children} 11 + </NextIntlClientProvider> 12 ); 13 };
+29
providers/matterProvider.tsx
···
··· 1 + 'use client'; 2 + 3 + import type { Heading } from '@vcarl/remark-headings'; 4 + import { createContext } from 'react'; 5 + import type { FC, PropsWithChildren } from 'react'; 6 + 7 + import type { LegacyFrontMatter } from '@/types'; 8 + 9 + type MatterContext = { matter: LegacyFrontMatter; headings: Heading[] }; 10 + 11 + export const MatterContext = createContext<MatterContext>({ 12 + matter: {}, 13 + headings: [], 14 + }); 15 + 16 + type MatterProviderProps = PropsWithChildren<{ 17 + matter: LegacyFrontMatter; 18 + headings: Heading[]; 19 + }>; 20 + 21 + export const MatterProvider: FC<MatterProviderProps> = ({ 22 + matter, 23 + headings, 24 + children, 25 + }) => ( 26 + <MatterContext.Provider value={{ matter, headings }}> 27 + {children} 28 + </MatterContext.Provider> 29 + );
-21
providers/mdxProvider.tsx
··· 1 - import type { MDXComponents } from 'mdx/types'; 2 - import { useMemo } from 'react'; 3 - import type { FC } from 'react'; 4 - 5 - import { runMDX } from '@/next.mdx.compiler.mjs'; 6 - import { htmlComponents, mdxComponents } from '@/next.mdx.use.mjs'; 7 - 8 - // Combine all MDX Components to be used 9 - const combinedComponents: MDXComponents = { 10 - ...htmlComponents, 11 - ...mdxComponents, 12 - }; 13 - 14 - export const MDXProvider: FC<{ content: string }> = ({ content }) => { 15 - // Parses the MDX Function and eval's it into a React Component 16 - // We don't need asynchronous runtime here as we want to render the MDX 17 - // as soon as it is available and be able to make initial renders 18 - const MDXContent = useMemo(() => runMDX(content), [content]); 19 - 20 - return <MDXContent components={combinedComponents} />; 21 - };
···
-45
providers/nodeReleasesProvider.tsx
··· 1 - import { createContext, useMemo } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import { releaseData } from '@/next.json.mjs'; 5 - import type { NodeReleaseSource, NodeRelease } from '@/types'; 6 - import { getNodeReleaseStatus } from '@/util/nodeRelease'; 7 - 8 - export const NodeReleasesContext = createContext<NodeRelease[]>([]); 9 - 10 - export const NodeReleasesProvider: FC<PropsWithChildren> = ({ children }) => { 11 - const releases = useMemo(() => { 12 - const now = new Date(); 13 - 14 - return releaseData.map((raw: NodeReleaseSource) => { 15 - const support = { 16 - currentStart: raw.currentStart, 17 - ltsStart: raw.ltsStart, 18 - maintenanceStart: raw.maintenanceStart, 19 - endOfLife: raw.endOfLife, 20 - }; 21 - 22 - const status = getNodeReleaseStatus(now, support); 23 - 24 - return { 25 - ...support, 26 - major: raw.major, 27 - version: raw.version, 28 - versionWithPrefix: `v${raw.version}`, 29 - codename: raw.codename || '', 30 - isLts: status === 'Active LTS' || status === 'Maintenance LTS', 31 - status: status, 32 - npm: raw.npm || '', 33 - v8: raw.v8 || '', 34 - releaseDate: raw.releaseDate || '', 35 - modules: raw.modules || '', 36 - }; 37 - }); 38 - }, []); 39 - 40 - return ( 41 - <NodeReleasesContext.Provider value={releases}> 42 - {children} 43 - </NodeReleasesContext.Provider> 44 - ); 45 - };
···
+1 -3
providers/notificationProvider.tsx
··· 15 duration: number; 16 } | null; 17 18 - type NotificationProps = { 19 - viewportClassName?: string; 20 - }; 21 22 const NotificationContext = createContext<NotificationContextType>(null); 23
··· 15 duration: number; 16 } | null; 17 18 + type NotificationProps = { viewportClassName?: string }; 19 20 const NotificationContext = createContext<NotificationContextType>(null); 21
-13
providers/siteProvider.tsx
··· 1 - import { createContext } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import { siteConfig } from '@/next.json.mjs'; 5 - import type { SiteConfig } from '@/types'; 6 - 7 - const config = siteConfig as SiteConfig; 8 - 9 - export const SiteContext = createContext<SiteConfig>(config); 10 - 11 - export const SiteProvider: FC<PropsWithChildren> = ({ children }) => ( 12 - <SiteContext.Provider value={config}>{children}</SiteContext.Provider> 13 - );
···
+6 -4
providers/themeProvider.tsx
··· 1 import { ThemeProvider as NextThemeProvider } from 'next-themes'; 2 import type { FC, PropsWithChildren } from 'react'; 3 4 - import { THEME_LOCAL_STORAGE_KEY } from '@/next.constants.mjs'; 5 6 export const ThemeProvider: FC<PropsWithChildren> = ({ children }) => ( 7 <NextThemeProvider 8 attribute="data-theme" 9 - storageKey={THEME_LOCAL_STORAGE_KEY} 10 - enableSystem={true} 11 - enableColorScheme={true} 12 > 13 {children} 14 </NextThemeProvider>
··· 1 + 'use client'; 2 + 3 import { ThemeProvider as NextThemeProvider } from 'next-themes'; 4 import type { FC, PropsWithChildren } from 'react'; 5 6 + import { THEME_STORAGE_KEY } from '@/next.constants.mjs'; 7 8 export const ThemeProvider: FC<PropsWithChildren> = ({ children }) => ( 9 <NextThemeProvider 10 attribute="data-theme" 11 + defaultTheme="dark" 12 + storageKey={THEME_STORAGE_KEY} 13 + enableSystem={false} 14 > 15 {children} 16 </NextThemeProvider>
-33
providers/withNodeRelease.tsx
··· 1 - import { useMemo } from 'react'; 2 - import type { FC } from 'react'; 3 - 4 - import { useNodeReleases } from '@/hooks/useNodeReleases'; 5 - import type { NodeRelease, NodeReleaseStatus } from '@/types'; 6 - import { isNodeRelease } from '@/util/nodeRelease'; 7 - 8 - type WithNodeReleaseProps = { 9 - status: NodeReleaseStatus[] | NodeReleaseStatus; 10 - children: FC<{ release: NodeRelease }>; 11 - }; 12 - 13 - export const WithNodeRelease: FC<WithNodeReleaseProps> = ({ 14 - status, 15 - children: Component, 16 - }) => { 17 - const { getReleaseByStatus } = useNodeReleases(); 18 - 19 - const [release] = useMemo( 20 - () => 21 - [status] 22 - .flat() 23 - .map(s => getReleaseByStatus(s)) 24 - .filter(s => !!s), 25 - [status, getReleaseByStatus] 26 - ); 27 - 28 - if (release !== undefined && isNodeRelease(release)) { 29 - return <Component release={release} />; 30 - } 31 - 32 - return null; 33 - };
···
+2 -7
site.json
··· 1 { 2 "title": "Node.js", 3 "description": "Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.", 4 - "featuredImage": "/static/images/logo-hexagon-card.png", 5 "favicon": "/static/images/favicons/favicon.png", 6 "accentColor": "#333", 7 - "og": { 8 - "imgType": "image/png", 9 - "imgWidth": "224", 10 - "imgHeight": "256" 11 - }, 12 "twitter": { 13 "username": "@nodejs", 14 "card": "summary", ··· 36 "startDate": "2023-10-13T13:30:00.000Z", 37 "endDate": "2023-10-19T13:30:00.000Z", 38 "text": "Security releases now available", 39 - "link": "https://nodejs.org/en/blog/vulnerability/october-2023-security-releases/" 40 } 41 }, 42 "footerLinks": [
··· 1 { 2 "title": "Node.js", 3 "description": "Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.", 4 "favicon": "/static/images/favicons/favicon.png", 5 "accentColor": "#333", 6 "twitter": { 7 "username": "@nodejs", 8 "card": "summary", ··· 30 "startDate": "2023-10-13T13:30:00.000Z", 31 "endDate": "2023-10-19T13:30:00.000Z", 32 "text": "Security releases now available", 33 + "link": "https://nodejs.org/en/blog/vulnerability/october-2023-security-releases/", 34 + "html": null 35 } 36 }, 37 "footerLinks": [
-4
styles/old/index.css
··· 19 @import 'page-modules/anchorLinks'; 20 @import 'page-modules/prev-next-navigation'; 21 22 - * { 23 - transition: background-color $dark-transition-time ease; 24 - } 25 - 26 article a { 27 word-break: break-word; 28 }
··· 19 @import 'page-modules/anchorLinks'; 20 @import 'page-modules/prev-next-navigation'; 21 22 article a { 23 word-break: break-word; 24 }
-1
styles/old/page-modules/header.css
··· 36 37 .header-background-fill { 38 fill: $node-gray; 39 - transition: fill $dark-transition-time ease; 40 } 41 42 .switchers {
··· 36 37 .header-background-fill { 38 fill: $node-gray; 39 } 40 41 .switchers {
-34
styles/old/page-modules/scrollToTop.css
··· 5 scroll-behavior: auto; 6 } 7 } 8 - 9 - #scroll-to-top { 10 - animation: button-fade 0.5s 1s 1 forwards; 11 - background-color: $node-green; 12 - border: 1px solid $node-green; 13 - border-radius: 4px; 14 - bottom: 10%; 15 - color: $white; 16 - display: none; 17 - font-size: 1rem; 18 - margin-right: 15px; 19 - min-width: 20px; 20 - opacity: 0; 21 - padding: 0 5px 1px; 22 - position: fixed; 23 - right: 0; 24 - text-align: center; 25 - 26 - span { 27 - display: flex; 28 - flex-direction: row; 29 - padding: 5px; 30 - } 31 - 32 - @keyframes button-fade { 33 - 0% { 34 - opacity: 0; 35 - } 36 - 37 - 100% { 38 - opacity: 1; 39 - } 40 - } 41 - }
··· 5 scroll-behavior: auto; 6 } 7 }
-1
styles/old/variables.css
··· 17 $black-alpha-12: #00000012; 18 $black-alpha-0f: #0000000f; 19 20 - $dark-transition-time: 0.24s; 21 $dark-black: #090c15; 22 $dark-black2: #233056; 23 $dark-black3: #23305671;
··· 17 $black-alpha-12: #00000012; 18 $black-alpha-0f: #0000000f; 19 20 $dark-black: #090c15; 21 $dark-black2: #233056; 22 $dark-black3: #23305671;
-38
theme.tsx
··· 1 - import { memo } from 'react'; 2 - import type { FC, PropsWithChildren } from 'react'; 3 - 4 - import HtmlHead from './components/HtmlHead'; 5 - import { LayoutProvider } from './providers/layoutProvider'; 6 - import { MDXProvider } from './providers/mdxProvider'; 7 - import type { DynamicStaticProps } from './types'; 8 - 9 - type ThemeProps = PropsWithChildren<DynamicStaticProps>; 10 - 11 - // This is the Dynamic Page Theme Component that supports Dynamic MDX Page Rendering 12 - // With the dynamic MDXProvider Component. 13 - // @TODO: When migrating to the new Layout approach, each Layout should manually invoke the MDXProvider 14 - // @TODO: And use the MDX Content, Frontmatter, Headings and Children as seemed fit. 15 - const Theme: FC<ThemeProps> = ({ content, frontmatter = {}, children }) => ( 16 - <> 17 - <HtmlHead frontMatter={frontmatter} /> 18 - <LayoutProvider frontMatter={frontmatter}> 19 - {content && <MDXProvider content={content} />} 20 - 21 - {children} 22 - </LayoutProvider> 23 - </> 24 - ); 25 - 26 - export default memo( 27 - Theme, 28 - // The Theme Component is supposed to be used only for static Children or MDXRemote content 29 - // that comes from `getStaticProps`. This means that the `props` should never change. 30 - // At least the `props.content` should never. We should not calculate based on `children` 31 - // As this component should never have a dynamic children 32 - ( 33 - { content: pContent, frontmatter: pFrontmatter }, 34 - { content: nContent, frontmatter: nFrontmatter } 35 - ) => 36 - JSON.stringify([pContent, pFrontmatter]) === 37 - JSON.stringify([nContent, nFrontmatter]) 38 - );
···
-10
types/api.ts
··· 1 - export interface ApiChange { 2 - version: string | string[]; 3 - 'pr-url': string; 4 - description: string; 5 - } 6 - 7 - export interface ApiUpdate { 8 - type: 'added' | 'removed' | 'deprecated' | 'introduced_in' | 'napiVersion'; 9 - version: string[]; 10 - }
···
-6
types/dropdown.ts
··· 1 - export interface DropdownItem { 2 - title: string; 3 - label: string; 4 - onClick: () => void; 5 - active?: boolean; 6 - }
···
-9
types/dynamic.ts
··· 1 - import type { Heading } from '@vcarl/remark-headings'; 2 - 3 - import type { LegacyFrontMatter } from './frontmatter'; 4 - 5 - export interface DynamicStaticProps { 6 - content?: string; 7 - frontmatter?: LegacyFrontMatter; 8 - headings?: Heading[]; 9 - }
···
+5 -11
types/frontmatter.ts
··· 1 import type { LegacyLayouts } from './layouts'; 2 3 - // @TODO: This is the legacy frontmatter configuration going to be replaced in the future with the `ndoejs/nodejs.dev` one 4 - // this is going to be done via a script that replaces layouts 5 - // Note.: The current legacy pages have other frontmatter entries but they're irrelevant 6 - export interface LegacyFrontMatter { 7 layout?: LegacyLayouts; 8 title?: string; 9 - robots?: string; 10 labels?: Record<string, string>; 11 } 12 13 - // @TODO: This is the legacy frontmatter configuration going to be replaced in the future with the `ndoejs/nodejs.dev` one 14 - // this is going to be done via a script that replaces layouts 15 export interface LegacyBlogFrontMatter extends LegacyFrontMatter { 16 author: string; 17 date: string; 18 } 19 20 export interface LegacyDownloadsFrontMatter extends LegacyFrontMatter { 21 downloads: Record<string, string>; 22 additional: Record<string, string>; 23 } 24 - 25 - export interface LegacyDownloadsReleasesFrontMatter extends LegacyFrontMatter { 26 - modules: string; 27 - }
··· 1 import type { LegacyLayouts } from './layouts'; 2 3 + // @TODO: Extra data from Frontmatter should not be a thing in the future 4 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 + export interface LegacyFrontMatter extends Record<string, any> { 6 layout?: LegacyLayouts; 7 title?: string; 8 labels?: Record<string, string>; 9 } 10 11 + // @TODO: Extra data from Frontmatter should not be a thing in the future 12 export interface LegacyBlogFrontMatter extends LegacyFrontMatter { 13 author: string; 14 date: string; 15 } 16 17 + // @TODO: Extra data from Frontmatter should not be a thing in the future 18 export interface LegacyDownloadsFrontMatter extends LegacyFrontMatter { 19 downloads: Record<string, string>; 20 additional: Record<string, string>; 21 }
-7
types/i18n.ts
··· 7 hrefLang: string; 8 enabled: boolean; 9 } 10 - 11 - export interface LocaleContext { 12 - localeMessages: Record<string, string>; 13 - availableLocales: LocaleConfig[]; 14 - currentLocale: LocaleConfig; 15 - defaultLocale: LocaleConfig; 16 - }
··· 7 hrefLang: string; 8 enabled: boolean; 9 }
+2 -4
types/index.ts
··· 1 - export * from './api'; 2 export * from './blog'; 3 export * from './config'; 4 - export * from './dropdown'; 5 export * from './features'; 6 export * from './frontmatter'; 7 export * from './i18n'; 8 export * from './layouts'; 9 export * from './navigation'; 10 - export * from './prevNextLink'; 11 export * from './releases'; 12 - export * from './dynamic';
··· 1 export * from './blog'; 2 export * from './config'; 3 export * from './features'; 4 export * from './frontmatter'; 5 export * from './i18n'; 6 export * from './layouts'; 7 export * from './navigation'; 8 export * from './releases'; 9 + export * from './redirects'; 10 + export * from './server';
+1 -3
types/layouts.ts
··· 2 export type LegacyLayouts = 3 | 'about.hbs' 4 | 'learn.hbs' 5 - | 'blog-index.hbs' 6 | 'blog-post.hbs' 7 - | 'category-index.hbs' 8 | 'contribute.hbs' 9 | 'index.hbs' 10 | 'docs.hbs' 11 | 'download.hbs' 12 - | 'download-current.hbs' 13 | 'page.hbs';
··· 2 export type LegacyLayouts = 3 | 'about.hbs' 4 | 'learn.hbs' 5 + | 'blog-categpry.hbs' 6 | 'blog-post.hbs' 7 | 'contribute.hbs' 8 | 'index.hbs' 9 | 'docs.hbs' 10 | 'download.hbs' 11 | 'page.hbs';
-3
types/prevNextLink.ts
··· 1 - export interface LinkInfo { 2 - slug: string; 3 - }
···
+4
types/redirects.ts
···
··· 1 + export interface Redirect { 2 + source: string; 3 + destination: string; 4 + }
-5
types/releases.ts
··· 35 isLts: boolean; 36 status: NodeReleaseStatus; 37 } 38 - 39 - export type NodeReleaseSupport = Pick< 40 - NodeRelease, 41 - 'currentStart' | 'ltsStart' | 'maintenanceStart' | 'endOfLife' 42 - >;
··· 35 isLts: boolean; 36 status: NodeReleaseStatus; 37 }
+9
types/server.ts
···
··· 1 + import type { Heading } from '@vcarl/remark-headings'; 2 + 3 + import type { LegacyFrontMatter } from './frontmatter'; 4 + 5 + export interface ClientSharedServerContext { 6 + frontmatter: LegacyFrontMatter; 7 + headings: Heading[]; 8 + pathname: string; 9 + }
util/__tests__/detectOS.test.ts util/__tests__/detectOS.test.mjs
+1 -1
util/__tests__/downloadUrlByOS.test.ts util/__tests__/downloadUrlByOS.test.mjs
··· 32 it('returns the default download URL for other operating systems', () => { 33 const os = 'OTHER'; 34 const bitness = 86; 35 - const expectedUrl = 'https://nodejs.org/dist/v18.16.0/node-v18.16.0.tar.gz'; 36 37 expect(downloadUrlByOS(version, os, bitness)).toBe(expectedUrl); 38 });
··· 32 it('returns the default download URL for other operating systems', () => { 33 const os = 'OTHER'; 34 const bitness = 86; 35 + const expectedUrl = 'https://nodejs.org/dist/v18.16.0/node-v18.16.0.tar.xz'; 36 37 expect(downloadUrlByOS(version, os, bitness)).toBe(expectedUrl); 38 });
util/__tests__/string.mjs util/__tests__/stringUtils.mjs
-1
util/isAbsoluteUrl.ts
··· 1 - export const isAbsoluteUrl = (link: string) => /^https?:\/\//.test(link);
···
-8
util/linkWithLocale.ts
··· 1 - import type { UrlObject } from 'url'; 2 - 3 - export const linkWithLocale = (locale: string) => { 4 - return (path: string | UrlObject) => { 5 - path = path.toString(); 6 - return path === '/' ? `/${locale}` : `/${locale}${path}`; 7 - }; 8 - };
···
-39
util/nodeRelease.ts
··· 1 - import type { 2 - NodeRelease, 3 - NodeReleaseStatus, 4 - NodeReleaseSupport, 5 - } from '@/types/releases'; 6 - 7 - export const isNodeRelease = (release: NodeRelease) => 8 - typeof release === 'object' && release?.version; 9 - 10 - export const getNodeReleaseStatus = ( 11 - now: Date, 12 - support: NodeReleaseSupport 13 - ): NodeReleaseStatus => { 14 - if (support.endOfLife) { 15 - if (now > new Date(support.endOfLife)) { 16 - return 'End-of-life'; 17 - } 18 - } 19 - 20 - if (support.maintenanceStart) { 21 - if (now > new Date(support.maintenanceStart)) { 22 - return 'Maintenance LTS'; 23 - } 24 - } 25 - 26 - if (support.ltsStart) { 27 - if (now > new Date(support.ltsStart)) { 28 - return 'Active LTS'; 29 - } 30 - } 31 - 32 - if (support.currentStart) { 33 - if (now >= new Date(support.currentStart)) { 34 - return 'Current'; 35 - } 36 - } 37 - 38 - return 'Pending'; 39 - };
···
-2
util/parseApiDocsVersion.ts
··· 1 - export const parseApiDocsVersion = (version: string | string[]): string => 2 - typeof version === 'string' ? version : version.join(', ');
···