Initial commit: fristy.net personal site
Build & Push / Lint & Build (push) Successful in 1m14s
Build & Push / Build & Push Image (push) Canceled after 1m36s

This commit is contained in:
fristy committed 2026-09-10 04:54:34 +03:00
commit eb20d470e0
51 files changed
+6052

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
{
"meta": {
"title": "fristy.net — fristy love you",
"description": "Fristys persönliche Ecke: Projekte, Live-Discord-Status und Kontakt."
},
"nav": {
"projects": "Projekte",
"discord": "Discord",
"contact": "Kontakt"
},
"hero": {
"greeting": "Hey, ich bin",
"tagline": "Ich programmiere als Hobby. Das ist meine persönliche Ecke: Projekte, Live-Discord-Status und Kontaktinfos.",
"ctaProjects": "Projekte Ansehen",
"ctaContact": "Kontakt"
},
"discord": {
"title": "Discord",
"live": "Live",
"online": "Online",
"idle": "Abwesend",
"dnd": "Bitte Nicht Stören",
"offline": "Offline",
"playing": "Spielt",
"listening": "Hört",
"customStatus": "Status",
"noActivity": "Gerade still... Hier läuft wohl nichts.",
"profile": "Profil Ansehen"
},
"projects": {
"title": "Projekte",
"subtitle": "Meine Open-Source-Projekte auf git.fristy.net. Die Liste aktualisiert sich automatisch.",
"updated": "Aktualisiert",
"stars": "Sterne",
"forks": "Forks",
"viewOnGitea": "Auf Gitea Öffnen",
"viewWebsite": "Webseite Öffnen",
"empty": "Noch keine öffentlichen Repos.",
"noDescription": "Keine Beschreibung."
},
"contact": {
"title": "Kontakt & Social",
"email": "E-Mail",
"discord": "Discord",
"github": "GitHub",
"gitea": "Gitea",
"spotify": "Spotify",
"steam": "Steam",
"copy": "Kopieren",
"copied": "Kopiert!"
},
"footer": {
"rights": "Alle Rechte vorbehalten",
"prefix": "Diese Seite ist",
"licenseLabel": "Apache 2.0",
"middle": "unter der",
"sourceLabel": "Open Source",
"suffix": "-Lizenz",
"licenseFirst": false
},
"notFound": {
"title": "Seite Nicht Gefunden",
"subtitle": "Hier gibt es keine Seite, aber du bist nicht verloren.",
"home": "Zurück Nach Hause"
}
}
+66
View File
@@ -0,0 +1,66 @@
{
"meta": {
"title": "fristy.net — fristy love you",
"description": "Fristy's personal corner: projects, live Discord status and contact."
},
"nav": {
"projects": "Projects",
"discord": "Discord",
"contact": "Contact"
},
"hero": {
"greeting": "Hey, I'm",
"tagline": "I write code as a hobby. This is my personal corner: my projects, live Discord status and contact info.",
"ctaProjects": "See Projects",
"ctaContact": "Contact"
},
"discord": {
"title": "Discord",
"live": "Live",
"online": "Online",
"idle": "Idle",
"dnd": "Do Not Disturb",
"offline": "Offline",
"playing": "Playing",
"listening": "Listening To",
"customStatus": "Status",
"noActivity": "Quiet right now... Nothing seems to be going on.",
"profile": "View Profile"
},
"projects": {
"title": "Projects",
"subtitle": "My open-source projects on git.fristy.net. The list updates automatically.",
"updated": "Updated",
"stars": "Stars",
"forks": "Forks",
"viewOnGitea": "Open on Gitea",
"viewWebsite": "Open Website",
"empty": "No public repos to show yet.",
"noDescription": "No description."
},
"contact": {
"title": "Contact & Social",
"email": "Email",
"discord": "Discord",
"github": "GitHub",
"gitea": "Gitea",
"spotify": "Spotify",
"steam": "Steam",
"copy": "Copy",
"copied": "Copied!"
},
"footer": {
"rights": "All rights reserved",
"prefix": "This site is",
"licenseLabel": "Apache 2.0",
"middle": "under the",
"sourceLabel": "open source",
"suffix": " license",
"licenseFirst": false
},
"notFound": {
"title": "Page Not Found",
"subtitle": "There's no page here, but you're not lost. Home is right there.",
"home": "Back Home"
}
}
+66
View File
@@ -0,0 +1,66 @@
{
"meta": {
"title": "fristy.net - fristy love you",
"description": "Fristy'nin kişisel köşesi: projeler, canlı Discord durumu ve iletişim."
},
"nav": {
"projects": "Projeler",
"discord": "Discord",
"contact": "İletişim"
},
"hero": {
"greeting": "Selam, ben",
"tagline": "Hobi olarak kod yazıyorum. Burası kişisel köşem: projelerim, anlık Discord durumum ve iletişim bilgilerim.",
"ctaProjects": "Projelere Bak",
"ctaContact": "İletişim"
},
"discord": {
"title": "Discord",
"live": "Canlı",
"online": "Çevrimiçi",
"idle": "Boşta",
"dnd": "Rahatsız Etmeyin",
"offline": "Çevrimdışı",
"playing": "Oynuyor",
"listening": "Dinliyor",
"customStatus": "Durum",
"noActivity": "Şu an sessizlik... Ortalıkta bir şey yok gibi.",
"profile": "Profile Git"
},
"projects": {
"title": "Projeler",
"subtitle": "Açık kaynak projelerim git.fristy.net üzerinde. Liste otomatik güncelleniyor.",
"updated": "Güncellendi",
"stars": "Yıldız",
"forks": "Çatal",
"viewOnGitea": "Gitea'da Aç",
"viewWebsite": "Web Sitesini Aç",
"empty": "Henüz gösterilecek public repo yok.",
"noDescription": "Açıklama yok."
},
"contact": {
"title": "İletişim ve Sosyal",
"email": "E-Posta",
"discord": "Discord",
"github": "GitHub",
"gitea": "Gitea",
"spotify": "Spotify",
"steam": "Steam",
"copy": "Kopyala",
"copied": "Kopyalandı!"
},
"footer": {
"rights": "Tüm Hakları Saklıdır",
"prefix": "Bu site",
"licenseLabel": "Apache 2.0",
"middle": "lisansı altında",
"sourceLabel": "açık kaynak",
"suffix": " kodludur",
"licenseFirst": true
},
"notFound": {
"title": "Sayfa Bulunamadı",
"subtitle": "Böyle bir sayfa yok, ama kaybolmadın. Ana sayfa hemen şurada.",
"home": "Ana Sayfaya Dön"
}
}
+12
View File
@@ -0,0 +1,12 @@
import type { Locale } from "./locales";
import tr from "./dictionaries/tr.json";
import en from "./dictionaries/en.json";
import de from "./dictionaries/de.json";
const dictionaries = { tr, en, de } as const;
export type Dictionary = typeof tr;
export function getDictionary(locale: Locale): Dictionary {
return dictionaries[locale] ?? dictionaries.tr;
}
+11
View File
@@ -0,0 +1,11 @@
import { cookies } from "next/headers";
import { DEFAULT_LOCALE, LOCALE_COOKIE, isLocale, type Locale } from "./locales";
export async function getLocale(): Promise<Locale> {
const store = await cookies();
const raw = store.get(LOCALE_COOKIE)?.value;
if (raw && isLocale(raw)) {
return raw;
}
return DEFAULT_LOCALE;
}
+48
View File
@@ -0,0 +1,48 @@
export const LOCALES = ["tr", "en", "de"] as const;
export type Locale = (typeof LOCALES)[number];
export const DEFAULT_LOCALE: Locale = "tr";
export const LOCALE_COOKIE = "locale";
export const LOCALE_LABELS: Record<Locale, string> = {
tr: "TR",
en: "EN",
de: "DE",
};
export function isLocale(value: string): value is Locale {
return (LOCALES as readonly string[]).includes(value);
}
export function matchLocale(acceptLanguage: string | null): Locale {
if (!acceptLanguage) {
return DEFAULT_LOCALE;
}
const preferences = acceptLanguage
.split(",")
.map((part) => {
const [range, ...params] = part.trim().split(";");
let q = 1;
for (const param of params) {
const match = param.trim().match(/^q=([0-9.]+)$/);
if (match) {
q = Number(match[1]);
}
}
return { range: range.toLowerCase(), q };
})
.sort((a, b) => b.q - a.q);
for (const { range } of preferences) {
if (range === "*") {
return DEFAULT_LOCALE;
}
const base = range.split("-")[0];
if (isLocale(base)) {
return base;
}
}
return DEFAULT_LOCALE;
}
+16
View File
@@ -0,0 +1,16 @@
"use server";
import { cookies } from "next/headers";
import { LOCALE_COOKIE, isLocale, type Locale } from "./locales";
export async function setLocaleCookie(locale: Locale): Promise<void> {
if (!isLocale(locale)) {
return;
}
const store = await cookies();
store.set(LOCALE_COOKIE, locale, {
path: "/",
maxAge: 60 * 60 * 24 * 365,
sameSite: "lax",
});
}