From eca56fab5c11e52a9cb6b27f8998428c1833da50 Mon Sep 17 00:00:00 2001 From: poyrazavsever Date: Mon, 31 Aug 2026 19:39:22 +0300 Subject: [PATCH] feat(layout): add animated promotional side rails --- app/[locale]/layout.tsx | 32 ++- components/app-shell.tsx | 63 +++--- components/layout-promo-rails.tsx | 342 ++++++++++++++++++++++++++++++ data/blog.ts | 5 + data/layout-promos.ts | 202 ++++++++++++++++++ messages/en.json | 62 ++++++ messages/tr.json | 62 ++++++ 7 files changed, 737 insertions(+), 31 deletions(-) create mode 100644 components/layout-promo-rails.tsx create mode 100644 data/layout-promos.ts diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index f028dd9..f2349a7 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -9,6 +9,7 @@ import { AppShell } from "@/components/app-shell"; import { GoogleAnalytics } from "@/components/google-analytics"; import { PoyrazBottomRightFollower } from "@/components/poyraz-bottom-right-follower"; import { listAnimationSources } from "@/data/animation-sources"; +import { getHomeBlogNews, getLatestAgendaArticle } from "@/data/blog"; export async function generateMetadata({ params, @@ -116,10 +117,13 @@ export default async function LocaleLayout({ } // Provide messages for NextIntlClientProvider - const [messages, animationSources] = await Promise.all([ - getMessages(), - listAnimationSources(locale), - ]); + const [messages, animationSources, latestAgendaArticle, latestPosts] = + await Promise.all([ + getMessages(), + listAnimationSources(locale), + getLatestAgendaArticle(locale), + getHomeBlogNews(locale, 1), + ]); const animationSourceSearchItems = animationSources.map((source) => ({ slug: source.slug, title: source.title, @@ -133,7 +137,25 @@ export default async function LocaleLayout({ - + {children} diff --git a/components/app-shell.tsx b/components/app-shell.tsx index 0fa9e47..0fca8e1 100644 --- a/components/app-shell.tsx +++ b/components/app-shell.tsx @@ -1,7 +1,6 @@ "use client"; import { useEffect, useState } from "react"; import { Icon } from "@iconify/react"; -import { usePathname } from "@/i18n/routing"; import { AnnouncementBar } from "poyraz-ui/organisms"; import { SiteNavbar } from "@/components/site-navbar"; import { NekoFollower } from "@/components/neko-follower"; @@ -10,6 +9,11 @@ import { useLocale } from "next-intl"; import { getLocalizedValue } from "@/lib/locale"; import dynamic from "next/dynamic"; import type { AnimationSourceSearchItem } from "@/lib/command-palette-links"; +import { + LayoutLeftPromoRail, + LayoutRightPromoRail, + type LayoutContentPromo, +} from "@/components/layout-promo-rails"; const AtaturkWidgetModal = dynamic( () => import("@/components/ataturk-widget-modal").then((mod) => mod.AtaturkWidgetModal), @@ -19,6 +23,8 @@ const AtaturkWidgetModal = dynamic( type AppShellProps = { children: React.ReactNode; animationSources: AnimationSourceSearchItem[]; + latestAgenda: LayoutContentPromo | null; + latestPost: LayoutContentPromo | null; }; export type ThemeMode = "light" | "dark"; @@ -32,15 +38,15 @@ function getInitialTheme(): ThemeMode { return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } -export function AppShell({ children, animationSources }: AppShellProps) { - const pathname = usePathname(); +export function AppShell({ + children, + animationSources, + latestAgenda, + latestPost, +}: AppShellProps) { const locale = useLocale(); const announcement = ANNOUNCEMENT_ITEMS[0]; const [theme, setTheme] = useState(getInitialTheme); - const isStandaloneLinksPage = - pathname === "/links" || pathname.startsWith("/links/"); - const isStandaloneMediaKitPage = - pathname === "/media-kit" || pathname.startsWith("/media-kit/"); useEffect(() => { document.documentElement.dataset.poyrazTheme = theme; @@ -48,32 +54,37 @@ export function AppShell({ children, animationSources }: AppShellProps) { localStorage.setItem("poyraz-theme", theme); }, [theme]); - if (isStandaloneLinksPage || isStandaloneMediaKitPage) { - return children; - } - const localizedText = announcement ? getLocalizedValue(announcement.text, locale) : ""; return ( <> {ENABLE_NEKO_FOLLOWER ? : null} -
- + - {announcement ? ( - } - > - {localizedText} - - ) : null} -
{children}
+
+
+ + {announcement ? ( + } + > + {localizedText} + + ) : null} +
+
{children}
+
+
); diff --git a/components/layout-promo-rails.tsx b/components/layout-promo-rails.tsx new file mode 100644 index 0000000..68b1dfe --- /dev/null +++ b/components/layout-promo-rails.tsx @@ -0,0 +1,342 @@ +"use client"; + +import { useState, type KeyboardEvent } from "react"; +import Image from "next/image"; +import { Icon } from "@iconify/react"; +import { AnimatePresence, motion, useReducedMotion } from "framer-motion"; +import { useLocale, useTranslations } from "next-intl"; +import { + Badge, + Button, + ButtonIcon, + ButtonLabel, + Card, + Typography, +} from "poyraz-ui/atoms"; +import { Link } from "@/i18n/routing"; +import { + LEFT_LAYOUT_PROMO_SLIDES, + RIGHT_LAYOUT_PROMO_SLIDES, + type LayoutPromoCardDefinition, + type LayoutPromoSlide, +} from "@/data/layout-promos"; +import { SPONSORS } from "@/data/sponsors"; +import { getLocalizedValue } from "@/lib/locale"; + +export type LayoutContentPromo = { + title: string; + href: string; +}; + +type PromoRailSide = "left" | "right"; + +function RailButton({ + href, + label, + icon = "mdi:arrow-right", + variant = "outline", + external = false, +}: { + href: string; + label: string; + icon?: string; + variant?: "default" | "outline" | "secondary"; + external?: boolean; +}) { + const content = ( + <> + {label} + + + + + ); + + return ( + + ); +} + +function SponsorLogos() { + return ( +
+ {SPONSORS.map((sponsor, index) => ( + + + + ))} +
+ ); +} + +function PromoIcon({ + card, + className, +}: { + card: LayoutPromoCardDefinition; + className?: string; +}) { + return ( + + ); +} + +function PromoCard({ + card, + latestAgenda, + latestPost, +}: { + card: LayoutPromoCardDefinition; + latestAgenda: LayoutContentPromo | null; + latestPost: LayoutContentPromo | null; +}) { + const t = useTranslations("LayoutPromos"); + const locale = useLocale(); + const liveContent = + card.contentSource === "latestAgenda" + ? latestAgenda + : card.contentSource === "latestPost" + ? latestPost + : null; + const href = liveContent?.href ?? getLocalizedValue(card.href, locale); + const description = liveContent?.title ?? t(card.descriptionKey); + const external = card.external ?? false; + const iconSurface = card.iconSurface ?? "accent"; + const cardClassName = + card.surface === "primary" + ? "rounded-sm border-primary/25 bg-primary/5 p-3" + : "rounded-sm border-border p-3"; + const iconClassName = + iconSurface === "primary" + ? "bg-primary text-primary-foreground" + : iconSurface === "foreground" + ? "bg-foreground text-background" + : "bg-accent text-foreground"; + + return ( + + {card.eyebrowKey ? ( +
+ + {t(card.eyebrowKey)} + + +
+ ) : ( +
+ +
+ )} + + + {t(card.titleKey)} + + + {description} + + + {card.kind === "sponsors" ? : null} + +
+ +
+
+ ); +} + +function PromoRail({ + side, + slides, + latestAgenda = null, + latestPost = null, +}: { + side: PromoRailSide; + slides: readonly LayoutPromoSlide[]; + latestAgenda?: LayoutContentPromo | null; + latestPost?: LayoutContentPromo | null; +}) { + const t = useTranslations("LayoutPromos"); + const reduceMotion = useReducedMotion(); + const [activeSlide, setActiveSlide] = useState(0); + const direction = side === "left" ? -1 : 1; + const activeCards = slides[activeSlide] ?? slides[0]; + const railId = `${side}-promo-rail`; + + const selectAdjacentSlide = (step: number) => { + setActiveSlide((current) => (current + step + slides.length) % slides.length); + }; + + const handleNavigationKeyDown = (event: KeyboardEvent) => { + if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return; + + event.preventDefault(); + selectAdjacentSlide(event.key === "ArrowRight" ? 1 : -1); + }; + + return ( + + ); +} + +export function LayoutLeftPromoRail({ + latestAgenda, + latestPost, +}: { + latestAgenda: LayoutContentPromo | null; + latestPost: LayoutContentPromo | null; +}) { + return ( + + ); +} + +export function LayoutRightPromoRail() { + return ; +} diff --git a/data/blog.ts b/data/blog.ts index 2294d37..73e44b8 100644 --- a/data/blog.ts +++ b/data/blog.ts @@ -97,6 +97,11 @@ export async function getAllAgendaArticles(locale?: string): Promise isNewsletterCategory(article.category)); } +export async function getLatestAgendaArticle(locale?: string) { + const articles = await getAllAgendaArticles(locale); + return articles[0] ?? null; +} + export async function getHomeBlogNews(locale?: string, limit = 3) { const articles = await getAllBlogArticles(locale); diff --git a/data/layout-promos.ts b/data/layout-promos.ts new file mode 100644 index 0000000..8b0cc75 --- /dev/null +++ b/data/layout-promos.ts @@ -0,0 +1,202 @@ +import type { Localized } from "@/lib/locale"; + +export type LayoutPromoCopyKey = + | "weeklyEyebrow" + | "weeklyTitle" + | "weeklyFallback" + | "weeklyCta" + | "projectsTitle" + | "projectsDescription" + | "projectsCta" + | "latestPostEyebrow" + | "latestPostTitle" + | "latestPostFallback" + | "latestPostCta" + | "anatomyTitle" + | "anatomyDescription" + | "anatomyCta" + | "youtubeEyebrow" + | "youtubeTitle" + | "youtubeDescription" + | "youtubeCta" + | "designSystemEyebrow" + | "designSystemTitle" + | "designSystemDescription" + | "designSystemCta" + | "communityEyebrow" + | "communityTitle" + | "communityDescription" + | "communityCta" + | "referencesEyebrow" + | "referencesTitle" + | "referencesDescription" + | "referencesCta" + | "sponsorsEyebrow" + | "sponsorsTitle" + | "sponsorsDescription" + | "sponsorsCta" + | "contactTitle" + | "contactDescription" + | "contactCta" + | "linkedinEyebrow" + | "linkedinTitle" + | "linkedinDescription" + | "linkedinCta" + | "instagramEyebrow" + | "instagramTitle" + | "instagramDescription" + | "instagramCta"; + +export type LayoutPromoCardDefinition = { + id: string; + kind?: "standard" | "sponsors"; + eyebrowKey?: LayoutPromoCopyKey; + titleKey: LayoutPromoCopyKey; + descriptionKey: LayoutPromoCopyKey; + ctaKey: LayoutPromoCopyKey; + href: string | Localized; + icon: string; + iconSurface?: "accent" | "foreground" | "primary"; + surface?: "default" | "primary"; + buttonVariant?: "default" | "outline" | "secondary"; + external?: boolean; + contentSource?: "latestAgenda" | "latestPost"; +}; + +export type LayoutPromoSlide = readonly LayoutPromoCardDefinition[]; + +export const LEFT_LAYOUT_PROMO_SLIDES: readonly LayoutPromoSlide[] = [ + [ + { + id: "weekly-agenda", + eyebrowKey: "weeklyEyebrow", + titleKey: "weeklyTitle", + descriptionKey: "weeklyFallback", + ctaKey: "weeklyCta", + href: "/agenda", + icon: "mdi:newspaper-variant-outline", + surface: "primary", + buttonVariant: "default", + contentSource: "latestAgenda", + }, + { + id: "projects", + titleKey: "projectsTitle", + descriptionKey: "projectsDescription", + ctaKey: "projectsCta", + href: "/projects", + icon: "mdi:layers-triple-outline", + }, + { + id: "latest-post", + eyebrowKey: "latestPostEyebrow", + titleKey: "latestPostTitle", + descriptionKey: "latestPostFallback", + ctaKey: "latestPostCta", + href: "/blog", + icon: "mdi:post-outline", + contentSource: "latestPost", + }, + { + id: "javascript-anatomy", + titleKey: "anatomyTitle", + descriptionKey: "anatomyDescription", + ctaKey: "anatomyCta", + href: "/content", + icon: "ri:twitter-x-fill", + iconSurface: "foreground", + }, + ], + [ + { + id: "youtube", + eyebrowKey: "youtubeEyebrow", + titleKey: "youtubeTitle", + descriptionKey: "youtubeDescription", + ctaKey: "youtubeCta", + href: "https://youtube.com/@poyrazavsever", + icon: "mdi:youtube", + iconSurface: "primary", + external: true, + }, + { + id: "poyraz-ui", + eyebrowKey: "designSystemEyebrow", + titleKey: "designSystemTitle", + descriptionKey: "designSystemDescription", + ctaKey: "designSystemCta", + href: "https://ui.poyrazavsever.com", + icon: "mdi:palette-swatch-outline", + external: true, + }, + { + id: "community", + eyebrowKey: "communityEyebrow", + titleKey: "communityTitle", + descriptionKey: "communityDescription", + ctaKey: "communityCta", + href: "/about/volunteer-community", + icon: "mdi:account-group-outline", + }, + { + id: "references", + eyebrowKey: "referencesEyebrow", + titleKey: "referencesTitle", + descriptionKey: "referencesDescription", + ctaKey: "referencesCta", + href: "/about/references", + icon: "mdi:comment-quote-outline", + }, + ], +]; + +export const RIGHT_LAYOUT_PROMO_SLIDES: readonly LayoutPromoSlide[] = [ + [ + { + id: "sponsors", + kind: "sponsors", + eyebrowKey: "sponsorsEyebrow", + titleKey: "sponsorsTitle", + descriptionKey: "sponsorsDescription", + ctaKey: "sponsorsCta", + href: "/media-kit", + icon: "mdi:handshake-outline", + buttonVariant: "default", + }, + { + id: "contact", + titleKey: "contactTitle", + descriptionKey: "contactDescription", + ctaKey: "contactCta", + href: "/contact", + icon: "mdi:message-text-outline", + iconSurface: "primary", + surface: "primary", + buttonVariant: "default", + }, + ], + [ + { + id: "linkedin", + eyebrowKey: "linkedinEyebrow", + titleKey: "linkedinTitle", + descriptionKey: "linkedinDescription", + ctaKey: "linkedinCta", + href: "https://www.linkedin.com/in/poyrazavsever/", + icon: "mdi:linkedin", + external: true, + }, + { + id: "instagram", + eyebrowKey: "instagramEyebrow", + titleKey: "instagramTitle", + descriptionKey: "instagramDescription", + ctaKey: "instagramCta", + href: "https://instagram.com/poyraz_avsever", + icon: "mdi:instagram", + surface: "primary", + buttonVariant: "default", + external: true, + }, + ], +]; diff --git a/messages/en.json b/messages/en.json index 3683387..8a07f96 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,5 +1,6 @@ { "Nav": { + "home": "Home", "about": "About", "blog": "Blog", "agenda": "Weekly Agenda", @@ -12,6 +13,10 @@ "socialLinks": "Social Links", "others": "Others", "animationResources": "Animation Resources", + "links": "Links", + "mediaKit": "Media Kit", + "references": "References", + "volunteerCommunity": "Volunteering & Community", "backToMenu": "Back to menu", "menu": "Menu", "mobileMenu": "Mobile Menu", @@ -35,6 +40,58 @@ "sponsors": "Sponsors" } }, + "LayoutPromos": { + "leftRailLabel": "Featured content", + "rightRailLabel": "Partnerships and contact", + "slideNavigationLabel": "Promotional slides", + "slideCta": "Go to slide {slide}", + "slideStatus": "Slide {current} of {total}", + "weeklyEyebrow": "This week", + "weeklyTitle": "Have you read this week's agenda?", + "weeklyFallback": "Weekly developments from software, technology, design, and artificial intelligence.", + "weeklyCta": "Read the agenda", + "projectsTitle": "Curious how I build my projects?", + "projectsDescription": "Explore the decisions, architecture, and outcomes behind selected products through detailed case studies.", + "projectsCta": "Explore projects", + "latestPostEyebrow": "New post", + "latestPostTitle": "What's new on the blog?", + "latestPostFallback": "Read my latest notes on software and product development.", + "latestPostCta": "Read the post", + "anatomyTitle": "Have you watched JavaScript Anatomy?", + "anatomyDescription": "Explore my series explaining JavaScript concepts through concise 1-4 minute landscape videos.", + "anatomyCta": "Watch the series", + "youtubeEyebrow": "YouTube", + "youtubeTitle": "Join a community of 8K+ people.", + "youtubeDescription": "Watch practical videos about software, artificial intelligence, and product development.", + "youtubeCta": "Visit the channel", + "designSystemEyebrow": "Poyraz UI", + "designSystemTitle": "Explore the design system behind this site.", + "designSystemDescription": "See the components, design decisions, and live examples I use across the portfolio.", + "designSystemCta": "Open the docs", + "communityEyebrow": "Community", + "communityTitle": "What do I do beyond writing code?", + "communityDescription": "Explore my workshops, hackathons, mentoring, and volunteer community work.", + "communityCta": "Community work", + "referencesEyebrow": "References", + "referencesTitle": "What do the people I work with say?", + "referencesDescription": "Read feedback from teammates and people I have built products with.", + "referencesCta": "Read references", + "sponsorsEyebrow": "Sponsors", + "sponsorsTitle": "These brands have sponsored my work so far.", + "sponsorsDescription": "Would you like to introduce your brand to my software and technology-focused audience?", + "sponsorsCta": "Become a sponsor", + "contactTitle": "Have an idea? Let's build it together.", + "contactDescription": "Reach out directly for a project, partnership, or content idea.", + "contactCta": "Get in touch", + "linkedinEyebrow": "LinkedIn", + "linkedinTitle": "Follow my professional journey.", + "linkedinDescription": "I share my projects, technical experiences, and notes from the industry on LinkedIn.", + "linkedinCta": "Visit LinkedIn", + "instagramEyebrow": "Instagram", + "instagramTitle": "Technology, in a shorter format.", + "instagramDescription": "Follow for concise technical videos, moments from events, and a look behind my creative process.", + "instagramCta": "Follow on Instagram" + }, "About": { "title": "About me", "contactCta": "Contact me", @@ -134,12 +191,17 @@ "copied": "Copied" }, "Links": { + "title": "My links", "desc": "Here you can find all my social accounts, portfolio pages, and quick access links in one place. Open and share directly.", + "socialLinks": "Social media links", + "quickLinks": "Quick access", + "quickLinksDesc": "Frequently used projects, resources, and resume", "allLinks": "All Links", "allLinksDesc": "Pages, resources, and social profiles", "selectCategory": "Select category", "allCategories": "All categories", "searchPlaceholder": "Search links... github, medium, /blog", + "searchAriaLabel": "Search links", "empty": "No links found matching the selected filter.", "categories": { "navigation": "Pages", diff --git a/messages/tr.json b/messages/tr.json index acc3e7d..3d97293 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -1,5 +1,6 @@ { "Nav": { + "home": "Ana Sayfa", "about": "Hakkımda", "blog": "Blog", "agenda": "Haftalık Gündem", @@ -12,6 +13,10 @@ "socialLinks": "Sosyal Bağlantılar", "others": "Diğerleri", "animationResources": "Animasyon Kaynakları", + "links": "Bağlantılar", + "mediaKit": "Medya Kiti", + "references": "Referanslar", + "volunteerCommunity": "Gönüllük ve Topluluk", "backToMenu": "Menüye dön", "menu": "Menü", "mobileMenu": "Mobil Menü", @@ -35,6 +40,58 @@ "sponsors": "Sponsorlar" } }, + "LayoutPromos": { + "leftRailLabel": "Öne çıkan içerikler", + "rightRailLabel": "İş birlikleri ve iletişim", + "slideNavigationLabel": "Tanıtım slaytları", + "slideCta": "{slide}. slayda git", + "slideStatus": "{current} / {total}. slayt", + "weeklyEyebrow": "Bu hafta", + "weeklyTitle": "Bu haftanın gündemini okudun mu?", + "weeklyFallback": "Yazılım, teknoloji, tasarım ve yapay zekâ dünyasından haftalık gelişmeler.", + "weeklyCta": "Gündemi oku", + "projectsTitle": "Nasıl geliştirdiğimi merak ediyor musun?", + "projectsDescription": "Seçili projelerde aldığım kararları, mimariyi ve ortaya çıkan sonuçları vaka çalışmalarıyla incele.", + "projectsCta": "Projeleri incele", + "latestPostEyebrow": "Yeni yazı", + "latestPostTitle": "Blogda yeni ne var?", + "latestPostFallback": "Yazılım ve ürün geliştirme üzerine son notlarıma göz at.", + "latestPostCta": "Yazıyı oku", + "anatomyTitle": "JavaScript Anatomisi'ni izledin mi?", + "anatomyDescription": "JavaScript kavramlarını 1-4 dakikalık kısa ve yatay videolarla anlattığım seriyi keşfet.", + "anatomyCta": "Seriyi izle", + "youtubeEyebrow": "YouTube", + "youtubeTitle": "8 B+ kişilik topluluğa katıl.", + "youtubeDescription": "Yazılım, yapay zekâ ve ürün geliştirme üzerine uygulamalı videoları izle.", + "youtubeCta": "Kanala git", + "designSystemEyebrow": "Poyraz UI", + "designSystemTitle": "Bu sitenin tasarım sistemini keşfet.", + "designSystemDescription": "Kullandığım bileşenleri, tasarım kararlarını ve canlı örnekleri incele.", + "designSystemCta": "Dokümantasyonu aç", + "communityEyebrow": "Topluluk", + "communityTitle": "Kodun dışında neler yapıyorum?", + "communityDescription": "Workshop, hackathon, mentörlük ve gönüllülük çalışmalarımı incele.", + "communityCta": "Topluluk çalışmaları", + "referencesEyebrow": "Referanslar", + "referencesTitle": "Birlikte çalıştığım insanlar ne diyor?", + "referencesDescription": "Ekip arkadaşlarımdan ve birlikte ürettiğim insanlardan gelen yorumları oku.", + "referencesCta": "Referansları oku", + "sponsorsEyebrow": "Sponsorlar", + "sponsorsTitle": "Bak, bunlar bana şimdiye kadar sponsor oldu.", + "sponsorsDescription": "Sen de markanı yazılım ve teknoloji odaklı kitlemle buluşturmak ister misin?", + "sponsorsCta": "Sen de sponsor ol", + "contactTitle": "Bir fikrin mi var? Birlikte üretelim.", + "contactDescription": "Proje, iş birliği veya içerik fikrin için doğrudan iletişime geç.", + "contactCta": "İletişime geç", + "linkedinEyebrow": "LinkedIn", + "linkedinTitle": "Profesyonel yolculuğumu takip et.", + "linkedinDescription": "Projelerimi, teknik deneyimlerimi ve sektörden notlarımı LinkedIn'de paylaşıyorum.", + "linkedinCta": "LinkedIn'e git", + "instagramEyebrow": "Instagram", + "instagramTitle": "Teknolojinin daha kısa hâli burada.", + "instagramDescription": "Kısa teknik videolar, etkinliklerden anlar ve üretim sürecimin perde arkası için takip et.", + "instagramCta": "Instagram'da takip et" + }, "About": { "title": "Hakkımda", "contactCta": "İletişime geç", @@ -134,12 +191,17 @@ "copied": "Kopyalandı" }, "Links": { + "title": "Bağlantılarım", "desc": "Burada sosyal hesaplarım, portfolyo sayfalarım ve hızlı erişim linklerimin tamamı tek yerde duruyor. Direkt açıp paylaşabilirsin.", + "socialLinks": "Sosyal medya bağlantıları", + "quickLinks": "Hızlı erişim", + "quickLinksDesc": "Sık kullanılan projeler, kaynaklar ve özgeçmiş", "allLinks": "Tüm Linkler", "allLinksDesc": "Sayfalar, kaynaklar ve sosyal profiller", "selectCategory": "Kategori seç", "allCategories": "Tüm kategoriler", "searchPlaceholder": "Link ara... github, medium, /blog", + "searchAriaLabel": "Bağlantılarda ara", "empty": "Seçili filtreye uygun link bulunamadı.", "categories": { "navigation": "Sayfalar",