Skip to content

No locale prefix for default locale#16050

Merged
wackerow merged 4 commits into
stagingfrom
remove-fixed-locale
Sep 3, 2025
Merged

No locale prefix for default locale#16050
wackerow merged 4 commits into
stagingfrom
remove-fixed-locale

Conversation

@pettinarip
Copy link
Copy Markdown
Member

@pettinarip pettinarip commented Aug 11, 2025

Intent to remove unnecessary redirect for most of the visitors.

Description

Configures next-intl routing to use "as-needed" locale prefix.

SEO Migration TODO

  • Redirect /en/*/*
  • Update hreflangs — English should now point to / (not /en)
  • Update canonical tags
  • Update internal links
  • Update XML Sitemap
    • Remove /en URLs.
    • Add / URLs for English.
  • Resubmit sitemap in Google Search Console.

Summary by CodeRabbit

  • New Features

    • Cleaner URLs for the default language: removes the default locale prefix from links and sitemap entries (e.g., “/en” no longer shown for English).
    • Localized URLs now appear only when needed; default-language pages use unprefixed paths.
  • Refactor

    • Improved routing configuration to better handle conditional locale prefixes.
    • Standardized URL generation to consistently omit the default locale while preserving non-default locales.
    • Harmonized analytics tracking to use normalized, locale-aware paths for more accurate reporting.

@netlify
Copy link
Copy Markdown

netlify Bot commented Aug 11, 2025

Deploy Preview for ethereumorg ready!

Name Link
🔨 Latest commit dfe2637
🔍 Latest deploy log https://app.netlify.com/projects/ethereumorg/deploys/68b6f0504cb4af00081e246d
😎 Deploy Preview https://deploy-preview-16050--ethereumorg.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
7 paths audited
Performance: 44 (🔴 down 11 from production)
Accessibility: 96 (🟢 up 1 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 92 (🔴 down 8 from production)
PWA: 59 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@pettinarip pettinarip marked this pull request as draft August 11, 2025 15:45
@github-actions github-actions Bot added the tooling 🔧 Changes related to tooling of the project label Aug 11, 2025
Comment thread src/lib/utils/matomo.ts Outdated
@pettinarip pettinarip marked this pull request as ready for review September 1, 2025 14:00
@github-actions github-actions Bot added the config ⚙️ Changes to configuration files label Sep 2, 2025
@pettinarip pettinarip changed the base branch from dev to staging September 2, 2025 13:25
@pettinarip pettinarip changed the title No locale prefix for default lang No locale prefix for default locale Sep 2, 2025
@minimalsm
Copy link
Copy Markdown
Contributor

@coderabbitai do your thing and review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 3, 2025

🧩 Analysis chain

I'll review this PR that implements "as-needed" locale prefix configuration for next-intl routing. Let me analyze the changes first.


🏁 Script executed:

#!/bin/bash
# Get the PR details and changed files
gh pr view 16050 --json files,additions,deletions,title,body

# List all changed files in the PR
gh pr diff 16050 --name-only

# Get the detailed diff of the PR
gh pr diff 16050

Length of output: 6108


Now let me examine some key configuration files to better understand the implementation:


🏁 Script executed:

#!/bin/bash
# Check the i18n configuration
cat i18n.config.json

# Check the current routing configuration
cat src/i18n/routing.ts

# Look for any middleware configuration
find . -name "middleware.ts" -o -name "middleware.js" | head -5 | xargs cat

# Check if there are any other locale-related configurations
cat src/lib/constants.ts | grep -A10 -B10 "LOCALE\|locale"

Length of output: 2103


Let me check for the i18n config file in the correct location and examine any existing redirect configurations:


🏁 Script executed:

#!/bin/bash
# Find the i18n config file
find . -name "*i18n*" -type f | head -10

# Check for any existing redirect configurations
find . -name "*.config.*" | xargs grep -l "redirect\|rewrite" 2>/dev/null | head -5 | xargs cat

# Look for any existing SEO/canonical implementations
rg -n "canonical\|hreflang" --type=tsx --type=ts -A3 -B3

# Check if there are any tests related to routing
find . -path "*/test*" -name "*routing*" -o -path "*/test*" -name "*i18n*" | head -5 | xargs ls -la 2>/dev/null || echo "No test files found"

Length of output: 2813


Let me examine the SEO-related implementations and understand the current locale configuration better:


🏁 Script executed:

#!/bin/bash
# Look for SEO/canonical implementations
rg -n "canonical\|hreflang" --type=ts --type=tsx -A3 -B3 2>/dev/null || rg -n "canonical\|hreflang" -A3 -B3

# Check the i18n utility file
cat src/lib/utils/i18n.ts

# Look for any Next.js app directory structure and layout files
find app -name "layout.tsx" -o -name "layout.ts" | head -5 | xargs cat

# Check package.json for next-intl version and other relevant dependencies
cat package.json | jq '.dependencies | with_entries(select(.key | contains("intl") or contains("i18n") or contains("next")))'

Length of output: 3096


Let me find and examine the package.json file and check for any SEO metadata implementations:


🏁 Script executed:

#!/bin/bash
# Find package.json
find . -maxdepth 3 -name "package.json" | head -1 | xargs cat | jq '.dependencies | with_entries(select(.key | contains("intl") or contains("i18n") or contains("next")))'

# Look for metadata and SEO implementations
rg -n "metadata\|Metadata\|generateMetadata" --type=ts -A5 -B2

# Check for any existing redirect implementations in Next.js config
cat next.config.js

# Look for any existing canonical or hreflang implementations in components
rg -n "rel.*canonical\|hreflang" -A3 -B3

Length of output: 5994


Let me find the package.json file and check for any metadata implementations in the codebase:


🏁 Script executed:

#!/bin/bash
# Find and examine package.json
find . -name "package.json" -exec cat {} \; | head -50

# Look for metadata implementations in pages or components
rg -n "generateMetadata\|metadata:" --type=ts -A5 -B2

# Check for any BaseLayout implementation that might handle SEO
find . -name "*Layout*" -type f | head -5 | xargs cat | head -100

# Look for any existing SEO components
find . -name "*[Ss]eo*" -type f -o -name "*[Mm]eta*" -type f | head -5

Length of output: 3704


Now let me examine the metadata utilities and tutorial metadata to understand how SEO is currently implemented:


🏁 Script executed:

#!/bin/bash
# Examine the metadata utilities
cat src/lib/utils/metadata.ts

# Check the tutorial metadata component
cat src/components/TutorialMetadata.tsx

# Look at the md metadata implementation
cat src/lib/md/metadata.ts

# Find any pages with metadata implementations
rg -n "export.*metadata\|generateMetadata" --type=ts -A10 -B2 | head -50

Length of output: 7199


Let me now examine how the getFullUrl function was modified and check for any existing redirect configurations:


🏁 Script executed:

#!/bin/bash
# Look for any redirect configurations in Netlify or Vercel
find . -name "_redirects" -o -name "vercel.json" -o -name "netlify.toml" | head -5 | xargs cat

# Check if there are any existing tests for the URL utilities
find . -path "*/test*" -name "*url*" -o -path "*/test*" -name "*routing*" | head -5 | xargs cat

# Look for any app directory pages that might have generateMetadata
find app -name "page.tsx" -o -name "page.ts" | head -10 | xargs grep -l "generateMetadata\|metadata" | head -3 | xargs cat | head -100

# Check the current implementation of getFullUrl to understand the change better
cat src/lib/utils/url.ts

Length of output: 11258


Based on my comprehensive analysis of the PR, I can now provide a thorough review of this important SEO migration. The changes implement "as-needed" locale prefix configuration for next-intl routing to remove unnecessary redirects for English users.

@minimalsm: I've thoroughly reviewed PR #16050 implementing the "as-needed" locale prefix configuration. This is a well-structured SEO migration with several strong implementations, though I found some areas that need attention:

✅ Excellent Implementations

1. Core Routing Configuration

  • The localePrefix: "as-needed" in src/i18n/routing.ts is correctly implemented
  • Middleware configuration properly handles the routing changes

2. SEO Metadata Updates

  • The getFullUrl function correctly handles default locale URLs (removes /en prefix for English)
  • Canonical URLs and hreflang tags are properly updated in metadata.ts
  • The x-default hreflang correctly points to the default locale

3. XML Sitemap Configuration

  • Excellent handling of English URLs in next-sitemap.config.js
  • Correctly strips /en prefix and avoids duplicate root entries
  • Proper priority and frequency settings for default locale

🔍 Issues Requiring Attention

1. Matomo Analytics Inconsistency
The normalizePathForMatomo function adds /en prefix to English paths for analytics consistency, but this creates a mismatch with the actual URLs served:

// This will track '/en/developers' for a page served at '/developers'
export const normalizePathForMatomo = (pathname: string): string => {
  // ...
  return `/${DEFAULT_LOCALE}${pathname}` // Adding /en prefix
}

Recommendation: Consider whether you want analytics to track the actual served URLs (/developers) or maintain the historical /en prefix pattern for consistency with past data.

2. Missing Redirect Configuration
I noticed existing redirects in _redirects file, but no explicit /en/*/* redirects for the SEO migration. You'll need server-side redirects to handle:

  • Bookmarked /en/ URLs
  • External links pointing to /en/ URLs
  • Search engine indexed /en/ URLs

3. Testing Coverage
No test files were found for the routing/URL utilities. Consider adding tests for:

  • getFullUrl function behavior
  • Canonical URL generation
  • Locale prefix handling

📋 SEO Migration Checklist Verification

Based on your TODO list, most items are completed in code:

  • ✅ Update hreflangs (implemented in metadata.ts)
  • ✅ Update canonical tags (implemented in metadata.ts)
  • ✅ Update XML Sitemap (implemented in next-sitemap.config.js)
  • ❓ Redirect /en/*/* (needs verification in server config)
  • ⏳ Update internal links (would need full codebase scan to verify)

🚀 Recommendations

  1. Add explicit redirects for /en/*/* in your _redirects file
  2. Verify internal links throughout the codebase don't hardcode /en/ paths
  3. Consider Matomo tracking strategy - decide on consistent URL tracking approach
  4. Add tests for URL utilities and routing logic
  5. Monitor search console after deployment for crawl errors or indexing issues

The implementation is solid and follows Next.js + next-intl best practices. The code quality is high with proper type safety and clean abstractions.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 3, 2025

Walkthrough

Updates unify locale handling: default-locale stripping in sitemap, “as-needed” locale prefix in routing, and URL building that omits default locale. Matomo tracking now normalizes paths by ensuring a locale segment, with component and utility changes to send sanitized/normalized URLs to Matomo.

Changes

Cohort / File(s) Summary
Matomo tracking normalization
src/components/Matomo.tsx, src/lib/utils/matomo.ts
Introduced normalizePathForMatomo and applied it to pageview/custom URL tracking. Component now sends normalized referrer and path; utils now prefix paths with DEFAULT_LOCALE when missing and build normalized tracking URLs.
Routing and URLs locale handling
src/i18n/routing.ts, src/lib/utils/url.ts, next-sitemap.config.js
Routing adds localePrefix: "as-needed". URL builder omits default-locale prefix and prefixes non-default locales. Sitemap transform uses a defaultLocale constant, strips default-locale prefix from paths, drops root /en entry, and sets loc from stripped paths; changefreq/priority logic preserved.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Router as Next Router
  participant MatomoComp as Matomo Component
  participant Utils as utils/matomo.normalizePathForMatomo
  participant _paq as Matomo _paq Queue

  Router->>MatomoComp: pathname change
  MatomoComp->>Utils: normalize(previousPath)
  Utils-->>MatomoComp: normalizedPreviousPath
  MatomoComp->>Utils: normalize(pathname)
  Utils-->>MatomoComp: normalizedPathname
  MatomoComp->>_paq: setReferrerUrl(normalizedPreviousPath)
  MatomoComp->>_paq: setCustomUrl(normalizedPathname)
  MatomoComp->>_paq: deleteCustomVariables / trackPageView (delayed)
  note right of _paq: URLs always include a locale segment
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remove-fixed-locale

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/i18n/routing.ts (1)

10-10: Good move: default locale without prefix via next-intl.

This aligns routing with SEO expectations and reduces redirects.

Ensure 301 redirects exist for legacy /en/*/* to preserve link equity and avoid 404s. Want a quick _redirects/Next config snippet?

next-sitemap.config.js (1)

4-4: Avoid drift: derive defaultLocale from i18n config.

Hard-coding "en" risks divergence from app constants.

Apply:

-const defaultLocale = "en"
+const defaultLocale =
+  (i18nConfig.find((l) => l.default)?.code) || "en"

Also, nice touch stripping /en and dropping the root entry. Consider adding explicit 301 /en/*/* at the edge to accelerate recrawl.

Also applies to: 13-25, 27-27

src/components/Matomo.tsx (1)

8-8: Standardize Matomo URLs: send full URLs (not just paths).

trackCustomEvent now sends full URLs, while pageviews send only path—this creates inconsistency in reports.

Apply:

-    push(["setReferrerUrl", normalizedPreviousPath])
-    push(["setCustomUrl", normalizedPathname])
+    push(["setReferrerUrl", `${window.location.origin}${normalizedPreviousPath}`])
+    push(["setCustomUrl", `${window.location.origin}${normalizedPathname}`])

Also applies to: 44-49

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f065958 and dfe2637.

📒 Files selected for processing (5)
  • next-sitemap.config.js (1 hunks)
  • src/components/Matomo.tsx (2 hunks)
  • src/i18n/routing.ts (1 hunks)
  • src/lib/utils/matomo.ts (2 hunks)
  • src/lib/utils/url.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-18T21:22:45.937Z
Learnt from: CR
PR: ethereum/ethereum-org-website#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T21:22:45.937Z
Learning: Applies to {next-sitemap.js,next-sitemap.config.js} : Sitemap generation should use next-sitemap

Applied to files:

  • next-sitemap.config.js
🔇 Additional comments (2)
src/lib/utils/url.ts (1)

53-55: Check for absolute-path usage of getFullUrl
Our search didn’t identify any direct call sites passing an absolute path to getFullUrl. Manually verify there are no indirect or dynamic invocations that could trigger dropping the locale.

src/lib/utils/matomo.ts (1)

50-54: LGTM: normalize and send full URL for events.

Matches Matomo expectations and avoids query/hash noise.

Comment thread src/lib/utils/matomo.ts
Comment on lines +9 to +25
/**
* Normalizes paths to ensure consistent Matomo tracking.
* With localePrefix: "as-needed", English paths don't have /en prefix,
* but we want to track them as /en paths for analytics consistency.
*/
export const normalizePathForMatomo = (pathname: string): string => {
const hasLocalePrefix = LOCALES_CODES.some((locale) =>
pathname.startsWith(`/${locale}/`)
)

if (hasLocalePrefix) {
return pathname
}

// For paths without locale prefix (English content), add /en prefix
return `/${DEFAULT_LOCALE}${pathname}`
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Edge case: locale root without trailing slash is misdetected.

startsWith(\/${locale}/`)misses/${locale}(no trailing slash), causing accidental/endouble-prefixing (e.g.,"/es""/en/es"`).

Apply:

-export const normalizePathForMatomo = (pathname: string): string => {
-  const hasLocalePrefix = LOCALES_CODES.some((locale) =>
-    pathname.startsWith(`/${locale}/`)
-  )
+export const normalizePathForMatomo = (pathname: string): string => {
+  const hasLocalePrefix = LOCALES_CODES.some(
+    (locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`)
+  )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Normalizes paths to ensure consistent Matomo tracking.
* With localePrefix: "as-needed", English paths don't have /en prefix,
* but we want to track them as /en paths for analytics consistency.
*/
export const normalizePathForMatomo = (pathname: string): string => {
const hasLocalePrefix = LOCALES_CODES.some((locale) =>
pathname.startsWith(`/${locale}/`)
)
if (hasLocalePrefix) {
return pathname
}
// For paths without locale prefix (English content), add /en prefix
return `/${DEFAULT_LOCALE}${pathname}`
}
export const normalizePathForMatomo = (pathname: string): string => {
const hasLocalePrefix = LOCALES_CODES.some(
(locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`)
)
if (hasLocalePrefix) {
return pathname
}
// For paths without locale prefix (English content), add /en prefix
return `/${DEFAULT_LOCALE}${pathname}`
}
🤖 Prompt for AI Agents
In src/lib/utils/matomo.ts around lines 9 to 25, the locale detection uses
startsWith(`/${locale}/`) which misses the exact locale root (e.g., "/es") and
causes double-prefixing; update the hasLocalePrefix check to treat either an
exact match (`pathname === \`/${locale}\``) or a prefix match
(`pathname.startsWith(\`/${locale}/\`)`) as having a locale so the function
returns the original pathname for both `/locale` and `/locale/...` cases and
only prefixes `/${DEFAULT_LOCALE}` for truly locale-less paths.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pathname is what it is, I don't think this matters... the helper function here just slips in the English prefix as-needed

@wackerow wackerow merged commit b022ce6 into staging Sep 3, 2025
13 checks passed
@wackerow wackerow deleted the remove-fixed-locale branch September 3, 2025 15:22
@pettinarip pettinarip mentioned this pull request Sep 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config ⚙️ Changes to configuration files tooling 🔧 Changes related to tooling of the project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants