feat(landing): pixel design ported to static Astro + en/es/eu i18n
Adopt the "Carved from stone" pixel design (landing/design/) as the apex onboarding page, ported from its React/Babel-via-CDN prototype to static Astro so it works on an offline LAN. Real modded-LAN content replaces the prototype's fictional public-SMP copy. - Vendor web fonts locally (tooling/fetch-fonts.sh -> public/fonts/) so the page renders without the Google Fonts CDN at party time. - Port the design system (main.css: mood/hero/bevels) and split markup into Astro components (Creeper, PixelIcon, CopyChip, ServerListPanel). - site.ts holds language-neutral config + theme knobs (mood/hero/headFont/ dust) that replace the design's in-browser TweaksPanel; LITERALS holds never-translated product/in-game terms. - i18n: English (/), Spanish (/es/), Euskera (/eu/) generated from one [...lang].astro via getStaticPaths; copy lives in src/i18n/ui.ts. Nav has an EN/ES/EU switcher; html lang + hreflang set per page. - Status panel shows a static server-list row + honest stat tiles (no fake live count). Copy + scroll-reveal are the only client JS. Build emits to ../www (gitignored). Euskera strings pending a native review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
340
landing/src/i18n/ui.ts
Normal file
340
landing/src/i18n/ui.ts
Normal file
@@ -0,0 +1,340 @@
|
||||
// Translatable UI copy for the landing page. Structure is identical across
|
||||
// locales; the page (src/pages/[...lang].astro) renders against it.
|
||||
//
|
||||
// Conventions:
|
||||
// - "{name}" in step1 is replaced with the launcher name at render time.
|
||||
// - Sentences containing links/kbd/literals are split into parts (a/b/pre/
|
||||
// post/link) so the markup stays in the template, not in the strings.
|
||||
// - Product / in-game literals (authlib-injector, Multiplayer, Add Server)
|
||||
// live in site.ts LITERALS and are never translated.
|
||||
|
||||
export type Lang = "en" | "es" | "eu";
|
||||
export const LANGS: Lang[] = ["en", "es", "eu"];
|
||||
export const LANG_LABEL: Record<Lang, string> = { en: "EN", es: "ES", eu: "EU" };
|
||||
// URL path per locale (en is default, served at root).
|
||||
export const LANG_PATH: Record<Lang, string> = { en: "/", es: "/es/", eu: "/eu/" };
|
||||
|
||||
export interface UI {
|
||||
htmlLang: string;
|
||||
copied: string; // transient copy-button feedback
|
||||
nav: { status: string; features: string; join: string; play: string };
|
||||
hero: {
|
||||
eyebrow: string; // "Modded LAN · NeoForge" — version appended at render
|
||||
tagline: string;
|
||||
sub: string;
|
||||
copyIp: string;
|
||||
howToJoin: string;
|
||||
metaModpack: string;
|
||||
metaLan: string;
|
||||
};
|
||||
status: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
lead: string;
|
||||
modded: string;
|
||||
motd: string;
|
||||
upTo: string;
|
||||
};
|
||||
statLabels: [string, string, string, string];
|
||||
features: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
lead: string;
|
||||
items: { h: string; p: string }[];
|
||||
};
|
||||
join: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
lead: string;
|
||||
s1: { kicker: string; h: string; p: string };
|
||||
s2: {
|
||||
kicker: string;
|
||||
h: string;
|
||||
pa: string; // before the authlib-injector literal
|
||||
pb: string; // after it
|
||||
copy: string;
|
||||
registerPre: string;
|
||||
caPre: string;
|
||||
caLink: string;
|
||||
caPost: string;
|
||||
};
|
||||
s3: { kicker: string; h: string; p: string };
|
||||
s4: {
|
||||
kicker: string;
|
||||
h: string;
|
||||
pa: string; // before the Multiplayer/Add Server kbds
|
||||
pb: string; // after them
|
||||
copy: string;
|
||||
};
|
||||
};
|
||||
footer: { join: string; disc: string };
|
||||
}
|
||||
|
||||
export const ui: Record<Lang, UI> = {
|
||||
en: {
|
||||
htmlLang: "en",
|
||||
copied: "Copied!",
|
||||
nav: { status: "Status", features: "Features", join: "How to Join", play: "Play Now" },
|
||||
hero: {
|
||||
eyebrow: "Modded LAN · NeoForge",
|
||||
tagline: "Kitchen-sink survival, built to outlast the weekend.",
|
||||
sub:
|
||||
"A self-hosted modded server for the crew — a curated tech + magic + " +
|
||||
"exploration pack, your own skins, one-click setup. Grab the address, " +
|
||||
"import the pack, dig in.",
|
||||
copyIp: "Copy IP",
|
||||
howToJoin: "How to Join",
|
||||
metaModpack: "NeoForge modpack",
|
||||
metaLan: "LAN-only",
|
||||
},
|
||||
status: {
|
||||
eyebrow: "Server",
|
||||
title: "This is what you'll see.",
|
||||
lead: "Exactly the entry that shows up in your multiplayer list.",
|
||||
modded: "Modded",
|
||||
motd: "NeoForge modpack · Simple Voice Chat · LAN-only",
|
||||
upTo: "up to",
|
||||
},
|
||||
statLabels: ["Mods", "NeoForge", "Player slots", "World backups"],
|
||||
features: {
|
||||
eyebrow: "What makes it Ulicraft",
|
||||
title: "Built for the crew.",
|
||||
lead:
|
||||
"Self-hosted, curated, and persistent — a modded server that respects " +
|
||||
"your time and outlives the LAN.",
|
||||
items: [
|
||||
{
|
||||
h: "Kitchen-sink pack",
|
||||
p: "~50–100 hand-picked mods — tech, magic, exploration and QoL. NeoForge 1.21.1, curated, no bloat.",
|
||||
},
|
||||
{
|
||||
h: "Your identity, self-hosted",
|
||||
p: "Drasl runs accounts and skins on the LAN. No Mojang login needed — your skin and cape just work.",
|
||||
},
|
||||
{
|
||||
h: "One-click setup",
|
||||
p: "packwiz installs and updates the whole modpack inside your launcher, so everyone stays in sync.",
|
||||
},
|
||||
{
|
||||
h: "Built to last",
|
||||
p: "Not a weekend throwaway — it runs on the homelab with 6-hour world backups. Your base survives.",
|
||||
},
|
||||
],
|
||||
},
|
||||
join: {
|
||||
eyebrow: "How to Join · 4 Steps",
|
||||
title: "From zero to spawning in.",
|
||||
lead: "One-time setup. After this, the launcher keeps you in sync.",
|
||||
s1: {
|
||||
kicker: "Launcher",
|
||||
h: "Download {name}",
|
||||
p: "Grab {name} for your system — a Prism-based launcher with authlib-injector support built in.",
|
||||
},
|
||||
s2: {
|
||||
kicker: "Account",
|
||||
h: "Add your account",
|
||||
pa: "In the launcher, add an",
|
||||
pb: "account using this URL, then log in with your Ulicraft credentials.",
|
||||
copy: "Copy",
|
||||
registerPre: "Need credentials? Register at",
|
||||
caPre: "Offline party? Download and trust the",
|
||||
caLink: "local CA certificate",
|
||||
caPost: "so the game-file mirror works over HTTPS.",
|
||||
},
|
||||
s3: {
|
||||
kicker: "Modpack",
|
||||
h: "Import the modpack",
|
||||
p: "Add a new instance from this packwiz URL — it installs and updates the whole pack.",
|
||||
},
|
||||
s4: {
|
||||
kicker: "Connect",
|
||||
h: "Join the server",
|
||||
pa: "Launch the instance, open",
|
||||
pb: ", paste the address, and join.",
|
||||
copy: "Copy IP",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
join: "How to Join",
|
||||
disc: "Not affiliated with Mojang or Microsoft. Minecraft is a trademark of Mojang AB.",
|
||||
},
|
||||
},
|
||||
|
||||
es: {
|
||||
htmlLang: "es",
|
||||
copied: "¡Copiado!",
|
||||
nav: { status: "Estado", features: "Características", join: "Cómo entrar", play: "Jugar ahora" },
|
||||
hero: {
|
||||
eyebrow: "LAN con mods · NeoForge",
|
||||
tagline: "Supervivencia todo incluido, para durar más que el finde.",
|
||||
sub:
|
||||
"Un servidor con mods autoalojado para la cuadrilla: un pack curado de " +
|
||||
"tecnología, magia y exploración, tus propias skins y configuración en un " +
|
||||
"clic. Copia la dirección, importa el pack y a cavar.",
|
||||
copyIp: "Copiar IP",
|
||||
howToJoin: "Cómo entrar",
|
||||
metaModpack: "Pack NeoForge",
|
||||
metaLan: "Solo LAN",
|
||||
},
|
||||
status: {
|
||||
eyebrow: "Servidor",
|
||||
title: "Esto es lo que verás.",
|
||||
lead: "La misma entrada que aparece en tu lista de multijugador.",
|
||||
modded: "Con mods",
|
||||
motd: "Pack NeoForge · Simple Voice Chat · Solo LAN",
|
||||
upTo: "hasta",
|
||||
},
|
||||
statLabels: ["Mods", "NeoForge", "Plazas", "Copias del mundo"],
|
||||
features: {
|
||||
eyebrow: "Qué hace especial a Ulicraft",
|
||||
title: "Hecho para la cuadrilla.",
|
||||
lead:
|
||||
"Autoalojado, curado y persistente: un servidor con mods que respeta tu " +
|
||||
"tiempo y sobrevive a la LAN.",
|
||||
items: [
|
||||
{
|
||||
h: "Pack todo incluido",
|
||||
p: "~50–100 mods elegidos a mano: tecnología, magia, exploración y QoL. NeoForge 1.21.1, curado y sin relleno.",
|
||||
},
|
||||
{
|
||||
h: "Tu identidad, autoalojada",
|
||||
p: "Drasl gestiona cuentas y skins en la LAN. Sin login de Mojang: tu skin y tu capa funcionan sin más.",
|
||||
},
|
||||
{
|
||||
h: "Configuración en un clic",
|
||||
p: "packwiz instala y actualiza todo el modpack dentro de tu launcher, así todos vais sincronizados.",
|
||||
},
|
||||
{
|
||||
h: "Hecho para durar",
|
||||
p: "No es de usar y tirar: corre en el homelab con copias del mundo cada 6 horas. Tu base sobrevive.",
|
||||
},
|
||||
],
|
||||
},
|
||||
join: {
|
||||
eyebrow: "Cómo entrar · 4 pasos",
|
||||
title: "De cero a aparecer en el mundo.",
|
||||
lead: "Configuración única. Después, el launcher te mantiene sincronizado.",
|
||||
s1: {
|
||||
kicker: "Launcher",
|
||||
h: "Descarga {name}",
|
||||
p: "Coge {name} para tu sistema: un launcher basado en Prism con soporte authlib-injector integrado.",
|
||||
},
|
||||
s2: {
|
||||
kicker: "Cuenta",
|
||||
h: "Añade tu cuenta",
|
||||
pa: "En el launcher, añade una cuenta",
|
||||
pb: "con esta URL y luego inicia sesión con tus credenciales de Ulicraft.",
|
||||
copy: "Copiar",
|
||||
registerPre: "¿Sin credenciales? Regístrate en",
|
||||
caPre: "¿Fiesta sin internet? Descarga y confía en el",
|
||||
caLink: "certificado CA local",
|
||||
caPost: "para que el mirror de archivos funcione por HTTPS.",
|
||||
},
|
||||
s3: {
|
||||
kicker: "Modpack",
|
||||
h: "Importa el modpack",
|
||||
p: "Añade una instancia nueva desde esta URL de packwiz: instala y actualiza todo el pack.",
|
||||
},
|
||||
s4: {
|
||||
kicker: "Conectar",
|
||||
h: "Entra al servidor",
|
||||
pa: "Lanza la instancia, abre",
|
||||
pb: ", pega la dirección y entra.",
|
||||
copy: "Copiar IP",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
join: "Cómo entrar",
|
||||
disc: "No afiliado a Mojang ni Microsoft. Minecraft es una marca de Mojang AB.",
|
||||
},
|
||||
},
|
||||
|
||||
eu: {
|
||||
htmlLang: "eu",
|
||||
copied: "Kopiatuta!",
|
||||
nav: { status: "Egoera", features: "Ezaugarriak", join: "Nola sartu", play: "Jokatu orain" },
|
||||
hero: {
|
||||
eyebrow: "Mod-dun LANa · NeoForge",
|
||||
tagline: "Mod ugariko biziraupena, irauteko egina.",
|
||||
sub:
|
||||
"Koadrilarentzako mod-dun zerbitzari auto-ostatatua: teknologia, magia eta " +
|
||||
"esplorazio pack zaindua, zure azalak eta konfigurazioa klik bakarrean. " +
|
||||
"Hartu helbidea, inportatu packa eta hasi zulatzen.",
|
||||
copyIp: "Kopiatu IPa",
|
||||
howToJoin: "Nola sartu",
|
||||
metaModpack: "NeoForge packa",
|
||||
metaLan: "LAN soilik",
|
||||
},
|
||||
status: {
|
||||
eyebrow: "Zerbitzaria",
|
||||
title: "Hau ikusiko duzu.",
|
||||
lead: "Zure multijokalari-zerrendan agertzen den sarrera bera.",
|
||||
modded: "Mod-duna",
|
||||
motd: "NeoForge packa · Simple Voice Chat · LAN soilik",
|
||||
upTo: "gehienez",
|
||||
},
|
||||
statLabels: ["Modak", "NeoForge", "Plazak", "Munduaren babeskopiak"],
|
||||
features: {
|
||||
eyebrow: "Zerk egiten du Ulicraft berezi",
|
||||
title: "Koadrilarentzat eginda.",
|
||||
lead:
|
||||
"Auto-ostatatua, zaindua eta iraunkorra: zure denbora errespetatzen duen " +
|
||||
"eta LANa gainditzen duen mod-dun zerbitzaria.",
|
||||
items: [
|
||||
{
|
||||
h: "Pack osoa",
|
||||
p: "Eskuz aukeratutako ~50–100 mod: teknologia, magia, esplorazioa eta QoL. NeoForge 1.21.1, zaindua eta betegarririk gabe.",
|
||||
},
|
||||
{
|
||||
h: "Zure nortasuna, auto-ostatatua",
|
||||
p: "Draslek kontuak eta azalak LANean kudeatzen ditu. Mojang loginik gabe: zure azalak eta kapak besterik gabe dabiltza.",
|
||||
},
|
||||
{
|
||||
h: "Konfigurazioa klik batean",
|
||||
p: "packwiz-ek modpack osoa instalatu eta eguneratzen du zure launcherrean, denok sinkronizatuta egoteko.",
|
||||
},
|
||||
{
|
||||
h: "Irauteko eginda",
|
||||
p: "Ez da behin erabili eta botatzekoa: homelab-ean dabil, munduaren babeskopiak 6 orduro. Zure basea bizirik.",
|
||||
},
|
||||
],
|
||||
},
|
||||
join: {
|
||||
eyebrow: "Nola sartu · 4 urrats",
|
||||
title: "Zerotik mundura agertzera.",
|
||||
lead: "Behin bakarrik konfiguratu. Gero, launcherrak sinkronizatuta mantenduko zaitu.",
|
||||
s1: {
|
||||
kicker: "Launcherra",
|
||||
h: "Deskargatu {name}",
|
||||
p: "Hartu {name} zure sistemarako: Prism-en oinarritutako launcherra, authlib-injector euskarriarekin.",
|
||||
},
|
||||
s2: {
|
||||
kicker: "Kontua",
|
||||
h: "Gehitu zure kontua",
|
||||
pa: "Launcherrean, gehitu",
|
||||
pb: "kontu bat URL honekin, eta gero hasi saioa zure Ulicraft kredentzialekin.",
|
||||
copy: "Kopiatu",
|
||||
registerPre: "Kredentzialik ez? Erregistratu hemen:",
|
||||
caPre: "Internetik gabeko festa? Deskargatu eta fidatu",
|
||||
caLink: "tokiko CA ziurtagiriaz",
|
||||
caPost: "fitxategi-mirrorrak HTTPS bidez ibil dadin.",
|
||||
},
|
||||
s3: {
|
||||
kicker: "Modpacka",
|
||||
h: "Inportatu modpacka",
|
||||
p: "Gehitu instantzia berri bat packwiz URL honetatik: pack osoa instalatu eta eguneratzen du.",
|
||||
},
|
||||
s4: {
|
||||
kicker: "Konektatu",
|
||||
h: "Sartu zerbitzarira",
|
||||
pa: "Abiarazi instantzia, ireki",
|
||||
pb: ", itsatsi helbidea eta sartu.",
|
||||
copy: "Kopiatu IPa",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
join: "Nola sartu",
|
||||
disc: "Ez dago Mojang edo Microsoft-ekin lotuta. Minecraft Mojang AB-ren marka da.",
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user