diff --git a/app/components/AskOrganizer/AskOrganizer.container.tsx b/app/components/AskOrganizer/AskOrganizer.container.tsx index 6614bac..d9c6ac5 100644 --- a/app/components/AskOrganizer/AskOrganizer.container.tsx +++ b/app/components/AskOrganizer/AskOrganizer.container.tsx @@ -1,6 +1,7 @@ "use client"; import { memo } from "react"; +import { useTranslation } from "../../contexts/MessagesContext"; import { useAnalytics } from "../../hooks"; import AskOrganizerView from "./AskOrganizer.view"; import type { @@ -35,12 +36,15 @@ const AskOrganizerContainer = memo( title, subtitle, description, - buttonText = "Ask an organizer", - buttonHref = "#", + buttonText, + buttonHref, className = "", variant = "centered", onContactClick, }) => { + const t = useTranslation(); + const defaultButtonText = buttonText ?? t("askOrganizer.buttonText"); + const defaultButtonHref = buttonHref ?? t("askOrganizer.buttonHref"); const { trackEvent, trackCustomEvent } = useAnalytics(); const resolvedVariant: AskOrganizerVariant = variant ?? "centered"; @@ -74,8 +78,8 @@ const AskOrganizerContainer = memo( { component: "AskOrganizer", variant: resolvedVariant, - buttonText, - buttonHref, + buttonText: defaultButtonText, + buttonHref: defaultButtonHref, }, onContactClick as | ((_data: Record) => void) @@ -92,8 +96,8 @@ const AskOrganizerContainer = memo( title={title} subtitle={subtitle} description={description} - buttonText={buttonText} - buttonHref={buttonHref} + buttonText={defaultButtonText} + buttonHref={defaultButtonHref} className={className} sectionPadding={sectionPadding} contentGap={`${contentGap} ${styles.container}`} diff --git a/app/components/AskOrganizer/AskOrganizer.view.tsx b/app/components/AskOrganizer/AskOrganizer.view.tsx index c16d816..a1f2c28 100644 --- a/app/components/AskOrganizer/AskOrganizer.view.tsx +++ b/app/components/AskOrganizer/AskOrganizer.view.tsx @@ -1,3 +1,6 @@ +"use client"; + +import { useTranslation } from "../../contexts/MessagesContext"; import ContentLockup from "../ContentLockup"; import Button from "../Button"; import type { AskOrganizerViewProps } from "./AskOrganizer.types"; @@ -16,11 +19,14 @@ function AskOrganizerView({ labelledBy, onContactClick, }: AskOrganizerViewProps) { + const t = useTranslation(); + const ariaLabel = t("askOrganizer.ariaLabel"); + return (
@@ -42,7 +48,7 @@ function AskOrganizerView({ variant={variant === "inverse" ? "primary" : "default"} className="xl:!px-[var(--spacing-scale-020)] xl:!py-[var(--spacing-scale-012)] xl:!text-[24px] xl:!leading-[28px]" onClick={onContactClick} - ariaLabel={`${buttonText} - Contact an organizer for help`} + ariaLabel={ariaLabel} > {buttonText} diff --git a/app/components/FeatureGrid/FeatureGrid.container.tsx b/app/components/FeatureGrid/FeatureGrid.container.tsx index 1ef87a8..967ad3e 100644 --- a/app/components/FeatureGrid/FeatureGrid.container.tsx +++ b/app/components/FeatureGrid/FeatureGrid.container.tsx @@ -1,47 +1,66 @@ "use client"; import { memo, useMemo } from "react"; +import { useTranslation } from "../../contexts/MessagesContext"; import FeatureGridView from "./FeatureGrid.view"; import type { FeatureGridProps, Feature } from "./FeatureGrid.types"; const FeatureGridContainer = memo( ({ title, subtitle, className = "" }) => { + const t = useTranslation(); + const features: Feature[] = useMemo( () => [ { backgroundColor: "bg-[var(--color-surface-default-brand-royal)]", - labelLine1: "Decision-making", - labelLine2: "support", + labelLine1: t( + "pages.home.featureGrid.features.decisionMaking.labelLine1", + ), + labelLine2: t( + "pages.home.featureGrid.features.decisionMaking.labelLine2", + ), panelContent: "/assets/Feature_Support.png", - ariaLabel: "Decision-making support tools", + ariaLabel: t("featureGrid.features.decisionMaking.ariaLabel"), href: "#decision-making", }, { backgroundColor: "bg-[#D1FFE2]", - labelLine1: "Values alignment", - labelLine2: "exercises", + labelLine1: t( + "pages.home.featureGrid.features.valuesAlignment.labelLine1", + ), + labelLine2: t( + "pages.home.featureGrid.features.valuesAlignment.labelLine2", + ), panelContent: "/assets/Feature_Exercises.png", - ariaLabel: "Values alignment exercises", + ariaLabel: t("featureGrid.features.valuesAlignment.ariaLabel"), href: "#values-alignment", }, { backgroundColor: "bg-[#F4CAFF]", - labelLine1: "Membership", - labelLine2: "guidance", + labelLine1: t( + "pages.home.featureGrid.features.membershipGuidance.labelLine1", + ), + labelLine2: t( + "pages.home.featureGrid.features.membershipGuidance.labelLine2", + ), panelContent: "/assets/Feature_Guidance.png", - ariaLabel: "Membership guidance resources", + ariaLabel: t("featureGrid.features.membershipGuidance.ariaLabel"), href: "#membership-guidance", }, { backgroundColor: "bg-[#CBDDFF]", - labelLine1: "Conflict resolution", - labelLine2: "tools", + labelLine1: t( + "pages.home.featureGrid.features.conflictResolution.labelLine1", + ), + labelLine2: t( + "pages.home.featureGrid.features.conflictResolution.labelLine2", + ), panelContent: "/assets/Feature_Tools.png", - ariaLabel: "Conflict resolution tools", + ariaLabel: t("featureGrid.features.conflictResolution.ariaLabel"), href: "#conflict-resolution", }, ], - [], + [t], ); const labelledBy = title ? "feature-grid-headline" : undefined; diff --git a/app/components/FeatureGrid/FeatureGrid.view.tsx b/app/components/FeatureGrid/FeatureGrid.view.tsx index 4429d93..318865d 100644 --- a/app/components/FeatureGrid/FeatureGrid.view.tsx +++ b/app/components/FeatureGrid/FeatureGrid.view.tsx @@ -1,3 +1,6 @@ +"use client"; + +import { useTranslation } from "../../contexts/MessagesContext"; import ContentLockup from "../ContentLockup"; import MiniCard from "../MiniCard"; import type { FeatureGridViewProps } from "./FeatureGrid.types"; @@ -9,11 +12,16 @@ function FeatureGridView({ features, labelledBy, }: FeatureGridViewProps) { + const t = useTranslation(); + const ariaLabel = t("featureGrid.ariaLabel"); + const linkText = t("featureGrid.linkText"); + const linkHref = t("featureGrid.linkHref"); + return (
@@ -23,8 +31,8 @@ function FeatureGridView({ title={title} subtitle={subtitle} variant="feature" - linkText="Learn more" - linkHref="#" + linkText={linkText} + linkHref={linkHref} titleId={labelledBy} />
diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index b2ea3d4..b6f9e37 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -1,21 +1,23 @@ +"use client"; + import { memo } from "react"; +import { useTranslation } from "../contexts/MessagesContext"; import Link from "next/link"; import Logo from "./Logo"; import Separator from "./Separator"; import { getAssetPath, ASSETS } from "../../lib/assetUtils"; const Footer = memo(() => { + const t = useTranslation("footer"); + // Schema markup for organization information const schemaData = { "@context": "https://schema.org", "@type": "Organization", - name: "Media Economies Design Lab", - email: "medlab@colorado.edu", - url: "https://communityrule.com", - sameAs: [ - "https://bsky.app/profile/medlabboulder", - "https://gitlab.com/medlabboulder", - ], + name: t("organization.name"), + email: t("organization.email"), + url: t("organization.url"), + sameAs: [t("social.bluesky.url"), t("social.gitlab.url")], }; return ( @@ -55,22 +57,22 @@ const Footer = memo(() => { {/* Contact info */}
- Media Economies Design Lab + {t("organization.name")}
- medlab@colorado.edu + {t("organization.email")}
{/* Social media links */} @@ -108,19 +110,19 @@ const Footer = memo(() => { href="#" className="text-[var(--color-content-default-primary)] font-inter text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer" > - Use cases + {t("navigation.useCases")} - Learn + {t("navigation.learn")} - About + {t("navigation.about")}
@@ -133,25 +135,25 @@ const Footer = memo(() => { href="#" className="text-[var(--color-content-default-secondary)] font-inter text-sm leading-5 font-normal tracking-[0%] lg:text-base lg:leading-6 hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer" > - Privacy Policy + {t("legal.privacyPolicy")} - Terms of Service + {t("legal.termsOfService")} - Cookies Settings + {t("legal.cookiesSettings")} {/* Copyright */}
- © All right reserved + {t("copyright")}
diff --git a/app/components/Header/Header.container.tsx b/app/components/Header/Header.container.tsx index 165f2bf..52f6990 100644 --- a/app/components/Header/Header.container.tsx +++ b/app/components/Header/Header.container.tsx @@ -2,6 +2,7 @@ import { memo } from "react"; import { usePathname } from "next/navigation"; +import { useTranslation } from "../../contexts/MessagesContext"; import MenuBarItem from "../MenuBarItem"; import Button from "../Button"; import AvatarContainer from "../AvatarContainer"; @@ -11,13 +12,6 @@ import { getAssetPath, ASSETS } from "../../../lib/assetUtils"; import { HeaderView } from "./Header.view"; import type { HeaderProps, NavSize } from "./Header.types"; -// Configuration data for testing -export const navigationItems = [ - { href: "#", text: "Use cases", extraPadding: true }, - { href: "/learn", text: "Learn" }, - { href: "#", text: "About" }, -]; - export const avatarImages = [ { src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" }, { src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" }, @@ -46,6 +40,7 @@ export const logoConfig = [ const HeaderContainer = memo(() => { const pathname = usePathname(); + const t = useTranslation("header"); // Schema markup for site navigation const schemaData = { @@ -60,6 +55,13 @@ const HeaderContainer = memo(() => { }, }; + // Navigation items with translations + const navigationItems = [ + { href: "#", text: t("navigation.useCases"), extraPadding: true }, + { href: "/learn", text: t("navigation.learn") }, + { href: "#", text: t("navigation.about") }, + ]; + const renderNavigationItems = (size: NavSize) => { return navigationItems.map((item, index) => ( (() => { href={item.href} size={item.extraPadding && size === "xsmall" ? "xsmallUseCases" : size} isActive={pathname === item.href} - ariaLabel={`Navigate to ${item.text} page`} + ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)} > {item.text} @@ -94,8 +96,12 @@ const HeaderContainer = memo(() => { const renderLoginButton = (size: NavSize) => { return ( - - Log in + + {t("buttons.logIn")} ); }; @@ -106,12 +112,9 @@ const HeaderContainer = memo(() => { avatarSize: "small" | "medium" | "large" | "xlarge", ) => { return ( - ); }; diff --git a/app/components/Header/Header.view.tsx b/app/components/Header/Header.view.tsx index 2ea7986..780c7b2 100644 --- a/app/components/Header/Header.view.tsx +++ b/app/components/Header/Header.view.tsx @@ -1,3 +1,6 @@ +"use client"; + +import { useTranslation } from "../../contexts/MessagesContext"; import MenuBar from "../MenuBar"; import type { HeaderViewProps } from "./Header.types"; @@ -9,6 +12,8 @@ export function HeaderView({ renderCreateRuleButton, renderLogo, }: HeaderViewProps) { + const t = useTranslation("header"); + return ( <>