diff --git a/src/components/SiteFooter.tsx b/src/components/SiteFooter.tsx index 6af9e70..dc9bd60 100644 --- a/src/components/SiteFooter.tsx +++ b/src/components/SiteFooter.tsx @@ -1,32 +1,109 @@ import { createLink } from "@tanstack/react-router"; +import { useTranslation } from "react-i18next"; + +import type { Locale } from "../lib/locale"; import { Footer } from "../design-system/footer"; import { Link } from "../design-system/link"; +import { useLocale } from "../lib/LocaleContext"; import { AtStoreLogo } from "./AtStoreLogo"; const FooterLink = createLink(Link); -const FOOTER_LINK_GROUPS = [ +type LocaleLink = { + kind: "locale"; + to: "/$locale" | "/$locale/about"; + tKey: string; +}; +type SearchLink = { + kind: "search"; + to: "/search" | "/apps/all"; + search: { sort: "popular" }; + tKey: string; +}; +type PlainLink = { + kind: "plain"; + to: + | "/developers/atproto" + | "/products/manage" + | "/apps/tags" + | "/apps/lexicons"; + tKey: string; +}; +type FooterLinkDef = LocaleLink | SearchLink | PlainLink; + +const LINK_GROUPS: Array<{ titleKey?: string; links: Array }> = [ { links: [ - { href: "/about", label: "About" }, - { href: "/home", label: "Home" }, - { href: "/search", label: "Search" }, - { href: "/developers/atproto", label: "Developer API" }, - { href: "/products/manage", label: "Manage listings" }, + { kind: "locale", to: "/$locale/about", tKey: "siteFooter.nav.about" }, + { kind: "locale", to: "/$locale", tKey: "siteFooter.nav.home" }, + { + kind: "search", + to: "/search", + search: { sort: "popular" }, + tKey: "siteFooter.nav.search", + }, + { + kind: "plain", + to: "/developers/atproto", + tKey: "siteFooter.nav.developerApi", + }, + { + kind: "plain", + to: "/products/manage", + tKey: "siteFooter.nav.manageListings", + }, ], }, { - title: "Apps", + titleKey: "siteFooter.apps.groupTitle", links: [ - { href: "/apps/all", label: "All Apps" }, - { href: "/apps/tags", label: "Categories" }, - { href: "/apps/lexicons", label: "Shared data" }, + { + kind: "search", + to: "/apps/all", + search: { sort: "popular" }, + tKey: "siteFooter.apps.allApps", + }, + { kind: "plain", to: "/apps/tags", tKey: "siteFooter.apps.categories" }, + { + kind: "plain", + to: "/apps/lexicons", + tKey: "siteFooter.apps.sharedData", + }, ], }, -] as const; +]; + +function renderLink(link: FooterLinkDef, locale: Locale, label: string) { + switch (link.kind) { + case "locale": { + return ( + + {label} + + ); + } + case "search": { + return ( + + {label} + + ); + } + case "plain": { + return ( + + {label} + + ); + } + } +} export function SiteFooter() { + const { locale } = useLocale(); + const { t } = useTranslation("common"); + return ( @@ -34,16 +111,14 @@ export function SiteFooter() { - {FOOTER_LINK_GROUPS.map((group, index) => ( + {LINK_GROUPS.map((group, i) => ( - {group.links.map((link) => ( - - {link.label} - - ))} + {group.links.map((link) => + renderLink(link, locale, t(link.tKey as never)), + )} ))} @@ -51,7 +126,7 @@ export function SiteFooter() { - {new Date().getFullYear()} at-store Copyright. All rights reserved. + {t("siteFooter.copyright", { year: new Date().getFullYear() })} diff --git a/src/components/SiteHeader.tsx b/src/components/SiteHeader.tsx index 49d222d..56f0544 100644 --- a/src/components/SiteHeader.tsx +++ b/src/components/SiteHeader.tsx @@ -11,6 +11,7 @@ import { Flex } from "../design-system/flex"; import { IconButton } from "../design-system/icon-button"; import { Navbar, NavbarAction, NavbarLogo } from "../design-system/navbar"; import { fontSize } from "../design-system/theme/typography.stylex"; +import { useLocale } from "../lib/LocaleContext"; import { AtStoreLogo } from "./AtStoreLogo"; import { LanguageSwitcher } from "./LanguageSwitcher"; import { NavbarAuth } from "./NavbarAuth"; @@ -30,6 +31,7 @@ const styles = stylex.create({ }); export function SiteHeader() { + const { locale } = useLocale(); const { data: session } = useQuery(user.getSessionQueryOptions); const { data: notifications } = useQuery({ ...notificationApi.getProductNotificationsQueryOptions({ limit: 50 }), @@ -43,7 +45,12 @@ export function SiteHeader() { return ( - + diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 441720f..1825989 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -17,7 +17,7 @@ export { * different surfaces in parallel. `common` is for site-wide chrome (header, * footer, switcher); page-specific surfaces get their own namespace. */ -export const NAMESPACES = ["common", "about"] as const; +export const NAMESPACES = ["common", "about", "home"] as const; export type Namespace = (typeof NAMESPACES)[number]; export const DEFAULT_NAMESPACE: Namespace = "common"; diff --git a/src/i18n/locales/en-XA/common.json b/src/i18n/locales/en-XA/common.json index 9419d82..19bde31 100644 --- a/src/i18n/locales/en-XA/common.json +++ b/src/i18n/locales/en-XA/common.json @@ -9,5 +9,21 @@ "en": "[~~ Éñĝļîšĥ ~~]", "en-XA": "[~~ Þšéüðö (éñ-ẊÅ) ~~]" } + }, + "siteFooter": { + "copyright": "[~~~~ {{year}} åţ-šţöŕé Çöþýŕîĝĥţ. Åļļ ŕîĝĥţš ŕéšéŕṽéð. ~~~~]", + "nav": { + "about": "[~~ Åƀöüţ ~~]", + "home": "[~~ Ĥöḿé ~~]", + "search": "[~~ Šéåŕçĥ ~~]", + "developerApi": "[~~ Ðéṽéļöþéŕ ÅÞÎ ~~]", + "manageListings": "[~~ Ḿåñåĝé ļîšţîñĝš ~~]" + }, + "apps": { + "groupTitle": "[~~ Åþþš ~~]", + "allApps": "[~~ Åļļ Åþþš ~~]", + "categories": "[~~ Çåţéĝöŕîéš ~~]", + "sharedData": "[~~ Šĥåŕéð ðåţå ~~]" + } } } diff --git a/src/i18n/locales/en-XA/home.json b/src/i18n/locales/en-XA/home.json new file mode 100644 index 0000000..93c6166 --- /dev/null +++ b/src/i18n/locales/en-XA/home.json @@ -0,0 +1,34 @@ +{ + "ogTitle": "[~~~ åţ-šţöŕé | Åþþš öñ ţĥé Åţḿöšþĥéŕé ~~~]", + "ogDescription": "[~~~~ Ðîšçöṽéŕ åþþš åñð ţööļš åçŕöšš ţĥé Åţḿöšþĥéŕé. Ƒîñð ýöüŕ ñéẋţ ƒåṽöŕîţé åþþ ţöðåý! ~~~~]", + "hero": { + "eyebrow": "[~~ Ţĥé ÅŢ Þŕöţöçöļ åþþ ðîŕéçţöŕý ~~]", + "title": "[~~ Åþþš öñ ţĥé Åţḿöšþĥéŕé ~~]", + "description": "[~~~~ Ðîšçöṽéŕ ţĥé ƀéšţ åþþš ţĥé Åţḿöšþĥéŕé ĥåš ţö öƒƒéŕ. Ŵîţĥ éṽéŕý þŕöðüçţ ýöü öŵñ ýöüŕ ðåţå åñð üšé ţĥé šåḿé îðéñţîţý åçŕöšš åļļ åþþš. ~~~~]" + }, + "claimBanner": { + "title_one": "[~~ Çļåîḿ ýöüŕ ļîšţîñĝ ~~]", + "title_other": "[~~ Çļåîḿ ýöüŕ ļîšţîñĝš ~~]", + "continue": "[~~ Çöñţîñüé ~~]", + "body_one": "[~~~~ \"{{name}}\" îš šţîļļ öñ ţĥé šţöŕé ŕéþö. Çļåîḿ îţ ţö ḿåñåĝé üþðåţéš ƒŕöḿ ýöüŕ ÞЊ. ~~~~]", + "body_other": "[~~~~ Ýöü ĥåṽé {{count}} ļîšţîñĝš öñ ţĥé šţöŕé ŕéþö. Çļåîḿ ţĥéḿ ţö ḿåñåĝé üþðåţéš ƒŕöḿ ýöüŕ ÞЊ. ~~~~]" + }, + "browseSection": { + "eyebrow": "[~~ ßŕöŵšé Åþþš ~~]", + "title": "[~~ Ƒîñð åþþš ýöü'ļļ ļöṽé ~~]" + }, + "popularSection": { + "eyebrow": "[~~ Þöþüļåŕ Ŕîĝĥţ Ñöŵ ~~]", + "title": "[~~ Ţŕéñðîñĝ åçŕöšš ţĥé éçöšýšţéḿ ~~]" + }, + "newSection": { + "eyebrow": "[~~ Ñéŵ & Ñöţéŵöŕţĥý ~~]", + "title": "[~~ Ƒŕéšĥ åþþš ĵüšţ åððéð ~~]" + }, + "sectionHeader": { + "seeAll": "[~~ Šéé Åļļ ~~]" + }, + "popularItem": { + "explore": "[~~ Éẋþļöŕé ~~]" + } +} diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index d55339f..ada8ccd 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -9,5 +9,21 @@ "en": "English", "en-XA": "Pseudo (en-XA)" } + }, + "siteFooter": { + "copyright": "{{year}} at-store Copyright. All rights reserved.", + "nav": { + "about": "About", + "home": "Home", + "search": "Search", + "developerApi": "Developer API", + "manageListings": "Manage listings" + }, + "apps": { + "groupTitle": "Apps", + "allApps": "All Apps", + "categories": "Categories", + "sharedData": "Shared data" + } } } diff --git a/src/i18n/locales/en/home.json b/src/i18n/locales/en/home.json new file mode 100644 index 0000000..c837819 --- /dev/null +++ b/src/i18n/locales/en/home.json @@ -0,0 +1,34 @@ +{ + "ogTitle": "at-store | Apps on the Atmosphere", + "ogDescription": "Discover apps and tools across the Atmosphere. Find your next favorite app today!", + "hero": { + "eyebrow": "The AT Protocol app directory", + "title": "Apps on the Atmosphere", + "description": "Discover the best apps the Atmosphere has to offer. With every product you own your data and use the same identity across all apps." + }, + "claimBanner": { + "title_one": "Claim your listing", + "title_other": "Claim your listings", + "continue": "Continue", + "body_one": "\"{{name}}\" is still on the store repo. Claim it to manage updates from your PDS.", + "body_other": "You have {{count}} listings on the store repo. Claim them to manage updates from your PDS." + }, + "browseSection": { + "eyebrow": "Browse Apps", + "title": "Find apps you'll love" + }, + "popularSection": { + "eyebrow": "Popular Right Now", + "title": "Trending across the ecosystem" + }, + "newSection": { + "eyebrow": "New & Noteworthy", + "title": "Fresh apps just added" + }, + "sectionHeader": { + "seeAll": "See All" + }, + "popularItem": { + "explore": "Explore" + } +} diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts index e27c22e..deb919d 100644 --- a/src/i18n/resources.ts +++ b/src/i18n/resources.ts @@ -6,14 +6,16 @@ import type { Namespace } from "./config"; import enXAAbout from "./locales/en-XA/about.json"; import enXACommon from "./locales/en-XA/common.json"; +import enXAHome from "./locales/en-XA/home.json"; import enAbout from "./locales/en/about.json"; import enCommon from "./locales/en/common.json"; +import enHome from "./locales/en/home.json"; export const resources: Record> = { - en: { common: enCommon, about: enAbout }, + en: { common: enCommon, about: enAbout, home: enHome }, // en-XA is a dev-only pseudo-locale. Rollup replaces import.meta.env.DEV // with `false` in prod and tree-shakes the dead branch + JSON assets. ...(import.meta.env.DEV - ? { "en-XA": { common: enXACommon, about: enXAAbout } } + ? { "en-XA": { common: enXACommon, about: enXAAbout, home: enXAHome } } : {}), }; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index ab7c68d..5a0e207 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -14,7 +14,7 @@ import { Route as HomeRouteImport } from './routes/home' import { Route as HeaderLayoutRouteImport } from './routes/_header-layout' import { Route as LocaleRouteImport } from './routes/$locale' import { Route as OgIndexRouteImport } from './routes/og.index' -import { Route as HeaderLayoutIndexRouteImport } from './routes/_header-layout.index' +import { Route as LocaleIndexRouteImport } from './routes/$locale.index' import { Route as XrpcNsidRouteImport } from './routes/xrpc.$nsid' import { Route as OgTagRouteImport } from './routes/og.tag' import { Route as OgReviewRouteImport } from './routes/og.review' @@ -85,10 +85,10 @@ const OgIndexRoute = OgIndexRouteImport.update({ path: '/og/', getParentRoute: () => rootRouteImport, } as any) -const HeaderLayoutIndexRoute = HeaderLayoutIndexRouteImport.update({ +const LocaleIndexRoute = LocaleIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => HeaderLayoutRoute, + getParentRoute: () => LocaleRoute, } as any) const XrpcNsidRoute = XrpcNsidRouteImport.update({ id: '/xrpc/$nsid', @@ -348,7 +348,7 @@ const HeaderLayoutProductsProductIdReviewsReviewIdEditRoute = export interface FileRoutesByFullPath { '/$locale': typeof LocaleRouteWithChildren - '/': typeof HeaderLayoutIndexRoute + '/': typeof HeaderLayoutAdminLayoutRouteWithChildren '/home': typeof HomeRoute '/login': typeof LoginRoute '/$locale/about': typeof LocaleAboutRoute @@ -358,6 +358,7 @@ export interface FileRoutesByFullPath { '/og/review': typeof OgReviewRoute '/og/tag': typeof OgTagRoute '/xrpc/$nsid': typeof XrpcNsidRoute + '/$locale/': typeof LocaleIndexRoute '/og/': typeof OgIndexRoute '/apps/$tag': typeof HeaderLayoutAppsTagRoute '/apps/all': typeof HeaderLayoutAppsAllRoute @@ -398,17 +399,17 @@ export interface FileRoutesByFullPath { '/products/$productId/reviews/$reviewId/edit': typeof HeaderLayoutProductsProductIdReviewsReviewIdEditRoute } export interface FileRoutesByTo { - '/$locale': typeof LocaleRouteWithChildren + '/': typeof HeaderLayoutAdminLayoutRouteWithChildren '/home': typeof HomeRoute '/login': typeof LoginRoute '/$locale/about': typeof LocaleAboutRoute - '/': typeof HeaderLayoutIndexRoute '/notifications': typeof HeaderLayoutNotificationsRoute '/search': typeof HeaderLayoutSearchRoute '/og/profile': typeof OgProfileRoute '/og/review': typeof OgReviewRoute '/og/tag': typeof OgTagRoute '/xrpc/$nsid': typeof XrpcNsidRoute + '/$locale': typeof LocaleIndexRoute '/og': typeof OgIndexRoute '/apps/$tag': typeof HeaderLayoutAppsTagRoute '/apps/all': typeof HeaderLayoutAppsAllRoute @@ -461,7 +462,7 @@ export interface FileRoutesById { '/og/review': typeof OgReviewRoute '/og/tag': typeof OgTagRoute '/xrpc/$nsid': typeof XrpcNsidRoute - '/_header-layout/': typeof HeaderLayoutIndexRoute + '/$locale/': typeof LocaleIndexRoute '/og/': typeof OgIndexRoute '/_header-layout/apps/$tag': typeof HeaderLayoutAppsTagRoute '/_header-layout/apps/all': typeof HeaderLayoutAppsAllRoute @@ -515,6 +516,7 @@ export interface FileRouteTypes { | '/og/review' | '/og/tag' | '/xrpc/$nsid' + | '/$locale/' | '/og/' | '/apps/$tag' | '/apps/all' @@ -555,17 +557,17 @@ export interface FileRouteTypes { | '/products/$productId/reviews/$reviewId/edit' fileRoutesByTo: FileRoutesByTo to: - | '/$locale' + | '/' | '/home' | '/login' | '/$locale/about' - | '/' | '/notifications' | '/search' | '/og/profile' | '/og/review' | '/og/tag' | '/xrpc/$nsid' + | '/$locale' | '/og' | '/apps/$tag' | '/apps/all' @@ -617,7 +619,7 @@ export interface FileRouteTypes { | '/og/review' | '/og/tag' | '/xrpc/$nsid' - | '/_header-layout/' + | '/$locale/' | '/og/' | '/_header-layout/apps/$tag' | '/_header-layout/apps/all' @@ -712,12 +714,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof OgIndexRouteImport parentRoute: typeof rootRouteImport } - '/_header-layout/': { - id: '/_header-layout/' + '/$locale/': { + id: '/$locale/' path: '/' - fullPath: '/' - preLoaderRoute: typeof HeaderLayoutIndexRouteImport - parentRoute: typeof HeaderLayoutRoute + fullPath: '/$locale/' + preLoaderRoute: typeof LocaleIndexRouteImport + parentRoute: typeof LocaleRoute } '/xrpc/$nsid': { id: '/xrpc/$nsid' @@ -1039,10 +1041,12 @@ declare module '@tanstack/react-router' { interface LocaleRouteChildren { LocaleAboutRoute: typeof LocaleAboutRoute + LocaleIndexRoute: typeof LocaleIndexRoute } const LocaleRouteChildren: LocaleRouteChildren = { LocaleAboutRoute: LocaleAboutRoute, + LocaleIndexRoute: LocaleIndexRoute, } const LocaleRouteWithChildren = @@ -1115,7 +1119,6 @@ interface HeaderLayoutRouteChildren { HeaderLayoutAdminLayoutRoute: typeof HeaderLayoutAdminLayoutRouteWithChildren HeaderLayoutNotificationsRoute: typeof HeaderLayoutNotificationsRoute HeaderLayoutSearchRoute: typeof HeaderLayoutSearchRoute - HeaderLayoutIndexRoute: typeof HeaderLayoutIndexRoute HeaderLayoutAppsTagRoute: typeof HeaderLayoutAppsTagRoute HeaderLayoutAppsAllRoute: typeof HeaderLayoutAppsAllRoute HeaderLayoutAppsLexiconRoute: typeof HeaderLayoutAppsLexiconRoute @@ -1141,7 +1144,6 @@ const HeaderLayoutRouteChildren: HeaderLayoutRouteChildren = { HeaderLayoutAdminLayoutRoute: HeaderLayoutAdminLayoutRouteWithChildren, HeaderLayoutNotificationsRoute: HeaderLayoutNotificationsRoute, HeaderLayoutSearchRoute: HeaderLayoutSearchRoute, - HeaderLayoutIndexRoute: HeaderLayoutIndexRoute, HeaderLayoutAppsTagRoute: HeaderLayoutAppsTagRoute, HeaderLayoutAppsAllRoute: HeaderLayoutAppsAllRoute, HeaderLayoutAppsLexiconRoute: HeaderLayoutAppsLexiconRoute, diff --git a/src/routes/_header-layout.index.tsx b/src/routes/$locale.index.tsx similarity index 89% rename from src/routes/_header-layout.index.tsx rename to src/routes/$locale.index.tsx index 2916c7d..41250e4 100644 --- a/src/routes/_header-layout.index.tsx +++ b/src/routes/$locale.index.tsx @@ -7,18 +7,23 @@ import { useNavigate, } from "@tanstack/react-router"; import { ChevronRight } from "lucide-react"; +import { Suspense } from "react"; +import { useTranslation } from "react-i18next"; import type { DirectoryListingCard } from "../integrations/tanstack-query/api-directory-listings.functions"; import { AppTagCard } from "../components/AppTagCard"; import { FeaturedListingGrid } from "../components/FeaturedListingGrid"; import { HeroImage } from "../components/HeroImage"; +import { SiteFooter } from "../components/SiteFooter"; +import { SiteHeader } from "../components/SiteHeader"; import { Alert } from "../design-system/alert"; import { Avatar } from "../design-system/avatar"; import { Button } from "../design-system/button"; import { Card, CardImage } from "../design-system/card"; import { Flex } from "../design-system/flex"; import { Grid } from "../design-system/grid"; +import { HeaderLayout } from "../design-system/header-layout"; import { Link } from "../design-system/link"; import { Page } from "../design-system/page"; import { @@ -41,13 +46,14 @@ import { SmallBody, } from "../design-system/typography"; import { Text } from "../design-system/typography/text"; +import { i18next } from "../i18n"; import { directoryListingApi } from "../integrations/tanstack-query/api-directory-listings.functions"; import { getDirectoryListingSlug } from "../lib/directory-listing-slugs"; import { getInitials } from "../lib/get-initials"; import { getDirectoryListingHeroImageAlt } from "../lib/listing-copy"; import { buildRouteOgMeta } from "../lib/og-meta"; -export const Route = createFileRoute("/_header-layout/")({ +export const Route = createFileRoute("/$locale/")({ loader: async ({ context }) => { await Promise.all([ context.queryClient.ensureQueryData( @@ -58,13 +64,14 @@ export const Route = createFileRoute("/_header-layout/")({ ), ]); }, - head: () => - buildRouteOgMeta({ - title: "at-store | Apps on the Atmosphere", - description: - "Discover apps and tools across the Atmosphere. Find your next favorite app today!", - }), - component: HomePage, + head: ({ params }) => { + const t = i18next.getFixedT(params.locale, "home"); + return buildRouteOgMeta({ + title: t("ogTitle"), + description: t("ogDescription"), + }); + }, + component: HomePageRoute, }); const AppLink = createLink(Link); @@ -102,16 +109,16 @@ const styles = stylex.create({ transitionTimingFunction: "ease-in-out", height: "100%", - ":hover::before": { - opacity: 1, - }, "::before": { inset: 0, borderRadius: radius.lg, cornerShape: "squircle", boxShadow: shadow.lg, content: "''", - opacity: 0, + opacity: { + default: 0, + ":hover": 1, + }, position: "absolute", transitionDuration: animationDuration.slow, transitionProperty: "opacity", @@ -165,16 +172,16 @@ const styles = stylex.create({ boxShadow: shadow.md, position: "relative", - ":hover::before": { - opacity: 1, - }, "::before": { inset: 0, borderRadius: radius.lg, cornerShape: "squircle", boxShadow: shadow.lg, content: "''", - opacity: 0, + opacity: { + default: 0, + ":hover": 1, + }, position: "absolute", transitionDuration: animationDuration.default, transitionProperty: "opacity", @@ -274,15 +281,15 @@ const styles = stylex.create({ paddingRight: horizontalSpace["2xl"], paddingTop: verticalSpace["2xl"], - ":hover::after": { - opacity: 1, - }, "::after": { inset: 0, borderRadius: radius.md, boxShadow: shadow.lg, content: "''", - opacity: 0, + opacity: { + default: 0, + ":hover": 1, + }, position: "absolute", transitionDuration: animationDuration.slow, transitionProperty: "opacity", @@ -341,7 +348,28 @@ const styles = stylex.create({ }, }); +function HomePageRoute() { + return ( + + + + + + + + + + + + + + + + ); +} + function HomePage() { + const { t } = useTranslation("home"); const navigate = useNavigate(); const { data } = useSuspenseQuery( directoryListingApi.getHomePageQueryOptions, @@ -361,31 +389,31 @@ function HomePage() { {showClaimBanner ? ( void navigate({ to: "/product/claim" })} > - Continue + {t("claimBanner.continue")} } > - {claimCount === 1 - ? `“${(claimEligibility.listings[0]?.name ?? "").trim() || "Listing"}” is still on the store repo. Claim it to manage updates from your PDS.` - : `You have ${String(claimCount)} listings on the store repo. Claim them to manage updates from your PDS.`} + {t("claimBanner.body", { + count: claimCount, + name: + (claimEligibility.listings[0]?.name ?? "").trim() || "Listing", + })} ) : null} - The AT Protocol app directory + {t("hero.eyebrow")} - Apps on the Atmosphere + {t("hero.title")} - Discover the best apps the Atmosphere has to offer. With every - product you own your data and use the same identity across all apps. + {t("hero.description")} @@ -420,8 +447,8 @@ function HomePage() {
@@ -433,8 +460,8 @@ function HomePage() {
@@ -459,8 +486,8 @@ function HomePage() {
@@ -492,13 +519,14 @@ type SectionHeaderProps = }; function SectionHeader({ eyebrow, title, to, search }: SectionHeaderProps) { + const { t } = useTranslation("home"); let action: React.ReactNode; switch (to) { case "/apps/all": { action = ( - See All + {t("sectionHeader.seeAll")} ); break; @@ -506,7 +534,7 @@ function SectionHeader({ eyebrow, title, to, search }: SectionHeaderProps) { case "/apps/tags": { action = ( - See All + {t("sectionHeader.seeAll")} ); break; @@ -582,6 +610,7 @@ function PopularListItem({ listing: DirectoryListingCard; rank: number; }) { + const { t } = useTranslation("home"); return ( {listing.tagline} ); diff --git a/src/routes/$locale.tsx b/src/routes/$locale.tsx index ef2065b..252c4b5 100644 --- a/src/routes/$locale.tsx +++ b/src/routes/$locale.tsx @@ -31,17 +31,6 @@ export const Route = createFileRoute("/$locale")({ href: restOfPath + location.searchStr, }); } - - // Temporary to show how route-based locales will work. once /home route is - // migrated with $locale prefix (next followup PR) we can remove this. - - // Bare `/` has no index child yet — bounce to the canonical - if (location.pathname === `/${params.locale}`) { - throw redirect({ - to: "/$locale/about", - params: { locale: params.locale }, - }); - } }, component: LocaleRoute, }); diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 54b0af5..e77f540 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -51,7 +51,7 @@ const detectLocale = createServerFn({ method: "GET" }).handler( * exists — so we redirect to the locale-prefixed version. Append to this * list as more surfaces are migrated. */ -const MIGRATED_TOP_LEVEL_PATHS: ReadonlySet = new Set(["/about"]); +const MIGRATED_TOP_LEVEL_PATHS: ReadonlySet = new Set(["/about", "/"]); const primaryColorTheme = stylex.createTheme(primaryColor, { bg: blue.bg,