feat: internationalize marketing configuration and components

- Add translation support for menu links, footer links, and avatar links
- Create `createTranslator` utility function for dynamic translations
- Update marketing configuration to use translation-based menu and link generation
- Modify navbar, footer, and user button components to use translated content
- Add new "Common" translation keys for login and signup buttons
- Refactor marketing configuration to dynamically generate links with translations
- Improve internationalization across marketing-related components
This commit is contained in:
javayhu 2025-03-08 18:08:13 +08:00
parent f4e51757e5
commit 8d8b66a7fe
10 changed files with 470 additions and 194 deletions

View File

@ -1,4 +1,8 @@
{
"Common": {
"login": "Log in",
"signUp": "Sign up"
},
"HomePage": {
"title": "next-intl example"
},
@ -19,7 +23,7 @@
"email": "Email",
"password": "Password",
"signIn": "Sign In",
"signUp": "Don't have an account? Sign up",
"signUpHint": "Don't have an account? Sign up",
"forgotPassword": "Forgot Password?",
"signInWithGoogle": "Sign In with Google",
"signInWithGitHub": "Sign In with GitHub",
@ -35,7 +39,7 @@
"email": "Email",
"password": "Password",
"signUp": "Sign Up",
"signIn": "Already have an account? Sign in",
"signInHint": "Already have an account? Sign in",
"checkEmail": "Please check your email inbox"
},
"forgotPassword": {
@ -68,6 +72,115 @@
"noPostsFound": "No posts found",
"allPosts": "All Posts"
},
"Marketing": {
"menu": {
"features": {
"title": "Features"
},
"pricing": {
"title": "Pricing"
},
"blog": {
"title": "Blog"
},
"ai": {
"title": "AI",
"items": {
"text": {
"title": "AI Text",
"description": "Show how to use AI to write stunning text"
},
"image": {
"title": "AI Image",
"description": "Show how to use AI to generate beautiful images"
},
"video": {
"title": "AI Video",
"description": "Show how to use AI to generate amazing videos"
},
"audio": {
"title": "AI Audio",
"description": "Show how to use AI to generate wonderful audio"
}
}
},
"pages": {
"title": "Pages",
"items": {
"about": {
"title": "About",
"description": "Learn more about our company, mission, and values"
},
"contact": {
"title": "Contact",
"description": "Get in touch with our team for support or inquiries"
},
"waitlist": {
"title": "Waitlist",
"description": "Join our waitlist for latest news and updates"
},
"changelog": {
"title": "Changelog",
"description": "See the latest updates to our products"
},
"roadmap": {
"title": "Roadmap",
"description": "Explore our future plans and upcoming features"
},
"cookiePolicy": {
"title": "Cookie Policy",
"description": "Information about how we use cookies on our website"
},
"privacyPolicy": {
"title": "Privacy Policy",
"description": "Details about how we protect and handle your data"
},
"termsOfService": {
"title": "Terms of Service",
"description": "The legal agreement between you and our company"
}
}
}
},
"footer": {
"product": {
"title": "Product",
"items": {
"features": "Features",
"pricing": "Pricing",
"faq": "FAQ"
}
},
"resources": {
"title": "Resources",
"items": {
"blog": "Blog",
"changelog": "Changelog",
"roadmap": "Roadmap"
}
},
"company": {
"title": "Company",
"items": {
"about": "About",
"contact": "Contact",
"waitlist": "Waitlist"
}
},
"legal": {
"title": "Legal",
"items": {
"cookiePolicy": "Cookie Policy",
"privacyPolicy": "Privacy Policy",
"termsOfService": "Terms of Service"
}
}
},
"avatar": {
"dashboard": "Dashboard",
"settings": "Settings"
}
},
"mail": {
"common": {
"team": "{name} Team",

View File

@ -1,4 +1,8 @@
{
"Common": {
"login": "登录",
"signUp": "注册"
},
"HomePage": {
"title": "next-intl 示例"
},
@ -19,7 +23,7 @@
"email": "邮箱",
"password": "密码",
"signIn": "登录",
"signUp": "没有账号?注册",
"signUpHint": "没有账号?注册",
"forgotPassword": "忘记密码?",
"signInWithGoogle": "使用 Google 登录",
"signInWithGitHub": "使用 GitHub 登录",
@ -35,7 +39,7 @@
"email": "邮箱",
"password": "密码",
"signUp": "注册",
"signIn": "已经有账号?登录",
"signInHint": "已经有账号?登录",
"checkEmail": "请检查您的邮箱"
},
"forgotPassword": {
@ -68,10 +72,120 @@
"noPostsFound": "没有找到文章",
"allPosts": "全部文章"
},
"Marketing": {
"menu": {
"features": {
"title": "功能"
},
"pricing": {
"title": "价格"
},
"blog": {
"title": "博客"
},
"ai": {
"title": "人工智能",
"items": {
"text": {
"title": "AI 文本",
"description": "展示如何使用 AI 生成精彩文本"
},
"image": {
"title": "AI 图像",
"description": "展示如何使用 AI 生成精美图像"
},
"video": {
"title": "AI 视频",
"description": "展示如何使用 AI 生成惊人视频"
},
"audio": {
"title": "AI 音频",
"description": "展示如何使用 AI 生成动听音频"
}
}
},
"pages": {
"title": "页面",
"items": {
"about": {
"title": "关于我们",
"description": "了解更多关于我们的公司、使命和价值观"
},
"contact": {
"title": "联系我们",
"description": "与我们的团队联系,以获取支持或咨询"
},
"waitlist": {
"title": "邮件列表",
"description": "加入我们的邮件列表,获取最新消息和更新"
},
"changelog": {
"title": "更新日志",
"description": "查看我们产品的更新历史,查看最新动态"
},
"roadmap": {
"title": "路线图",
"description": "探索我们的未来计划和即将推出的功能"
},
"cookiePolicy": {
"title": "Cookie 政策",
"description": "关于我们如何在网站上使用 Cookie 的信息"
},
"privacyPolicy": {
"title": "隐私政策",
"description": "关于我们将如何保护和处理您在网站上的数据"
},
"termsOfService": {
"title": "服务条款",
"description": "关于您与我们公司之间的法律协议和条款"
}
}
}
},
"footer": {
"product": {
"title": "产品",
"items": {
"features": "功能",
"pricing": "价格",
"faq": "常见问题"
}
},
"resources": {
"title": "资源",
"items": {
"blog": "博客",
"changelog": "更新日志",
"roadmap": "路线图"
}
},
"company": {
"title": "公司",
"items": {
"about": "关于我们",
"contact": "联系我们",
"waitlist": "邮件列表"
}
},
"legal": {
"title": "法律",
"items": {
"cookiePolicy": "Cookie 政策",
"privacyPolicy": "隐私政策",
"termsOfService": "服务条款"
}
}
},
"avatar": {
"dashboard": "工作台",
"settings": "设置",
"logout": "退出"
}
},
"mail": {
"common": {
"team": "{name} 团队",
"copyright": "版权所有 {year}"
"copyright": "版权所有 {year} All Rights Reserved."
},
"verifyEmail": {
"title": "你好, {name}.",

View File

@ -27,19 +27,19 @@ export default async function HomePage(props: HomePageProps) {
{/* <Features /> */}
<FeaturesSection />
{/* <FeaturesSection /> */}
<ContentSection />
{/* <ContentSection /> */}
<Pricing />
<FAQs />
{/* <FAQs />
<WallOfLoveSection />
<WallOfLoveSection /> */}
<StatsSection />
{/* <StatsSection />
<CallToAction />
<CallToAction /> */}
</div>
</>
);

View File

@ -78,7 +78,7 @@ export const LoginForm = ({ className }: { className?: string }) => {
return (
<AuthCard
headerLabel={t("welcomeBack")}
bottomButtonLabel={t("signUp")}
bottomButtonLabel={t("signUpHint")}
bottomButtonHref={`${Routes.Register}`}
showSocialLoginButton
className={cn("border-none", className)}

View File

@ -79,7 +79,7 @@ export const RegisterForm = () => {
return (
<AuthCard
headerLabel={t("createAccount")}
bottomButtonLabel={t("signIn")}
bottomButtonLabel={t("signInHint")}
bottomButtonHref={`${Routes.Login}`}
showSocialLoginButton
className="border-none"

View File

@ -4,13 +4,18 @@ import Container from "@/components/container";
import { ThemeSwitcherHorizontal } from "@/components/layout/theme-switcher-horizontal";
import { Logo } from "@/components/logo";
import BuiltWithButton from "@/components/shared/built-with-button";
import { FOOTER_LINKS, SOCIAL_LINKS } from "@/config/marketing";
import { createTranslator, getFooterLinks, SOCIAL_LINKS } from "@/config/marketing";
import { siteConfig } from "@/config/site";
import { LocaleLink } from "@/i18n/navigation";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import React from "react";
export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
const t = useTranslations();
const translator = createTranslator(t);
const footerLinks = getFooterLinks(translator);
return (
<footer className={cn("border-t", className)}>
<Container className="px-4">
@ -53,7 +58,7 @@ export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
</div>
{/* footer links */}
{FOOTER_LINKS.map((section) => (
{footerLinks.map((section) => (
<div
key={section.title}
className="col-span-1 md:col-span-1 items-start"
@ -85,7 +90,7 @@ export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
<div className="border-t py-8">
<Container className="px-4 flex items-center justify-between">
<span className="text-muted-foreground text-sm">
&copy; {new Date().getFullYear()} {siteConfig.name}. All Rights Reserved.
&copy; {new Date().getFullYear()} {siteConfig.name} All Rights Reserved.
</span>
<ThemeSwitcherHorizontal />

View File

@ -9,14 +9,14 @@ import {
CollapsibleContent,
CollapsibleTrigger
} from '@/components/ui/collapsible';
import { MENU_LINKS } from '@/config/marketing';
import { createTranslator, getMenuLinks } from '@/config/marketing';
import { siteConfig } from '@/config/site';
import { LocaleLink } from '@/i18n/navigation';
import { cn } from '@/lib/utils';
import { Routes } from '@/routes';
import { useTranslations } from "next-intl";
import { Portal } from '@radix-ui/react-portal';
import { ArrowUpRightIcon, ChevronDownIcon, ChevronUpIcon, MenuIcon, XIcon } from 'lucide-react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import * as React from 'react';
import { RemoveScroll } from 'react-remove-scroll';
@ -107,6 +107,10 @@ interface MainMobileMenuProps {
function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
const t = useTranslations();
const translator = createTranslator(t);
const menuLinks = getMenuLinks(translator);
const commonTranslations = useTranslations("Common");
return (
<div className="fixed inset-0 z-50 mt-[72px] overflow-y-auto bg-background backdrop-blur-md animate-in fade-in-0">
<div className="flex size-full flex-col items-start space-y-4 p-4">
@ -123,7 +127,7 @@ function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
'w-full'
)}
>
Log in
{commonTranslations("login")}
</LocaleLink>
<LocaleLink
href={Routes.Register}
@ -136,13 +140,13 @@ function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
)}
onClick={onLinkClicked}
>
Sign up
{commonTranslations("signUp")}
</LocaleLink>
</div>
{/* main menu */}
<ul className="w-full">
{MENU_LINKS.map((item) => (
{menuLinks.map((item) => (
<li key={item.title} className="py-2">
{item.items ? (
<Collapsible

View File

@ -17,13 +17,14 @@ import {
NavigationMenuTrigger,
navigationMenuTriggerStyle
} from '@/components/ui/navigation-menu';
import { MENU_LINKS } from '@/config/marketing';
import { createTranslator, getMenuLinks } from '@/config/marketing';
import { siteConfig } from '@/config/site';
import { useScroll } from "@/hooks/use-scroll";
import { LocaleLink } from '@/i18n/navigation';
import { authClient } from '@/lib/auth-client';
import { cn } from '@/lib/utils';
import { Routes } from '@/routes';
import { useTranslations } from "next-intl";
import { ArrowUpRightIcon } from 'lucide-react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
@ -41,6 +42,10 @@ export function Navbar({ scroll }: NavBarProps) {
const scrolled = useScroll(50);
const { data: session, error } = authClient.useSession();
const user = session?.user;
const t = useTranslations();
const translator = createTranslator(t);
const menuLinks = getMenuLinks(translator);
const commonTranslations = useTranslations("Common");
// console.log(`Navbar, user:`, user);
const pathname = usePathname();
@ -69,13 +74,13 @@ export function Navbar({ scroll }: NavBarProps) {
<div className="flex-1 flex items-center justify-center space-x-2">
<NavigationMenu className="relative">
<NavigationMenuList className="flex items-center">
{MENU_LINKS.map((item, index) =>
{menuLinks.map((item, index) =>
item.items ? (
<NavigationMenuItem key={index} className="relative">
<NavigationMenuTrigger
data-active={
item.items.some((subItem) =>
pathname.startsWith(subItem.href)
subItem.href && pathname.startsWith(subItem.href)
) ? "true" : undefined
}
className={cn(
@ -90,7 +95,7 @@ export function Navbar({ scroll }: NavBarProps) {
{item.items.map((subItem, subIndex) => (
<li key={subIndex}>
<NavigationMenuLink asChild>
<Link
<LocaleLink
href={subItem.href || '#'}
target={subItem.external ? '_blank' : undefined}
rel={
@ -116,7 +121,7 @@ export function Navbar({ scroll }: NavBarProps) {
{subItem.external && (
<ArrowUpRightIcon className="size-4 shrink-0 text-muted-foreground transition-colors group-hover:text-foreground" />
)}
</Link>
</LocaleLink>
</NavigationMenuLink>
</li>
))}
@ -127,7 +132,7 @@ export function Navbar({ scroll }: NavBarProps) {
<NavigationMenuItem key={index}>
<NavigationMenuLink
asChild
active={pathname.startsWith(item.href)}
active={item.href && pathname.startsWith(item.href) ? true : undefined}
className={cn(
customNavigationMenuTriggerStyle,
"data-[active]:text-primary data-[active]:font-bold dark:data-[active]:text-white"
@ -163,7 +168,7 @@ export function Navbar({ scroll }: NavBarProps) {
variant="outline"
size="sm"
>
<span>Log in</span>
{commonTranslations("login")}
</Button>
</LoginWrapper>
@ -173,7 +178,7 @@ export function Navbar({ scroll }: NavBarProps) {
asChild
>
<LocaleLink href={Routes.Register}>
Sign up
{commonTranslations("signUp")}
</LocaleLink>
</Button>
</div>

View File

@ -17,16 +17,20 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { AVATAR_LINKS } from "@/config/marketing";
import { createTranslator, getAvatarLinks } from "@/config/marketing";
import { useMediaQuery } from "@/hooks/use-media-query";
import { LocaleLink, useLocaleRouter } from "@/i18n/navigation";
import { authClient } from "@/lib/auth-client";
import { useTranslations } from "next-intl";
import { LogOutIcon } from "lucide-react";
import { useState } from "react";
export function UserButton() {
const { data: session, error } = authClient.useSession();
const user = session?.user;
const t = useTranslations();
const translator = createTranslator(t);
const avatarLinks = getAvatarLinks(translator);
const handleSignOut = async () => {
await authClient.signOut({
@ -85,7 +89,7 @@ export function UserButton() {
</div>
<ul className="mb-14 mt-1 w-full text-muted-foreground">
{AVATAR_LINKS.map((item) => (
{avatarLinks.map((item) => (
<li
key={item.title}
className="rounded-lg text-foreground hover:bg-muted"
@ -147,7 +151,7 @@ export function UserButton() {
</div>
<DropdownMenuSeparator />
{AVATAR_LINKS.map((item) => (
{avatarLinks.map((item) => (
<DropdownMenuItem
key={item.title}
asChild

View File

@ -25,158 +25,185 @@ import {
SquarePenIcon
} from 'lucide-react';
/**
* list all the menu links here, you can customize the links as you want
*/
export const MENU_LINKS: NestedMenuItem[] = [
{
title: 'Features',
href: Routes.Pricing,
external: false
},
{
title: 'Pricing',
href: Routes.Pricing,
external: false
},
{
title: 'Blog',
href: Routes.Blog,
external: false
},
{
title: 'AI',
items: [
{
title: 'AI Tex',
description: 'show how to use AI to write stunning text',
icon: <SquarePenIcon className="size-5 shrink-0" />,
href: Routes.AIText,
external: false
},
{
title: 'AI Image',
description: 'show how to use AI to generate beautiful images',
icon: <ImageIcon className="size-5 shrink-0" />,
href: Routes.AIImage,
external: false
},
{
title: 'AI Video',
description: 'show how to use AI to generate amazing videos',
icon: <FilmIcon className="size-5 shrink-0" />,
href: Routes.AIVideo,
external: false
},
{
title: 'AI Audio',
description: 'show how to use AI to generate wonderful audio',
icon: <AudioLinesIcon className="size-5 shrink-0" />,
href: Routes.AIAudio,
external: false
}
]
},
{
title: 'Pages',
items: [
{
title: 'About',
description: 'Learn more about our company, mission, and values',
icon: <InfoIcon className="size-5 shrink-0" />,
href: Routes.About,
external: false
},
{
title: 'Contact',
description: 'Get in touch with our team for support or inquiries',
icon: <MailIcon className="size-5 shrink-0" />,
href: Routes.Contact,
external: false
},
{
title: 'Waitlist',
description: 'Join our waitlist for latest news and updates',
icon: <MailboxIcon className="size-5 shrink-0" />,
href: Routes.Waitlist,
external: false
},
{
title: 'Changelog',
description: 'See the latest updates to our products',
icon: <ListChecksIcon className="size-5 shrink-0" />,
href: Routes.Changelog,
external: false
},
{
title: 'Roadmap',
description: 'Explore our future plans and upcoming features',
icon: <SquareKanbanIcon className="size-5 shrink-0" />,
href: Routes.Roadmap,
external: true
},
{
title: 'Cookie Policy',
description: 'Information about how we use cookies on our website',
icon: <CookieIcon className="size-5 shrink-0" />,
href: Routes.CookiePolicy,
external: false
},
{
title: 'Privacy Policy',
description: 'Details about how we protect and handle your data',
icon: <ShieldIcon className="size-5 shrink-0" />,
href: Routes.PrivacyPolicy,
external: false
},
{
title: 'Terms of Service',
description: 'The legal agreement between you and our company',
icon: <FileTextIcon className="size-5 shrink-0" />,
href: Routes.TermsOfService,
external: false
}
]
},
];
type TranslationFunction = (key: string, ...args: any[]) => string;
/**
* list all the footer links here, you can customize the links as you want
* Creates a translation function that works with our menu functions
* @param t - The next-intl translation function
* @returns A translation function that accepts string keys
*/
export const FOOTER_LINKS: NestedMenuItem[] = [
{
title: 'Product',
items: [
{ title: 'Features', href: Routes.Features, external: false },
{ title: 'Pricing', href: Routes.Pricing, external: false },
{ title: 'FAQ', href: Routes.FAQ, external: false },
]
},
{
title: 'Resources',
items: [
{ title: 'Blog', href: Routes.Blog, external: false },
{ title: 'Changelog', href: Routes.Changelog, external: false },
{ title: 'Roadmap', href: Routes.Roadmap, external: true },
]
},
{
title: 'Company',
items: [
{ title: 'About', href: Routes.About, external: false },
{ title: 'Contact', href: Routes.Contact, external: false },
{ title: 'Waitlist', href: Routes.Waitlist, external: false }
]
},
{
title: 'Legal',
items: [
{ title: 'Cookie Policy', href: Routes.CookiePolicy, external: false },
{ title: 'Privacy Policy', href: Routes.PrivacyPolicy, external: false },
{ title: 'Terms of Service', href: Routes.TermsOfService, external: false },
]
}
];
export function createTranslator(t: any): TranslationFunction {
return (key: string) => {
try {
// @ts-ignore - We know this is a valid key because we've defined it in our messages
return t(key);
} catch (error) {
console.error(`Translation key not found: ${key}`);
return key.split('.').pop() || key;
}
};
}
/**
* Get menu links with translations
* @param t - The translation function
* @returns The menu links with translated titles and descriptions
*/
export function getMenuLinks(t: TranslationFunction): NestedMenuItem[] {
return [
{
title: t('Marketing.menu.features.title'),
href: Routes.Features,
external: false
},
{
title: t('Marketing.menu.pricing.title'),
href: Routes.Pricing,
external: false
},
{
title: t('Marketing.menu.blog.title'),
href: Routes.Blog,
external: false
},
{
title: t('Marketing.menu.ai.title'),
items: [
{
title: t('Marketing.menu.ai.items.text.title'),
description: t('Marketing.menu.ai.items.text.description'),
icon: <SquarePenIcon className="size-5 shrink-0" />,
href: Routes.AIText,
external: false
},
{
title: t('Marketing.menu.ai.items.image.title'),
description: t('Marketing.menu.ai.items.image.description'),
icon: <ImageIcon className="size-5 shrink-0" />,
href: Routes.AIImage,
external: false
},
{
title: t('Marketing.menu.ai.items.video.title'),
description: t('Marketing.menu.ai.items.video.description'),
icon: <FilmIcon className="size-5 shrink-0" />,
href: Routes.AIVideo,
external: false
},
{
title: t('Marketing.menu.ai.items.audio.title'),
description: t('Marketing.menu.ai.items.audio.description'),
icon: <AudioLinesIcon className="size-5 shrink-0" />,
href: Routes.AIAudio,
external: false
}
]
},
{
title: t('Marketing.menu.pages.title'),
items: [
{
title: t('Marketing.menu.pages.items.about.title'),
description: t('Marketing.menu.pages.items.about.description'),
icon: <InfoIcon className="size-5 shrink-0" />,
href: Routes.About,
external: false
},
{
title: t('Marketing.menu.pages.items.contact.title'),
description: t('Marketing.menu.pages.items.contact.description'),
icon: <MailIcon className="size-5 shrink-0" />,
href: Routes.Contact,
external: false
},
{
title: t('Marketing.menu.pages.items.waitlist.title'),
description: t('Marketing.menu.pages.items.waitlist.description'),
icon: <MailboxIcon className="size-5 shrink-0" />,
href: Routes.Waitlist,
external: false
},
{
title: t('Marketing.menu.pages.items.changelog.title'),
description: t('Marketing.menu.pages.items.changelog.description'),
icon: <ListChecksIcon className="size-5 shrink-0" />,
href: Routes.Changelog,
external: false
},
{
title: t('Marketing.menu.pages.items.roadmap.title'),
description: t('Marketing.menu.pages.items.roadmap.description'),
icon: <SquareKanbanIcon className="size-5 shrink-0" />,
href: Routes.Roadmap,
external: true
},
{
title: t('Marketing.menu.pages.items.cookiePolicy.title'),
description: t('Marketing.menu.pages.items.cookiePolicy.description'),
icon: <CookieIcon className="size-5 shrink-0" />,
href: Routes.CookiePolicy,
external: false
},
{
title: t('Marketing.menu.pages.items.privacyPolicy.title'),
description: t('Marketing.menu.pages.items.privacyPolicy.description'),
icon: <ShieldIcon className="size-5 shrink-0" />,
href: Routes.PrivacyPolicy,
external: false
},
{
title: t('Marketing.menu.pages.items.termsOfService.title'),
description: t('Marketing.menu.pages.items.termsOfService.description'),
icon: <FileTextIcon className="size-5 shrink-0" />,
href: Routes.TermsOfService,
external: false
}
]
},
];
}
/**
* Get footer links with translations
* @param t - The translation function
* @returns The footer links with translated titles
*/
export function getFooterLinks(t: TranslationFunction): NestedMenuItem[] {
return [
{
title: t('Marketing.footer.product.title'),
items: [
{ title: t('Marketing.footer.product.items.features'), href: Routes.Features, external: false },
{ title: t('Marketing.footer.product.items.pricing'), href: Routes.Pricing, external: false },
{ title: t('Marketing.footer.product.items.faq'), href: Routes.FAQ, external: false },
]
},
{
title: t('Marketing.footer.resources.title'),
items: [
{ title: t('Marketing.footer.resources.items.blog'), href: Routes.Blog, external: false },
{ title: t('Marketing.footer.resources.items.changelog'), href: Routes.Changelog, external: false },
{ title: t('Marketing.footer.resources.items.roadmap'), href: Routes.Roadmap, external: true },
]
},
{
title: t('Marketing.footer.company.title'),
items: [
{ title: t('Marketing.footer.company.items.about'), href: Routes.About, external: false },
{ title: t('Marketing.footer.company.items.contact'), href: Routes.Contact, external: false },
{ title: t('Marketing.footer.company.items.waitlist'), href: Routes.Waitlist, external: false }
]
},
{
title: t('Marketing.footer.legal.title'),
items: [
{ title: t('Marketing.footer.legal.items.cookiePolicy'), href: Routes.CookiePolicy, external: false },
{ title: t('Marketing.footer.legal.items.privacyPolicy'), href: Routes.PrivacyPolicy, external: false },
{ title: t('Marketing.footer.legal.items.termsOfService'), href: Routes.TermsOfService, external: false },
]
}
];
}
/**
* list all the social links here, you can delete the ones that are not needed
@ -230,17 +257,21 @@ export const SOCIAL_LINKS: MenuItem[] = [
];
/**
* list all the avatar links here, you can customize the links as you want
* Get avatar links with translations
* @param t - The translation function
* @returns The avatar links with translated titles
*/
export const AVATAR_LINKS: MenuItem[] = [
{
title: 'Dashboard',
href: Routes.Dashboard,
icon: <DashboardIcon className="size-4 shrink-0" />
},
{
title: 'Settings',
href: Routes.Settings,
icon: <SettingsIcon className="size-4 shrink-0" />
}
];
export function getAvatarLinks(t: TranslationFunction): MenuItem[] {
return [
{
title: t('Marketing.avatar.dashboard'),
href: Routes.Dashboard,
icon: <DashboardIcon className="size-4 shrink-0" />
},
{
title: t('Marketing.avatar.settings'),
href: Routes.Settings,
icon: <SettingsIcon className="size-4 shrink-0" />
}
];
}