Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import { DEFAULT_LOCALE } from "./src/lib/constants"
const handleI18nRouting = createMiddleware(routing)

export default function middleware(request: NextRequest) {
// Normalize to lowercase paths site-wide (URLs are case-insensitive by spec,
// but our routes are defined in lowercase). Do this BEFORE i18n routing.
const originalPath = request.nextUrl.pathname
const lowerPath = originalPath.toLowerCase()
if (originalPath !== lowerPath) {
const url = request.nextUrl.clone()
url.pathname = lowerPath
return NextResponse.redirect(url, 301)
}

// Handle i18n routing
const response = handleI18nRouting(request)

// Upgrade default-locale strip redirects from 307 to 301 for SEO
Expand Down
37 changes: 37 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const createNextIntlPlugin = require("next-intl/plugin")

const { withSentryConfig } = require("@sentry/nextjs")

const redirects = require("./redirects.config")

const i18nConfigJson = require("./i18n.config.json")

const withNextIntl = createNextIntlPlugin()

const LIMIT_CPUS = Number(process.env.LIMIT_CPUS ?? 2)
Expand Down Expand Up @@ -135,6 +139,39 @@ module.exports = (phase, { defaultConfig }) => {
},
]
},
async redirects() {
// Build a strict locale matcher from configured locales
const LOCALE_ALTS = i18nConfigJson.map(({ code }) => code).join("|") // e.g. "en|es|fr|..."

// Helper function to generate both English (no prefix) and locale-prefixed redirects
const createRedirect = (source, destination, permanent = true) => {
// For external URLs, don't modify the destination
const isExternal = destination.startsWith("http")

// English / default-locale: no prefix in source or destination
const defaultRedirect = { source, destination, permanent }

// Locale-prefixed: only match allowed locales (prevents matching arbitrary segments)
const localeRedirect = {
source: `/:locale(${LOCALE_ALTS})${source}`,
destination: isExternal ? destination : `/:locale${destination}`,
permanent,
}

return [defaultRedirect, localeRedirect]
}

return [
// Custom locale aliases redirects
{ source: "/no/:path*", destination: "/nb/:path*", permanent: true },
{ source: "/ph/:path*", destination: "/fil/:path*", permanent: true },

// All primary redirects
...redirects.flatMap(([source, destination, permanent]) =>
createRedirect(source, destination, permanent)
),
]
},
}

nextConfig = {
Expand Down
186 changes: 0 additions & 186 deletions public/_redirects

This file was deleted.

104 changes: 104 additions & 0 deletions redirects.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// All primary redirects ([source, destination, permanent? (default true)])

/** @type { [string, string, boolean | undefined][] } */
module.exports = [
["/discord", "https://discord.gg/ethereum-org"],
["/writing-cohort", "https://ethereumwriterscohort.carrd.co/"],
["/pdfs/:path*", "/"],
["/brand", "/assets/"],
["/ethereum.html", "/what-is-ethereum/"],
["/ether", "/what-is-ether/"],
["/eth", "/what-is-ether/"],
["/token", "/developers/"],
["/crowdsale", "/developers/"],
["/cli", "/developers/"],
["/greeter", "/developers/"],
["/roadmap/vision", "/roadmap/"],
["/search", "/"],
["/garden", "/roadmap/"],
["/download", "/wallets/find-wallet/"],
["/how", "/guides/"],
["/content/:path*", "/:path*"],
["/nfts", "/nft/"],
["/daos", "/dao/"],
["/layer2", "/layer-2/"],
["/grants", "/community/grants/"],
["/java", "/developers/docs/programming-languages/java/"],
["/python", "/developers/docs/programming-languages/python/"],
["/javascript", "/developers/docs/programming-languages/javascript/"],
["/golang", "/developers/docs/programming-languages/golang/"],
["/rust", "/developers/docs/programming-languages/rust/"],
["/dot-net", "/developers/docs/programming-languages/dot-net/"],
["/delphi", "/developers/docs/programming-languages/delphi/"],
["/dart", "/developers/docs/programming-languages/dart/"],
["/languages", "/community/language-resources/"],
[
"/developers/docs/mining",
"/developers/docs/consensus-mechanisms/pow/mining/",
],
["/beginners", "/what-is-ethereum/"],
["/build", "/developers/learning-tools/"],
["/eth2/beacon-chain", "/roadmap/beacon-chain/"],
["/eth2/the-beacon-chain", "/roadmap/beacon-chain/"],
["/upgrades/the-beacon-chain", "/roadmap/beacon-chain/"],
["/eth2/merge", "/roadmap/merge/"],
["/eth2/the-merge", "/roadmap/merge/"],
["/upgrades/the-merge", "/roadmap/merge/"],
["/eth2/docking", "/roadmap/merge/"],
["/upgrades/docking", "/roadmap/merge/"],
["/eth2/the-docking", "/roadmap/merge/"],
["/upgrades/the-docking", "/roadmap/merge/"],
["/eth2/shard-chains", "/roadmap/danksharding/"],
["/upgrades/shard-chains", "/roadmap/danksharding/"],
["/upgrades/sharding", "/roadmap/danksharding/"],
["/upgrades/merge", "/roadmap/merge/"],
["/upgrades/merge/issuance", "/roadmap/merge/issuance"],
["/upgrades/beacon-chain", "/roadmap/beacon-chain"],
["/upgrades/vision", "/roadmap/"],
["/upgrades", "/roadmap"],
["/upgrades/get-involved", "/contributing"],
["/eth2/staking", "/staking/"],
["/eth2/vision", "/roadmap/vision/"],
["/eth2/get-involved", "/contributing/"],
["/eth2/get-involved/bug-bounty", "/bug-bounty/"],
["/upgrades/get-involved/bug-bounty", "/bug-bounty/"],
["/eth2/deposit-contract", "/staking/deposit-contract/"],
["/eth2", "/roadmap/"],
["/developers/docs/scaling/layer-2-rollups", "/developers/docs/scaling"],
["/developers/docs/layer-2-scaling", "/layer-2/"],
["/about/web-developer", "/about/#open-jobs"],
["/about/product-designer", "/about/#open-jobs"],
["/use", "/apps/"],
["/dapps", "/apps/"],
[
"/contributing/translation-program/translation-guide",
"/contributing/translation-program/faq/",
],
[
"/contributing/translation-program/content-versions",
"/contributing/translation-program/",
],
[
"/contributing/translation-program/content-buckets",
"/contributing/translation-program/",
],
[
"/developers/docs/smart-contracts/source-code-verification",
"/developers/docs/smart-contracts/verifying/",
],
[
"/developers/docs/smart-contracts/upgrading-smart-contracts",
"/developers/docs/smart-contracts/upgrading/",
],
["/staking/withdraws", "/staking/withdrawals/"],
[
"/guides/how-to-register-an-ethereum-account",
"/guides/how-to-create-an-ethereum-account/",
],
["/deprecated-software", "/apps/"],
["/enterprise/private-ethereum", "/enterprise/"],
["/dashboards", "/resources"],
["/tds", "/trillion-dollar-security"],
["/10-years", "/10years"],
["/history", "/ethereum-forks"],
]
Loading