Skip to content

Commit 07ef70d

Browse files
committed
Merge branch 'master' into staging
2 parents 20d16cd + e09fcff commit 07ef70d

File tree

4 files changed

+152
-190
lines changed

4 files changed

+152
-190
lines changed

middleware.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ import { DEFAULT_LOCALE } from "./src/lib/constants"
77
const handleI18nRouting = createMiddleware(routing)
88

99
export default function middleware(request: NextRequest) {
10+
// Normalize to lowercase paths site-wide (URLs are case-insensitive by spec,
11+
// but our routes are defined in lowercase). Do this BEFORE i18n routing.
12+
const originalPath = request.nextUrl.pathname
13+
const lowerPath = originalPath.toLowerCase()
14+
if (originalPath !== lowerPath) {
15+
const url = request.nextUrl.clone()
16+
url.pathname = lowerPath
17+
return NextResponse.redirect(url, 301)
18+
}
19+
20+
// Handle i18n routing
1021
const response = handleI18nRouting(request)
1122

1223
// Upgrade default-locale strip redirects from 307 to 301 for SEO

next.config.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const createNextIntlPlugin = require("next-intl/plugin")
99

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

12+
const redirects = require("./redirects.config")
13+
14+
const i18nConfigJson = require("./i18n.config.json")
15+
1216
const withNextIntl = createNextIntlPlugin()
1317

1418
const LIMIT_CPUS = Number(process.env.LIMIT_CPUS ?? 2)
@@ -135,6 +139,39 @@ module.exports = (phase, { defaultConfig }) => {
135139
},
136140
]
137141
},
142+
async redirects() {
143+
// Build a strict locale matcher from configured locales
144+
const LOCALE_ALTS = i18nConfigJson.map(({ code }) => code).join("|") // e.g. "en|es|fr|..."
145+
146+
// Helper function to generate both English (no prefix) and locale-prefixed redirects
147+
const createRedirect = (source, destination, permanent = true) => {
148+
// For external URLs, don't modify the destination
149+
const isExternal = destination.startsWith("http")
150+
151+
// English / default-locale: no prefix in source or destination
152+
const defaultRedirect = { source, destination, permanent }
153+
154+
// Locale-prefixed: only match allowed locales (prevents matching arbitrary segments)
155+
const localeRedirect = {
156+
source: `/:locale(${LOCALE_ALTS})${source}`,
157+
destination: isExternal ? destination : `/:locale${destination}`,
158+
permanent,
159+
}
160+
161+
return [defaultRedirect, localeRedirect]
162+
}
163+
164+
return [
165+
// Custom locale aliases redirects
166+
{ source: "/no/:path*", destination: "/nb/:path*", permanent: true },
167+
{ source: "/ph/:path*", destination: "/fil/:path*", permanent: true },
168+
169+
// All primary redirects
170+
...redirects.flatMap(([source, destination, permanent]) =>
171+
createRedirect(source, destination, permanent)
172+
),
173+
]
174+
},
138175
}
139176

140177
nextConfig = {

public/_redirects

Lines changed: 0 additions & 190 deletions
This file was deleted.

redirects.config.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// All primary redirects ([source, destination, permanent? (default true)])
2+
3+
/** @type { [string, string, boolean | undefined][] } */
4+
module.exports = [
5+
["/discord", "https://discord.gg/ethereum-org"],
6+
["/writing-cohort", "https://ethereumwriterscohort.carrd.co/"],
7+
["/pdfs/:path*", "/"],
8+
["/brand", "/assets/"],
9+
["/ethereum.html", "/what-is-ethereum/"],
10+
["/ether", "/what-is-ether/"],
11+
["/eth", "/what-is-ether/"],
12+
["/token", "/developers/"],
13+
["/crowdsale", "/developers/"],
14+
["/cli", "/developers/"],
15+
["/greeter", "/developers/"],
16+
["/roadmap/vision", "/roadmap/"],
17+
["/search", "/"],
18+
["/garden", "/roadmap/"],
19+
["/download", "/wallets/find-wallet/"],
20+
["/how", "/guides/"],
21+
["/content/:path*", "/:path*"],
22+
["/nfts", "/nft/"],
23+
["/daos", "/dao/"],
24+
["/layer2", "/layer-2/"],
25+
["/grants", "/community/grants/"],
26+
["/java", "/developers/docs/programming-languages/java/"],
27+
["/python", "/developers/docs/programming-languages/python/"],
28+
["/javascript", "/developers/docs/programming-languages/javascript/"],
29+
["/golang", "/developers/docs/programming-languages/golang/"],
30+
["/rust", "/developers/docs/programming-languages/rust/"],
31+
["/dot-net", "/developers/docs/programming-languages/dot-net/"],
32+
["/delphi", "/developers/docs/programming-languages/delphi/"],
33+
["/dart", "/developers/docs/programming-languages/dart/"],
34+
["/languages", "/community/language-resources/"],
35+
[
36+
"/developers/docs/mining",
37+
"/developers/docs/consensus-mechanisms/pow/mining/",
38+
],
39+
["/beginners", "/what-is-ethereum/"],
40+
["/build", "/developers/learning-tools/"],
41+
["/eth2/beacon-chain", "/roadmap/beacon-chain/"],
42+
["/eth2/the-beacon-chain", "/roadmap/beacon-chain/"],
43+
["/upgrades/the-beacon-chain", "/roadmap/beacon-chain/"],
44+
["/eth2/merge", "/roadmap/merge/"],
45+
["/eth2/the-merge", "/roadmap/merge/"],
46+
["/upgrades/the-merge", "/roadmap/merge/"],
47+
["/eth2/docking", "/roadmap/merge/"],
48+
["/upgrades/docking", "/roadmap/merge/"],
49+
["/eth2/the-docking", "/roadmap/merge/"],
50+
["/upgrades/the-docking", "/roadmap/merge/"],
51+
["/eth2/shard-chains", "/roadmap/danksharding/"],
52+
["/upgrades/shard-chains", "/roadmap/danksharding/"],
53+
["/upgrades/sharding", "/roadmap/danksharding/"],
54+
["/upgrades/merge", "/roadmap/merge/"],
55+
["/upgrades/merge/issuance", "/roadmap/merge/issuance"],
56+
["/upgrades/beacon-chain", "/roadmap/beacon-chain"],
57+
["/upgrades/vision", "/roadmap/"],
58+
["/upgrades", "/roadmap"],
59+
["/upgrades/get-involved", "/contributing"],
60+
["/eth2/staking", "/staking/"],
61+
["/eth2/vision", "/roadmap/vision/"],
62+
["/eth2/get-involved", "/contributing/"],
63+
["/eth2/get-involved/bug-bounty", "/bug-bounty/"],
64+
["/upgrades/get-involved/bug-bounty", "/bug-bounty/"],
65+
["/eth2/deposit-contract", "/staking/deposit-contract/"],
66+
["/eth2", "/roadmap/"],
67+
["/developers/docs/scaling/layer-2-rollups", "/developers/docs/scaling"],
68+
["/developers/docs/layer-2-scaling", "/layer-2/"],
69+
["/about/web-developer", "/about/#open-jobs"],
70+
["/about/product-designer", "/about/#open-jobs"],
71+
["/use", "/apps/"],
72+
["/dapps", "/apps/"],
73+
[
74+
"/contributing/translation-program/translation-guide",
75+
"/contributing/translation-program/faq/",
76+
],
77+
[
78+
"/contributing/translation-program/content-versions",
79+
"/contributing/translation-program/",
80+
],
81+
[
82+
"/contributing/translation-program/content-buckets",
83+
"/contributing/translation-program/",
84+
],
85+
[
86+
"/developers/docs/smart-contracts/source-code-verification",
87+
"/developers/docs/smart-contracts/verifying/",
88+
],
89+
[
90+
"/developers/docs/smart-contracts/upgrading-smart-contracts",
91+
"/developers/docs/smart-contracts/upgrading/",
92+
],
93+
["/staking/withdraws", "/staking/withdrawals/"],
94+
[
95+
"/guides/how-to-register-an-ethereum-account",
96+
"/guides/how-to-create-an-ethereum-account/",
97+
],
98+
["/deprecated-software", "/apps/"],
99+
["/enterprise/private-ethereum", "/enterprise/"],
100+
["/dashboards", "/resources"],
101+
["/tds", "/trillion-dollar-security"],
102+
["/10-years", "/10years"],
103+
["/history", "/ethereum-forks"],
104+
]

0 commit comments

Comments
 (0)