Second pass on localization

This commit is contained in:
adilallo
2026-01-30 17:28:48 -07:00
parent 2f37031411
commit 14ec2dd2a0
17 changed files with 179 additions and 59 deletions
+14 -12
View File
@@ -2,6 +2,7 @@
import { memo } from "react"; import { memo } from "react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useTranslation } from "../../contexts/MessagesContext";
import MenuBarItem from "../MenuBarItem"; import MenuBarItem from "../MenuBarItem";
import Button from "../Button"; import Button from "../Button";
import AvatarContainer from "../AvatarContainer"; import AvatarContainer from "../AvatarContainer";
@@ -11,13 +12,6 @@ import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
import { HeaderView } from "./Header.view"; import { HeaderView } from "./Header.view";
import type { HeaderProps, NavSize } from "./Header.types"; 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 = [ export const avatarImages = [
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" }, { src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" }, { src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
@@ -46,6 +40,7 @@ export const logoConfig = [
const HeaderContainer = memo<HeaderProps>(() => { const HeaderContainer = memo<HeaderProps>(() => {
const pathname = usePathname(); const pathname = usePathname();
const t = useTranslation("header");
// Schema markup for site navigation // Schema markup for site navigation
const schemaData = { const schemaData = {
@@ -60,6 +55,13 @@ const HeaderContainer = memo<HeaderProps>(() => {
}, },
}; };
// 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) => { const renderNavigationItems = (size: NavSize) => {
return navigationItems.map((item, index) => ( return navigationItems.map((item, index) => (
<MenuBarItem <MenuBarItem
@@ -67,7 +69,7 @@ const HeaderContainer = memo<HeaderProps>(() => {
href={item.href} href={item.href}
size={item.extraPadding && size === "xsmall" ? "xsmallUseCases" : size} size={item.extraPadding && size === "xsmall" ? "xsmallUseCases" : size}
isActive={pathname === item.href} isActive={pathname === item.href}
ariaLabel={`Navigate to ${item.text} page`} ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)}
> >
{item.text} {item.text}
</MenuBarItem> </MenuBarItem>
@@ -94,8 +96,8 @@ const HeaderContainer = memo<HeaderProps>(() => {
const renderLoginButton = (size: NavSize) => { const renderLoginButton = (size: NavSize) => {
return ( return (
<MenuBarItem href="#" size={size} ariaLabel="Log in to your account"> <MenuBarItem href="#" size={size} ariaLabel={t("ariaLabels.logInToAccount")}>
Log in {t("buttons.logIn")}
</MenuBarItem> </MenuBarItem>
); );
}; };
@@ -108,10 +110,10 @@ const HeaderContainer = memo<HeaderProps>(() => {
return ( return (
<Button <Button
size={buttonSize} size={buttonSize}
ariaLabel="Create a new rule with avatar decoration" ariaLabel={t("ariaLabels.createNewRule")}
> >
{renderAvatarGroup(containerSize, avatarSize)} {renderAvatarGroup(containerSize, avatarSize)}
<span>Create rule</span> <span>{t("buttons.createRule")}</span>
</Button> </Button>
); );
}; };
+7 -2
View File
@@ -1,3 +1,6 @@
"use client";
import { useTranslation } from "../../contexts/MessagesContext";
import MenuBar from "../MenuBar"; import MenuBar from "../MenuBar";
import type { HeaderViewProps } from "./Header.types"; import type { HeaderViewProps } from "./Header.types";
@@ -9,6 +12,8 @@ export function HeaderView({
renderCreateRuleButton, renderCreateRuleButton,
renderLogo, renderLogo,
}: HeaderViewProps) { }: HeaderViewProps) {
const t = useTranslation("header");
return ( return (
<> <>
<script <script
@@ -18,12 +23,12 @@ export function HeaderView({
<header <header
className="sticky top-0 z-50 bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]" className="sticky top-0 z-50 bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]"
role="banner" role="banner"
aria-label="Main navigation header" aria-label={t("ariaLabels.mainNavigationHeader")}
> >
<nav <nav
className="flex items-center justify-between mx-auto h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]" className="flex items-center justify-between mx-auto h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
role="navigation" role="navigation"
aria-label="Main navigation" aria-label={t("ariaLabels.mainNavigation")}
> >
{/* Logo - Consistent left positioning across all breakpoints */} {/* Logo - Consistent left positioning across all breakpoints */}
<div className="flex items-center"> <div className="flex items-center">
@@ -2,6 +2,7 @@
import { memo } from "react"; import { memo } from "react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useTranslation } from "../../contexts/MessagesContext";
import MenuBarItem from "../MenuBarItem"; import MenuBarItem from "../MenuBarItem";
import Button from "../Button"; import Button from "../Button";
import AvatarContainer from "../AvatarContainer"; import AvatarContainer from "../AvatarContainer";
@@ -11,13 +12,6 @@ import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
import HomeHeaderView from "./HomeHeader.view"; import HomeHeaderView from "./HomeHeader.view";
import type { HomeHeaderProps, NavSize } from "./HomeHeader.types"; import type { HomeHeaderProps, NavSize } from "./HomeHeader.types";
// Configuration data for testing
export const navigationItems = [
{ href: "#", text: "Use cases", extraPadding: true },
{ href: "/learn", text: "Learn" },
{ href: "#", text: "About" },
];
export const avatarImages = [ export const avatarImages = [
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" }, { src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" }, { src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
@@ -54,6 +48,7 @@ export const logoConfig = [
const HomeHeaderContainer = memo<HomeHeaderProps>(() => { const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
const pathname = usePathname(); const pathname = usePathname();
const t = useTranslation("header");
// Schema markup for site navigation (home page specific) // Schema markup for site navigation (home page specific)
const schemaData = { const schemaData = {
@@ -69,6 +64,13 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
}, },
}; };
// 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) => { const renderNavigationItems = (size: NavSize) => {
return navigationItems.map((item, index) => ( return navigationItems.map((item, index) => (
<MenuBarItem <MenuBarItem
@@ -102,7 +104,7 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
: "default" : "default"
} }
isActive={pathname === item.href} isActive={pathname === item.href}
ariaLabel={`Navigate to ${item.text} page`} ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)}
> >
{item.text} {item.text}
</MenuBarItem> </MenuBarItem>
@@ -133,9 +135,9 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
href="#" href="#"
size={size} size={size}
variant={size === "xsmall" || size === "default" ? "home" : "default"} variant={size === "xsmall" || size === "default" ? "home" : "default"}
ariaLabel="Log in to your account" ariaLabel={t("ariaLabels.logInToAccount")}
> >
Log in {t("buttons.logIn")}
</MenuBarItem> </MenuBarItem>
); );
}; };
@@ -149,10 +151,10 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
<Button <Button
size={buttonSize} size={buttonSize}
variant="secondary" variant="secondary"
ariaLabel="Create a new rule with avatar decoration" ariaLabel={t("ariaLabels.createNewRule")}
> >
{renderAvatarGroup(containerSize, avatarSize)} {renderAvatarGroup(containerSize, avatarSize)}
<span>Create rule</span> <span>{t("buttons.createRule")}</span>
</Button> </Button>
); );
}; };
@@ -2,6 +2,7 @@
import { memo } from "react"; import { memo } from "react";
import Script from "next/script"; import Script from "next/script";
import { useTranslation } from "../../contexts/MessagesContext";
import HeaderTab from "../HeaderTab"; import HeaderTab from "../HeaderTab";
import MenuBar from "../MenuBar"; import MenuBar from "../MenuBar";
import type { HomeHeaderViewProps } from "./HomeHeader.types"; import type { HomeHeaderViewProps } from "./HomeHeader.types";
@@ -14,6 +15,8 @@ function HomeHeaderView({
renderCreateRuleButton, renderCreateRuleButton,
renderLogo, renderLogo,
}: HomeHeaderViewProps) { }: HomeHeaderViewProps) {
const t = useTranslation("homeHeader");
return ( return (
<> <>
<Script <Script
@@ -24,12 +27,12 @@ function HomeHeaderView({
<header <header
className="w-full bg-transparent overflow-hidden" className="w-full bg-transparent overflow-hidden"
role="banner" role="banner"
aria-label="Home page navigation header" aria-label={t("ariaLabels.homePageNavigationHeader")}
> >
<nav <nav
className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]" className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
role="navigation" role="navigation"
aria-label="Main navigation" aria-label={t("ariaLabels.mainNavigation")}
> >
<HeaderTab className="flex items-center self-end" stretch={true}> <HeaderTab className="flex items-center self-end" stretch={true}>
{/* Logo - Consistent left positioning within HeaderTab */} {/* Logo - Consistent left positioning within HeaderTab */}
@@ -1,26 +1,29 @@
"use client"; "use client";
import { memo } from "react"; import { memo } from "react";
import { useTranslation } from "../../contexts/MessagesContext";
import type { LanguageSwitcherProps, Language } from "./LanguageSwitcher.types"; import type { LanguageSwitcherProps, Language } from "./LanguageSwitcher.types";
const AVAILABLE_LANGUAGES: Language[] = [
{
code: "en",
name: "English",
nativeName: "English",
},
];
function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) { function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) {
const t = useTranslation("languageSwitcher");
const AVAILABLE_LANGUAGES: Language[] = [
{
code: "en",
name: t("languages.english.name"),
nativeName: t("languages.english.nativeName"),
},
];
return ( return (
<div className={className}> <div className={className}>
<label htmlFor="language-select" className="sr-only"> <label htmlFor="language-select" className="sr-only">
Select language {t("label")}
</label> </label>
<select <select
id="language-select" id="language-select"
className="bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] font-inter text-sm leading-5 font-normal border border-[var(--color-surface-default-secondary)] rounded-[var(--radius-measures-radius-small)] px-[var(--spacing-scale-012)] py-[var(--spacing-scale-008)] focus:outline-none focus:ring-2 focus:ring-[var(--color-surface-default-brand-royal)] focus:ring-offset-2 cursor-pointer" className="bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] font-inter text-sm leading-5 font-normal border border-[var(--color-surface-default-secondary)] rounded-[var(--radius-measures-radius-small)] px-[var(--spacing-scale-012)] py-[var(--spacing-scale-008)] focus:outline-none focus:ring-2 focus:ring-[var(--color-surface-default-brand-royal)] focus:ring-offset-2 cursor-pointer"
aria-label="Select language" aria-label={t("ariaLabel")}
disabled disabled
> >
{AVAILABLE_LANGUAGES.map((language) => ( {AVAILABLE_LANGUAGES.map((language) => (
@@ -30,7 +33,7 @@ function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) {
))} ))}
</select> </select>
<p className="text-[var(--color-content-default-secondary)] font-inter text-xs leading-4 font-normal mt-[var(--spacing-scale-008)]"> <p className="text-[var(--color-content-default-secondary)] font-inter text-xs leading-4 font-normal mt-[var(--spacing-scale-008)]">
Language switching functionality coming soon {t("comingSoonMessage")}
</p> </p>
</div> </div>
); );
+5 -1
View File
@@ -1,4 +1,7 @@
"use client";
import { memo } from "react"; import { memo } from "react";
import { useTranslation } from "../contexts/MessagesContext";
interface MenuBarProps extends React.HTMLAttributes<HTMLElement> { interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
children?: React.ReactNode; children?: React.ReactNode;
@@ -8,6 +11,7 @@ interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
const MenuBar = memo<MenuBarProps>( const MenuBar = memo<MenuBarProps>(
({ children, className = "", size = "default", ...props }) => { ({ children, className = "", size = "default", ...props }) => {
const t = useTranslation("menuBar");
const sizeStyles: Record<string, string> = { const sizeStyles: Record<string, string> = {
xsmall: xsmall:
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[4px]", "px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[4px]",
@@ -25,7 +29,7 @@ const MenuBar = memo<MenuBarProps>(
<nav <nav
className={baseStyles} className={baseStyles}
role="menubar" role="menubar"
aria-label="Main navigation menu" aria-label={t("ariaLabel")}
{...props} {...props}
> >
{children} {children}
@@ -2,6 +2,7 @@
import { memo } from "react"; import { memo } from "react";
import Image from "next/image"; import Image from "next/image";
import { useTranslation } from "../../contexts/MessagesContext";
import QuoteDecor from "../QuoteDecor"; import QuoteDecor from "../QuoteDecor";
import type { QuoteBlockViewProps } from "./QuoteBlock.types"; import type { QuoteBlockViewProps } from "./QuoteBlock.types";
@@ -19,6 +20,9 @@ function QuoteBlockView({
onImageLoad, onImageLoad,
onImageError, onImageError,
}: QuoteBlockViewProps) { }: QuoteBlockViewProps) {
const t = useTranslation("quoteBlock");
const avatarAlt = t("avatarAlt").replace("{author}", author);
return ( return (
<section <section
className={`${config.container} ${className}`} className={`${config.container} ${className}`}
@@ -54,7 +58,7 @@ function QuoteBlockView({
{!imageError ? ( {!imageError ? (
<Image <Image
src={currentAvatarSrc} src={currentAvatarSrc}
alt={`Portrait of ${author}`} alt={avatarAlt}
width={64} width={64}
height={64} height={64}
className={`filter sepia ${ className={`filter sepia ${
+7 -1
View File
@@ -1,3 +1,6 @@
"use client";
import { useTranslation } from "../../contexts/MessagesContext";
import type { RuleCardViewProps } from "./RuleCard.types"; import type { RuleCardViewProps } from "./RuleCard.types";
export function RuleCardView({ export function RuleCardView({
@@ -9,12 +12,15 @@ export function RuleCardView({
onClick, onClick,
onKeyDown, onKeyDown,
}: RuleCardViewProps) { }: RuleCardViewProps) {
const t = useTranslation("ruleCard");
const ariaLabel = t("ariaLabel").replace("{title}", title);
return ( return (
<div <div
className={`${backgroundColor} rounded-[var(--radius-measures-radius-small)] pt-[var(--spacing-scale-012)] pr-[var(--spacing-scale-012)] pl-[var(--spacing-scale-012)] pb-[var(--spacing-scale-024)] md:p-[var(--spacing-scale-024)] md:h-[210px] lg:h-[277px] flex flex-col gap-[18px] shadow-lg backdrop-blur-sm transition-all duration-500 ease-in-out hover:shadow-xl hover:scale-[1.02] focus:outline-none focus:ring-2 focus:ring-[var(--color-community-teal-500)] focus:ring-offset-2 cursor-pointer min-h-[44px] min-w-[44px] ${className}`} className={`${backgroundColor} rounded-[var(--radius-measures-radius-small)] pt-[var(--spacing-scale-012)] pr-[var(--spacing-scale-012)] pl-[var(--spacing-scale-012)] pb-[var(--spacing-scale-024)] md:p-[var(--spacing-scale-024)] md:h-[210px] lg:h-[277px] flex flex-col gap-[18px] shadow-lg backdrop-blur-sm transition-all duration-500 ease-in-out hover:shadow-xl hover:scale-[1.02] focus:outline-none focus:ring-2 focus:ring-[var(--color-community-teal-500)] focus:ring-offset-2 cursor-pointer min-h-[44px] min-w-[44px] ${className}`}
tabIndex={0} tabIndex={0}
role="button" role="button"
aria-label={`Learn more about ${title} governance pattern`} aria-label={ariaLabel}
onClick={onClick} onClick={onClick}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
> >
+22 -17
View File
@@ -1,4 +1,7 @@
"use client";
import Image from "next/image"; import Image from "next/image";
import { useTranslation } from "../../contexts/MessagesContext";
import RuleCard from "../RuleCard"; import RuleCard from "../RuleCard";
import Button from "../Button"; import Button from "../Button";
import { getAssetPath } from "../../../lib/assetUtils"; import { getAssetPath } from "../../../lib/assetUtils";
@@ -8,77 +11,79 @@ export function RuleStackView({
className, className,
onTemplateClick, onTemplateClick,
}: RuleStackViewProps) { }: RuleStackViewProps) {
const t = useTranslation("ruleStack");
return ( return (
<section <section
className={`w-full bg-transparent py-[var(--spacing-scale-032)] px-[var(--spacing-scale-020)] md:py-[var(--spacing-scale-048)] md:px-[var(--spacing-scale-032)] xmd:py-[var(--spacing-scale-056)] xmd:px-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)] lg:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] xl:px-[var(--spacing-scale-096)] flex flex-col gap-[var(--spacing-scale-024)] xmd:gap-[var(--spacing-scale-032)] lg:gap-[var(--spacing-scale-040)] ${className}`} className={`w-full bg-transparent py-[var(--spacing-scale-032)] px-[var(--spacing-scale-020)] md:py-[var(--spacing-scale-048)] md:px-[var(--spacing-scale-032)] xmd:py-[var(--spacing-scale-056)] xmd:px-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)] lg:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] xl:px-[var(--spacing-scale-096)] flex flex-col gap-[var(--spacing-scale-024)] xmd:gap-[var(--spacing-scale-032)] lg:gap-[var(--spacing-scale-040)] ${className}`}
> >
<div className="flex flex-col gap-[18px] xmd:grid xmd:grid-cols-2 lg:gap-[var(--spacing-scale-024)]"> <div className="flex flex-col gap-[18px] xmd:grid xmd:grid-cols-2 lg:gap-[var(--spacing-scale-024)]">
<RuleCard <RuleCard
title="Consensus clusters" title={t("cards.consensusClusters.title")}
description="Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council." description={t("cards.consensusClusters.description")}
icon={ icon={
<Image <Image
src={getAssetPath("assets/Icon_Sociocracy.svg")} src={getAssetPath("assets/Icon_Sociocracy.svg")}
alt="Sociocracy" alt={t("cards.consensusClusters.iconAlt")}
width={40} width={40}
height={40} height={40}
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]" className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
/> />
} }
backgroundColor="bg-[var(--color-surface-default-brand-lime)]" backgroundColor="bg-[var(--color-surface-default-brand-lime)]"
onClick={() => onTemplateClick("Consensus clusters")} onClick={() => onTemplateClick(t("cards.consensusClusters.title"))}
/> />
<RuleCard <RuleCard
title="Consensus" title={t("cards.consensus.title")}
description="Decisions that affect the group collectively should involve participation of all participants." description={t("cards.consensus.description")}
icon={ icon={
<Image <Image
src={getAssetPath("assets/Icon_Consensus.svg")} src={getAssetPath("assets/Icon_Consensus.svg")}
alt="Consensus" alt={t("cards.consensus.iconAlt")}
width={40} width={40}
height={40} height={40}
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]" className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
/> />
} }
backgroundColor="bg-[var(--color-surface-default-brand-rust)]" backgroundColor="bg-[var(--color-surface-default-brand-rust)]"
onClick={() => onTemplateClick("Consensus")} onClick={() => onTemplateClick(t("cards.consensus.title"))}
/> />
<RuleCard <RuleCard
title="Elected Board" title={t("cards.electedBoard.title")}
description="An elected board determines policies and organizes their implementation." description={t("cards.electedBoard.description")}
icon={ icon={
<Image <Image
src={getAssetPath("assets/Icon_ElectedBoard.svg")} src={getAssetPath("assets/Icon_ElectedBoard.svg")}
alt="Elected Board" alt={t("cards.electedBoard.iconAlt")}
width={40} width={40}
height={40} height={40}
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]" className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
/> />
} }
backgroundColor="bg-[var(--color-surface-default-brand-red)]" backgroundColor="bg-[var(--color-surface-default-brand-red)]"
onClick={() => onTemplateClick("Elected Board")} onClick={() => onTemplateClick(t("cards.electedBoard.title"))}
/> />
<RuleCard <RuleCard
title="Petition" title={t("cards.petition.title")}
description="All participants can propose and vote on proposals for the group." description={t("cards.petition.description")}
icon={ icon={
<Image <Image
src={getAssetPath("assets/Icon_Petition.svg")} src={getAssetPath("assets/Icon_Petition.svg")}
alt="Petition" alt={t("cards.petition.iconAlt")}
width={40} width={40}
height={40} height={40}
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]" className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
/> />
} }
backgroundColor="bg-[var(--color-surface-default-brand-teal)]" backgroundColor="bg-[var(--color-surface-default-brand-teal)]"
onClick={() => onTemplateClick("Petition")} onClick={() => onTemplateClick(t("cards.petition.title"))}
/> />
</div> </div>
{/* See all templates button */} {/* See all templates button */}
<div className="flex justify-center"> <div className="flex justify-center">
<Button variant="outlined" size="large"> <Button variant="outlined" size="large">
See all templates {t("button.seeAllTemplates")}
</Button> </Button>
</div> </div>
</section> </section>
+18
View File
@@ -0,0 +1,18 @@
{
"navigation": {
"useCases": "Use cases",
"learn": "Learn",
"about": "About"
},
"buttons": {
"logIn": "Log in",
"createRule": "Create rule"
},
"ariaLabels": {
"mainNavigationHeader": "Main navigation header",
"mainNavigation": "Main navigation",
"navigateToPage": "Navigate to {text} page",
"logInToAccount": "Log in to your account",
"createNewRule": "Create a new rule with avatar decoration"
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"ariaLabels": {
"homePageNavigationHeader": "Home page navigation header",
"mainNavigation": "Main navigation"
}
}
@@ -0,0 +1,11 @@
{
"label": "Select language",
"ariaLabel": "Select language",
"comingSoonMessage": "Language switching functionality coming soon",
"languages": {
"english": {
"name": "English",
"nativeName": "English"
}
}
}
+3
View File
@@ -0,0 +1,3 @@
{
"ariaLabel": "Main navigation menu"
}
+3
View File
@@ -0,0 +1,3 @@
{
"avatarAlt": "Portrait of {author}"
}
+3
View File
@@ -0,0 +1,3 @@
{
"ariaLabel": "Learn more about {title} governance pattern"
}
+28
View File
@@ -0,0 +1,28 @@
{
"cards": {
"consensusClusters": {
"title": "Consensus clusters",
"description": "Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council.",
"iconAlt": "Sociocracy"
},
"consensus": {
"title": "Consensus",
"description": "Decisions that affect the group collectively should involve participation of all participants.",
"iconAlt": "Consensus"
},
"electedBoard": {
"title": "Elected Board",
"description": "An elected board determines policies and organizes their implementation.",
"iconAlt": "Elected Board"
},
"petition": {
"title": "Petition",
"description": "All participants can propose and vote on proposals for the group.",
"iconAlt": "Petition"
}
},
"button": {
"seeAllTemplates": "See all templates"
},
"ariaLabel": "Learn more about {title} governance pattern"
}
+14
View File
@@ -4,6 +4,13 @@ import numberedCards from "./components/numberedCards.json";
import askOrganizer from "./components/askOrganizer.json"; import askOrganizer from "./components/askOrganizer.json";
import featureGrid from "./components/featureGrid.json"; import featureGrid from "./components/featureGrid.json";
import footer from "./components/footer.json"; import footer from "./components/footer.json";
import header from "./components/header.json";
import homeHeader from "./components/homeHeader.json";
import languageSwitcher from "./components/languageSwitcher.json";
import menuBar from "./components/menuBar.json";
import quoteBlock from "./components/quoteBlock.json";
import ruleCard from "./components/ruleCard.json";
import ruleStack from "./components/ruleStack.json";
import navigation from "./navigation.json"; import navigation from "./navigation.json";
import metadata from "./metadata.json"; import metadata from "./metadata.json";
@@ -14,6 +21,13 @@ export default {
askOrganizer, askOrganizer,
featureGrid, featureGrid,
footer, footer,
header,
homeHeader,
languageSwitcher,
menuBar,
quoteBlock,
ruleCard,
ruleStack,
navigation, navigation,
metadata, metadata,
}; };