From 754239229217faf09c818c33b72b9a11feb379eb Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Wed, 24 Dec 2025 15:52:29 -0500 Subject: [PATCH 1/3] chore(bundle): don't send translations to client --- apps/site/app/[locale]/[...path]/page.tsx | 2 +- .../site/app/[locale]/blog/[...path]/page.tsx | 2 +- .../download/archive/[version]/page.tsx | 2 +- apps/site/app/[locale]/feed/[feed]/route.ts | 2 +- apps/site/app/[locale]/layout.tsx | 5 +-- .../next-data/og/[category]/[title]/route.tsx | 2 +- apps/site/app/[locale]/page.tsx | 3 +- apps/site/app/sitemap.ts | 3 +- apps/site/components/withDownloadSection.tsx | 2 +- apps/site/components/withMetaBar.tsx | 2 +- apps/site/components/withNavBar.tsx | 2 +- apps/site/i18n.tsx | 8 ++--- apps/site/middleware.ts | 3 +- apps/site/navigation.mjs | 3 +- .../next-data/generators/downloadSnippets.mjs | 2 +- apps/site/next.dynamic.mjs | 2 +- apps/site/next.dynamic.page.mjs | 4 +-- apps/site/next.locales.mjs | 36 ------------------- apps/site/next.rewrites.mjs | 3 +- apps/site/tests/e2e/general-behavior.spec.ts | 10 +++--- docs/technologies.md | 9 ----- packages/i18n/src/index.mjs | 28 +++------------ 22 files changed, 36 insertions(+), 99 deletions(-) delete mode 100644 apps/site/next.locales.mjs diff --git a/apps/site/app/[locale]/[...path]/page.tsx b/apps/site/app/[locale]/[...path]/page.tsx index 59e62f0ad8f09..aecb38f3da63f 100644 --- a/apps/site/app/[locale]/[...path]/page.tsx +++ b/apps/site/app/[locale]/[...path]/page.tsx @@ -7,13 +7,13 @@ * dynamic params, which will lead on static export errors and other sort of issues. */ +import { availableLocaleCodes, defaultLocale } from '@node-core/website-i18n'; import { notFound } from 'next/navigation'; import { ENABLE_STATIC_EXPORT } from '#site/next.constants.mjs'; import { ENABLE_STATIC_EXPORT_LOCALE } from '#site/next.constants.mjs'; import { dynamicRouter } from '#site/next.dynamic.mjs'; import * as basePage from '#site/next.dynamic.page.mjs'; -import { availableLocaleCodes, defaultLocale } from '#site/next.locales.mjs'; import type { DynamicParams } from '#site/types'; import type { FC } from 'react'; diff --git a/apps/site/app/[locale]/blog/[...path]/page.tsx b/apps/site/app/[locale]/blog/[...path]/page.tsx index 24b6c820183f5..2a11e65becc46 100644 --- a/apps/site/app/[locale]/blog/[...path]/page.tsx +++ b/apps/site/app/[locale]/blog/[...path]/page.tsx @@ -1,9 +1,9 @@ +import { defaultLocale } from '@node-core/website-i18n'; import { notFound } from 'next/navigation'; import { ENABLE_STATIC_EXPORT } from '#site/next.constants.mjs'; import { BLOG_DYNAMIC_ROUTES } from '#site/next.dynamic.constants.mjs'; import * as basePage from '#site/next.dynamic.page.mjs'; -import { defaultLocale } from '#site/next.locales.mjs'; import type { DynamicParams } from '#site/types'; import type { FC } from 'react'; diff --git a/apps/site/app/[locale]/download/archive/[version]/page.tsx b/apps/site/app/[locale]/download/archive/[version]/page.tsx index 5b6f358317cfd..1cc23837ccdf1 100644 --- a/apps/site/app/[locale]/download/archive/[version]/page.tsx +++ b/apps/site/app/[locale]/download/archive/[version]/page.tsx @@ -1,10 +1,10 @@ +import { defaultLocale } from '@node-core/website-i18n'; import { notFound, redirect } from 'next/navigation'; import provideReleaseData from '#site/next-data/providers/releaseData'; import provideReleaseVersions from '#site/next-data/providers/releaseVersions'; import { ENABLE_STATIC_EXPORT } from '#site/next.constants.mjs'; import * as basePage from '#site/next.dynamic.page.mjs'; -import { defaultLocale } from '#site/next.locales.mjs'; import type { DynamicParams } from '#site/types'; import type { FC } from 'react'; diff --git a/apps/site/app/[locale]/feed/[feed]/route.ts b/apps/site/app/[locale]/feed/[feed]/route.ts index 9a101f55c3aa6..c70c0b63f9b3d 100644 --- a/apps/site/app/[locale]/feed/[feed]/route.ts +++ b/apps/site/app/[locale]/feed/[feed]/route.ts @@ -1,7 +1,7 @@ +import { defaultLocale } from '@node-core/website-i18n'; import { NextResponse } from 'next/server'; import { siteConfig } from '#site/next.json.mjs'; -import { defaultLocale } from '#site/next.locales.mjs'; import { getFeeds } from '#site/util/feeds'; type DynamicStaticPaths = { locale: string; feed: string }; diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index b8ca39bbe019f..5e1e440c5b974 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -1,3 +1,4 @@ +import { availableLocales, defaultLocale } from '@node-core/website-i18n'; import { Analytics } from '@vercel/analytics/react'; import { SpeedInsights } from '@vercel/speed-insights/next'; import classNames from 'classnames'; @@ -6,7 +7,6 @@ import { NextIntlClientProvider } from 'next-intl'; import BaseLayout from '#site/layouts/Base'; import { VERCEL_ENV } from '#site/next.constants.mjs'; import { IBM_PLEX_MONO, OPEN_SANS } from '#site/next.fonts'; -import { availableLocalesMap, defaultLocale } from '#site/next.locales.mjs'; import { ThemeProvider } from '#site/providers/themeProvider'; import type { FC, PropsWithChildren } from 'react'; @@ -22,7 +22,8 @@ type RootLayoutProps = PropsWithChildren<{ const RootLayout: FC = async ({ children, params }) => { const { locale } = await params; - const { langDir, hrefLang } = availableLocalesMap[locale] || defaultLocale; + const { langDir, hrefLang } = + availableLocales.find(l => l.code === locale) || defaultLocale; return ( { if (availableLocaleCodes.includes(locale)) { // Other languages don't really require HMR as they // will never be development languages so we can load them dynamically - const messages = await importLocale(locale); + const { default: messages } = await import( + `@node-core/website-i18n/locales/${locale}.json` + ); // Use default messages as fallback return deepMerge(defaultMessages, messages); diff --git a/apps/site/middleware.ts b/apps/site/middleware.ts index 58c3ded5d1d70..1f2b0955b6f13 100644 --- a/apps/site/middleware.ts +++ b/apps/site/middleware.ts @@ -1,7 +1,6 @@ +import { availableLocaleCodes, defaultLocale } from '@node-core/website-i18n'; import createMiddleware from 'next-intl/middleware'; -import { availableLocaleCodes, defaultLocale } from '#site/next.locales.mjs'; - export default createMiddleware({ // A list of all locales that are supported locales: availableLocaleCodes, diff --git a/apps/site/navigation.mjs b/apps/site/navigation.mjs index b551ea2bcc390..6a0b8f993d7c8 100644 --- a/apps/site/navigation.mjs +++ b/apps/site/navigation.mjs @@ -1,9 +1,8 @@ 'use strict'; +import { availableLocaleCodes } from '@node-core/website-i18n'; import { createNavigation } from 'next-intl/navigation'; -import { availableLocaleCodes } from './next.locales.mjs'; - export const { Link, redirect, usePathname, useRouter } = createNavigation({ locales: availableLocaleCodes, }); diff --git a/apps/site/next-data/generators/downloadSnippets.mjs b/apps/site/next-data/generators/downloadSnippets.mjs index 346f7a96ebaad..9e793444d5042 100644 --- a/apps/site/next-data/generators/downloadSnippets.mjs +++ b/apps/site/next-data/generators/downloadSnippets.mjs @@ -3,7 +3,7 @@ import { readFile, glob } from 'node:fs/promises'; import { basename, extname, join } from 'node:path'; -import { availableLocaleCodes } from '../../next.locales.mjs'; +import { availableLocaleCodes } from '@node-core/website-i18n'; /** * This method is used to generate the Node.js Website Download Snippets diff --git a/apps/site/next.dynamic.mjs b/apps/site/next.dynamic.mjs index 41c49be14d48d..6dfea0d82f56a 100644 --- a/apps/site/next.dynamic.mjs +++ b/apps/site/next.dynamic.mjs @@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises'; import { join, normalize, sep } from 'node:path'; +import { availableLocaleCodes, defaultLocale } from '@node-core/website-i18n'; import matter from 'gray-matter'; import { cache } from 'react'; import { VFile } from 'vfile'; @@ -17,7 +18,6 @@ import { IS_DEV_ENV } from './next.constants.mjs'; import { PAGE_METADATA } from './next.dynamic.constants.mjs'; import { getMarkdownFiles } from './next.helpers.mjs'; import { siteConfig } from './next.json.mjs'; -import { availableLocaleCodes, defaultLocale } from './next.locales.mjs'; // This is the combination of the Application Base URL and Base PATH const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; diff --git a/apps/site/next.dynamic.page.mjs b/apps/site/next.dynamic.page.mjs index 06150d6b8baa9..b9878b67706aa 100644 --- a/apps/site/next.dynamic.page.mjs +++ b/apps/site/next.dynamic.page.mjs @@ -1,5 +1,7 @@ import { join } from 'node:path'; +import { allLocaleCodes, availableLocaleCodes } from '@node-core/website-i18n'; +import { defaultLocale } from '@node-core/website-i18n'; import { notFound, redirect } from 'next/navigation'; import { setRequestLocale } from 'next-intl/server'; @@ -7,8 +9,6 @@ import { setClientContext } from '#site/client-context'; import WithLayout from '#site/components/withLayout'; import { PAGE_VIEWPORT } from '#site/next.dynamic.constants.mjs'; import { dynamicRouter } from '#site/next.dynamic.mjs'; -import { allLocaleCodes, availableLocaleCodes } from '#site/next.locales.mjs'; -import { defaultLocale } from '#site/next.locales.mjs'; import { MatterProvider } from '#site/providers/matterProvider'; /** diff --git a/apps/site/next.locales.mjs b/apps/site/next.locales.mjs deleted file mode 100644 index 058a9f3f62dde..0000000000000 --- a/apps/site/next.locales.mjs +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -import { - getAvailableLocales, - getAvailableLocaleCodes, - getDefaultLocale, - getAvailableLocalesMap, - getAllLocaleCodes, -} from '@node-core/website-i18n'; - -// As set of available and enabled locales for the website -// This is used for allowing us to redirect the user to any -// of the available locales that we have enabled on the website -const availableLocales = getAvailableLocales(); - -// This gives an easy way of accessing all available locale codes -const availableLocaleCodes = getAvailableLocaleCodes(); - -// This provides the default locale information for the Next.js Application -// This is marked by the unique `locale.default` property on the `en` locale -/** @type {import('@node-core/website-i18n/types').LocaleConfig} */ -const defaultLocale = getDefaultLocale(); - -// Creates a Map of available locales for easy access -const availableLocalesMap = getAvailableLocalesMap(); - -// Creates all supported locales -const allLocaleCodes = getAllLocaleCodes(); - -export { - allLocaleCodes, - availableLocales, - availableLocaleCodes, - availableLocalesMap, - defaultLocale, -}; diff --git a/apps/site/next.rewrites.mjs b/apps/site/next.rewrites.mjs index aa8bea4125883..53df5412dcd0d 100644 --- a/apps/site/next.rewrites.mjs +++ b/apps/site/next.rewrites.mjs @@ -1,7 +1,8 @@ 'use strict'; +import { availableLocaleCodes } from '@node-core/website-i18n'; + import { siteRedirects } from './next.json.mjs'; -import { availableLocaleCodes } from './next.locales.mjs'; // This allows us to prefix redirects with all available locale codes so that redirects are not bound to a single locale // This also transforms the locale itself as a matching group that can be used for rewrites diff --git a/apps/site/tests/e2e/general-behavior.spec.ts b/apps/site/tests/e2e/general-behavior.spec.ts index 827a0435a5e33..ca977da9bb493 100644 --- a/apps/site/tests/e2e/general-behavior.spec.ts +++ b/apps/site/tests/e2e/general-behavior.spec.ts @@ -1,10 +1,8 @@ -import { importLocale } from '@node-core/website-i18n'; +import englishLocale from '@node-core/website-i18n/locales/en.json'; import { test, expect, type Page } from '@playwright/test'; import type { Locale } from '@node-core/website-i18n/types'; -const englishLocale = await importLocale('en'); - // TODO(@avivkeller): It would be ideal for all the Test IDs to not exist in the // ui-components package, and instead be passed as props. const locators = { @@ -43,8 +41,10 @@ const openLanguageMenu = async (page: Page) => { const verifyTranslation = async (page: Page, locale: Locale | string) => { // Load locale data if string code provided (e.g., 'es', 'fr') - const localeData = - typeof locale === 'string' ? await importLocale(locale) : locale; + const localeData: Locale = + typeof locale === 'string' + ? await import(`@node-core/website-i18n/locales/${locale}.json`) + : locale; // Get navigation links and expected translations const links = await page diff --git a/docs/technologies.md b/docs/technologies.md index 6a15eb3d8e0a7..ff9885104c160 100644 --- a/docs/technologies.md +++ b/docs/technologies.md @@ -33,7 +33,6 @@ This document provides an overview of the technologies used in the Node.js websi - [`redirects.json`](#redirectsjson) - [Configuration Files](#configuration-files) - [`site.json`](#sitejson) - - [`next.locales.mjs`](#nextlocalesmjs) - [Development Environment](#development-environment) - [VSCode Configuration](#vscode-configuration) - [Build and Deployment](#build-and-deployment) @@ -267,14 +266,6 @@ Website metadata configuration: - Build-time metadata - Uses JSON format for easy collaboration -### `next.locales.mjs` - -Locale configuration and management: - -- Supported locale definitions -- Locale-specific settings -- Page availability per locale - ## Development Environment ### VSCode Configuration diff --git a/packages/i18n/src/index.mjs b/packages/i18n/src/index.mjs index dfb90efe53a9e..14102025eafa4 100644 --- a/packages/i18n/src/index.mjs +++ b/packages/i18n/src/index.mjs @@ -2,18 +2,6 @@ import localeConfig from './config.json' with { type: 'json' }; -/** - * Imports a locale when exists from the locales directory - * - * @param {string} locale The locale code to import - * @returns {Promise} The imported locale - */ -export const importLocale = async locale => { - return import(`./locales/${locale}.json`, { with: { type: 'json' } }).then( - f => f.default - ); -}; - /** * A set of available and enabled locales for the website * This is used for allowing us to redirect the user to any @@ -21,21 +9,15 @@ export const importLocale = async locale => { * * @returns {Array} */ -export const getAvailableLocales = () => - localeConfig.filter(locale => locale.enabled); +export const availableLocales = localeConfig.filter(locale => locale.enabled); // This gives an easy way of accessing all available locale codes -export const getAvailableLocaleCodes = () => - getAvailableLocales().map(locale => locale.code); +export const availableLocaleCodes = availableLocales.map(locale => locale.code); // This provides the default locale information for the Next.js Application // This is marked by the unique `locale.default` property on the `en` locale -export const getDefaultLocale = () => - getAvailableLocales().find(locale => locale.default); - -// Creates a Map of available locales for easy access -export const getAvailableLocalesMap = () => - Object.fromEntries(localeConfig.map(locale => [locale.code, locale])); +/** @type {import('./types').LocaleConfig} */ +export const defaultLocale = availableLocales.find(locale => locale.default); // Creates all supported locales -export const getAllLocaleCodes = () => localeConfig.map(locale => locale.code); +export const allLocaleCodes = localeConfig.map(locale => locale.code); From 7437d21dc0165df85b9c95d375a04129efdd5e0a Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Wed, 24 Dec 2025 16:06:44 -0500 Subject: [PATCH 2/3] fixup! --- apps/site/tests/e2e/general-behavior.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/site/tests/e2e/general-behavior.spec.ts b/apps/site/tests/e2e/general-behavior.spec.ts index ca977da9bb493..5ddc6ef37b671 100644 --- a/apps/site/tests/e2e/general-behavior.spec.ts +++ b/apps/site/tests/e2e/general-behavior.spec.ts @@ -1,4 +1,4 @@ -import englishLocale from '@node-core/website-i18n/locales/en.json'; +import englishLocale from '@node-core/website-i18n/locales/en.json' with { type: 'json' }; import { test, expect, type Page } from '@playwright/test'; import type { Locale } from '@node-core/website-i18n/types'; From 01712e196aa959bc66317c5a9edf9ce3b17a587d Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Wed, 24 Dec 2025 16:35:49 -0500 Subject: [PATCH 3/3] fixup! --- apps/site/next.dynamic.page.mjs | 7 +++++-- apps/site/tests/e2e/general-behavior.spec.ts | 6 +++++- packages/i18n/src/index.mjs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/site/next.dynamic.page.mjs b/apps/site/next.dynamic.page.mjs index b9878b67706aa..08cd8f6118c3f 100644 --- a/apps/site/next.dynamic.page.mjs +++ b/apps/site/next.dynamic.page.mjs @@ -1,7 +1,10 @@ import { join } from 'node:path'; -import { allLocaleCodes, availableLocaleCodes } from '@node-core/website-i18n'; -import { defaultLocale } from '@node-core/website-i18n'; +import { + allLocaleCodes, + defaultLocale, + availableLocaleCodes, +} from '@node-core/website-i18n'; import { notFound, redirect } from 'next/navigation'; import { setRequestLocale } from 'next-intl/server'; diff --git a/apps/site/tests/e2e/general-behavior.spec.ts b/apps/site/tests/e2e/general-behavior.spec.ts index 5ddc6ef37b671..327c897b383b5 100644 --- a/apps/site/tests/e2e/general-behavior.spec.ts +++ b/apps/site/tests/e2e/general-behavior.spec.ts @@ -43,7 +43,11 @@ const verifyTranslation = async (page: Page, locale: Locale | string) => { // Load locale data if string code provided (e.g., 'es', 'fr') const localeData: Locale = typeof locale === 'string' - ? await import(`@node-core/website-i18n/locales/${locale}.json`) + ? ( + await import(`@node-core/website-i18n/locales/${locale}.json`, { + with: { type: 'json' }, + }) + ).default : locale; // Get navigation links and expected translations diff --git a/packages/i18n/src/index.mjs b/packages/i18n/src/index.mjs index 14102025eafa4..91a206c640723 100644 --- a/packages/i18n/src/index.mjs +++ b/packages/i18n/src/index.mjs @@ -7,7 +7,7 @@ import localeConfig from './config.json' with { type: 'json' }; * This is used for allowing us to redirect the user to any * of the available locales that we have enabled on the website * - * @returns {Array} + * @type {Array} */ export const availableLocales = localeConfig.filter(locale => locale.enabled);