fix(landing): link English-only tool-detail and self-hosted pages to un-prefixed URLs (#553)

Tool-detail pages (/tools/<section>/<tool>/) and the /self-hosted pages are
built only in English, with no per-locale route, so a locale-prefixed link
404s in the static build. Add an enOnlyHref() helper and use it for those
links in Footer, Navbar, HeroSearch, and ToolGrid so localized pages point at
the English pages that actually exist. Adds an e2e guard asserting localized
pages emit un-prefixed URLs for those routes.
This commit is contained in:
SnapOtter
2026-07-18 00:01:03 +08:00
committed by GitHub
parent 3f7214bac2
commit 6ecc598fc4
6 changed files with 66 additions and 9 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
// biome-ignore-all lint/correctness/noUnusedVariables: Astro template consumes frontmatter values.
import LanguageSwitcher from "@/components/LanguageSwitcher.astro";
import { t } from "@/i18n";
import { localizeHref } from "@/lib/i18n-page";
import { enOnlyHref, localizeHref } from "@/lib/i18n-page";
interface Props {
locale?: string;
@@ -29,7 +29,7 @@ const columns = [
title: t(locale, "footer.col.solutions"),
links: [
{ label: t(locale, "footer.link.enterprise"), href: localizeHref(locale, "/#enterprise") },
{ label: t(locale, "footer.link.selfHosted"), href: localizeHref(locale, "/self-hosted") },
{ label: t(locale, "footer.link.selfHosted"), href: enOnlyHref("/self-hosted") },
{ label: t(locale, "footer.link.openSource"), href: localizeHref(locale, "/#open-source") },
{ label: t(locale, "footer.link.alternatives"), href: localizeHref(locale, "/alternatives") },
{ label: t(locale, "footer.link.pricing"), href: localizeHref(locale, "/#pricing") },
+2 -2
View File
@@ -4,7 +4,7 @@
import { TOOLS, toolSection } from "@snapotter/shared";
import * as lucideIcons from "lucide";
import { t } from "@/i18n";
import { localizeHref } from "@/lib/i18n-page";
import { enOnlyHref, localizeHref } from "@/lib/i18n-page";
import { loadToolStrings } from "@/lib/tool-strings";
interface Props {
@@ -72,7 +72,7 @@ function renderIcon(iconName: string): string {
hidden
>
<a
href={localizeHref(locale, `/tools/${toolSection(tool)}/${tool.id}/`)}
href={enOnlyHref(`/tools/${toolSection(tool)}/${tool.id}/`)}
class="flex items-center gap-3 px-4 py-2.5 no-underline transition-colors hover:bg-primary-subtle"
>
<svg
+3 -3
View File
@@ -1,7 +1,7 @@
---
// biome-ignore-all lint/correctness/noUnusedVariables: Astro template consumes frontmatter values.
import { t } from "@/i18n";
import { localizeHref } from "@/lib/i18n-page";
import { enOnlyHref, localizeHref } from "@/lib/i18n-page";
import { formatCompact, getStarCount } from "@/lib/stats";
interface Props {
@@ -16,12 +16,12 @@ type NavItem = { label: string; href: string; external?: boolean; desc?: string
const productLinks: NavItem[] = [
{
label: t(locale, "nav.product.developers.label"),
href: localizeHref(locale, "/self-hosted/file-conversion-api"),
href: enOnlyHref("/self-hosted/file-conversion-api"),
desc: t(locale, "nav.product.developers.desc"),
},
{
label: t(locale, "nav.product.selfHosted.label"),
href: localizeHref(locale, "/self-hosted"),
href: enOnlyHref("/self-hosted"),
desc: t(locale, "nav.product.selfHosted.desc"),
},
{
+2 -2
View File
@@ -4,7 +4,7 @@
import { CATEGORIES, TOOLS, toolSection } from "@snapotter/shared";
import * as lucideIcons from "lucide";
import { t } from "@/i18n";
import { localizeHref } from "@/lib/i18n-page";
import { enOnlyHref, localizeHref } from "@/lib/i18n-page";
import { loadToolStrings } from "@/lib/tool-strings";
import type { ModalityFilter, ToolSearchItem } from "../lib/tool-search";
import SectionHeading from "./SectionHeading.astro";
@@ -169,7 +169,7 @@ function toToolSearchItem(tool: (typeof TOOLS)[number]): ToolSearchItem {
description: toolStrings[tool.id]?.description ?? tool.description,
modality: tool.modality as ToolModality,
category: tool.category,
url: localizeHref(locale, `/tools/${toolSection(tool)}/${tool.id}/`),
url: enOnlyHref(`/tools/${toolSection(tool)}/${tool.id}/`),
icon: tool.icon,
iconSvg: renderIcon(tool.icon),
color: category?.color ?? "#E07832",