diff --git a/.claude/skills/nuxt-seo/SKILL.md b/.claude/skills/nuxt-seo/SKILL.md index 4938e011..621ced47 100644 --- a/.claude/skills/nuxt-seo/SKILL.md +++ b/.claude/skills/nuxt-seo/SKILL.md @@ -2,56 +2,54 @@ name: nuxt-seo description: >- Covers SEO for this Nuxt 4 site — canonical and hreflang ownership via - useLocaleHead, @nuxtjs/i18n locale objects, @nuxtjs/seo module version floors, - site URL resolution, Schema.org identity and @ids, robots directives, sitemap - sources, and OG image generation with nuxt-og-image. Use when editing the i18n, - site, sitemap, robots, schemaOrg or ogImage blocks in nuxt.config.ts, when - touching usePageSeo, useArticleSeo, useLocaleHead, useHead, useSeoMeta, + @nuxtjs/i18n's strictSeo, @nuxtjs/i18n locale objects, @nuxtjs/seo module version + floors, site URL resolution, Schema.org identity and @ids, robots directives, + sitemap sources, and OG image generation with nuxt-og-image. Use when editing the + i18n, site, sitemap, robots, schemaOrg or ogImage blocks in nuxt.config.ts, when + touching usePageSeo, useArticleSeo, useHead, useSeoMeta, defineOgImage or JSON-LD, when adding a locale or a page that needs metadata, or when hreflang, canonical tags, og:image, robots.txt or sitemap.xml entries are missing, duplicated or wrong. --- -Nuxt 4.4 · @nuxtjs/seo 5.1 · @nuxtjs/i18n 10.3 · @nuxt/content 3.14 · Tailwind 4.3 · Cloudflare Workers + D1 +Nuxt 4.5 · @nuxtjs/seo 5.3 · @nuxtjs/i18n 10.6 · @nuxt/content 3.16 · Tailwind 4.3 · Cloudflare Workers + D1 ## Emitter ownership map | tag | emitted by | file | |---|---|---| -| `rel=canonical`/`rel=alternate` (hreflang), `og:locale` | `useLocaleHead()` | `layouts/default.vue` | -| title/description, robots, OG/Twitter, per-page JSON-LD | `usePageSeo()`/`useArticleSeo()` | `composables/*Seo.ts` | +| `rel=canonical`/`rel=alternate` (hreflang), `og:locale`, `` | `@nuxtjs/i18n`, `experimental.strictSeo` | `nuxt.config.ts` (`i18n`) | +| title/description, robots, OG/Twitter, per-page JSON-LD | `usePageSeo()`/`useArticleSeo()` | `layers/*/composables/*Seo.ts` | | sitemap: extra sources / a collection's own `path`-`loc` fix | `sitemap.sources` / — | `server/api/__sitemap__/` / `content.config.ts` (`nuxt-content-cms`) | -Hard rule: no `rel: 'canonical'`/`rel: 'alternate'` outside `layouts/default.vue`. +Hard rule: no `rel: 'canonical'`/`rel: 'alternate'` in app code, and no +`useLocaleHead()`. `strictSeo` owns those tags, and AGENTS.md says +`useLocaleHead` is not allowed in this mode. ## Locale objects need `language`, never `iso` `@nuxtjs/i18n` reads `locale.language` to build the hreflang map (`node_modules/@nuxtjs/i18n/dist/runtime/kit/head.js`, `createLocaleMap`) — a locale with no `language` is skipped with a console warning and gets no -alternate link at all. This repo's `nuxt.config.ts` writes `iso: 'de-DE'` / -`iso: 'en-US'` instead: write `{code: 'de', language: 'de-DE', …}`, never +alternate link at all. Write `{code: 'de', language: 'de-DE', …}`, never `{code: 'de', iso: 'de-DE', …}`. TypeScript never flags it — `LocaleObject` carries an index signature, so `iso` type-checks fine and is ignored at -runtime. Migrating the existing `de`/`en` entries is out of scope; that does -**not** license copying the mistake into a new one. Grep any diff that adds a -locale for `iso:`; that string should not appear in it. +runtime. Grep any diff that adds a locale for `iso:`; that string should not +appear in it. ## Env blocks concatenate arrays `$production` (and `$env.`, and layer `extends`) merge into the base config through `c12`'s `merger`, which for two arrays does `obj[key] = -obj[key].concat(value)` (`@nuxt/kit`'s `loadNuxtConfig`), never a replace. Four -footguns in this repo's `$production` block: `i18n.locales`, -`schemaOrg.identity.sameAs`, `schemaOrg.identity.contactPoint` and -`image.format` are each restated in full there, so `concat` doubles every entry -that also exists in the base config. Add a locale (or a `sameAs` link) to the -base config only — never to both, the bug this skill's own baseline reproduced. +obj[key].concat(value)` (`@nuxt/kit`'s `loadNuxtConfig`), never a replace. An +array declared in both the base config and `$production` is doubled, not +overridden. `tests/architecture/production-config.spec.ts` enforces this. Add a +locale (or a `sameAs` link) to the base config only — never to both. ## `@nuxtjs/seo` is a meta-module — read `node_modules/`, not the lockfile It ships no features of its own. It declares a floor per sub-module in -`moduleDependencies` (`@nuxtjs/seo@5.1.4`'s `dist/module.mjs`); Nuxt resolves +`moduleDependencies` (`@nuxtjs/seo@5.3.16`'s `dist/module.mjs`); Nuxt resolves each name from the **project's** `node_modules` and semver-checks it there. Those floors, not the `dependencies` ranges, are what fails a build: @@ -62,14 +60,20 @@ Those floors, not the `dependencies` ranges, are what fails a build: | `nuxt-link-checker` | ≥4.3 | — | — | — | — | Diagnostic: what loads is whatever `node_modules//package.json` says. A -direct pin in this repo's `package.json` wins over the newer copy `@nuxtjs/seo` -drags into its own tree, and a lockfile lists both — so the lockfile alone can -never answer this. Worked example: `package.json` pins robots 5.7.1, sitemap -7.6.0, schema-org 5.0.10; `@nuxtjs/seo`'s `dependencies` additionally resolve -6.1.2 / 8.0.15 / 6.2.3 under `node_modules/.pnpm/`. Only the pins load (grep -`.nuxt/` for `.pnpm/@nuxtjs+sitemap@`), all clear the floors above, and the -build is clean — but the API you get is v7's. Tell the two apart by export: -`defineSitemapSchema` ⇒ sitemap v8, only `asSitemapCollection` ⇒ v7 (this repo). +lockfile can list more than one copy of a module, so read `node_modules`, not +the lockfile. Current resolved versions: `@nuxtjs/robots` 6.2.4, +`@nuxtjs/sitemap` 8.5.1, `nuxt-schema-org` 6.4.3, `nuxt-og-image` 6.10.3 (direct +pins in `package.json`), `nuxt-seo-utils` 8.6.1, `nuxt-site-config` 4.2.3 and +`nuxt-link-checker` 5.3.0 (not direct dependencies; resolved through +`@nuxtjs/seo` under `node_modules/.pnpm/`). All clear the floors above. +`@nuxtjs/seo`'s own `dependencies` ranges (`^6.1.5`, `^8.3.4`, `^6.2.9`) are +satisfied by the pins, so there is one copy of each. + +The sitemap major version is not told by export names: `dist/content.mjs` of +8.5.1 exports both `asSitemapCollection` and `defineSitemapSchema`. This repo's +`content.config.ts` uses `defineSitemapSchema` (imported from +`@nuxtjs/sitemap/content`) and never `asSitemapCollection`. Read the version +from `node_modules/@nuxtjs/sitemap/package.json`. ## One site URL — but `i18n.baseUrl` outranks it @@ -79,7 +83,7 @@ entry at `_priority: -1`, above `nuxt.config.ts`'s `site` block (`-3`). But the entry it pushes from `i18n.baseUrl` carries **no** `_priority` (so, 0) and is pushed last, and the stack sorts ascending with later entries overwriting — so `i18n.baseUrl` beats `NUXT_SITE_URL`, at build and at request time. This repo -sets `i18n.baseUrl` (`nuxt.config.ts:94`), so setting `NUXT_SITE_URL` alone +sets `i18n.baseUrl` (`nuxt.config.ts:142`), so setting `NUXT_SITE_URL` alone would silently do nothing. Change `i18n.baseUrl` too, or drop it so the env var can win; either way don't hand-edit `site.url` per environment block. @@ -102,17 +106,25 @@ of the sitemap automatically. 2. **Every `defineOgImage()` call uses a locally-owned template**, not one of the 12 the module ships under `runtime/app/components/Templates/Community/` — they live inside the module, not under this repo's `components/OgImage/`. - Here `composables/usePageSeo.ts` and `composables/useArticleSeo.ts` both call - `defineOgImage('NuxtSeo', …)` while only `TeamMember.satori.vue` exists - locally. `npx nuxt-og-image eject NuxtSeo` copies it to - `components/OgImage/NuxtSeo.satori.vue`, where you own it. + `npx nuxt-og-image eject NuxtSeo` copies it to + `components/OgImage/NuxtSeo.satori.vue`, where you own it. Current state: + `layers/content-core/composables/usePageSeo.ts` and + `layers/blog/composables/useArticleSeo.ts` call + `defineOgImage('NuxtSeo.satori', …)`. The module's own copy matches that + name too, so confirm the rendered image, not just the call. 3. **The renderer suffix matches the template file** — `.satori.vue`, `.takumi.vue` or `.browser.vue`; v6 picks the renderer from the filename, not a `defaults` config key (`nuxt-og-image/dist/runtime/types.d.ts`). + The suffix in the call name (`'NuxtSeo.satori'`) is what selects the + Satori variant when the module also ships a takumi one. -Precondition 1 alone is not enough: a prerender crawl still renders the -un-ejected community template unless 2 is also done. Both are real, open -defects here; fixing them is out of scope. +Current state: `nuxt.config.ts` sets `ogImage.zeroRuntime: true` (line ~447) +and has no `nitro` block, so precondition 1 fails and no OG images are +generated. Pages fall back to `/images/og-default.png` +(`layers/content-core/utils/socialImage.ts`). The ejected template +`components/OgImage/NuxtSeo.satori.vue` still renders the Nuxt SEO logo and +wordmark, so it has to be replaced with brand artwork before a prerender pass +ships its output. ## References @@ -144,8 +156,9 @@ consent mechanism exists. fields, a `noindex` route that still appears in the sitemap. Fix the composable or config it points at; re-run step 1. 3. View-source a page and count `rel="canonical"` (must be 1) and - `rel="alternate"` (must equal the locale count) — more than one canonical, - or one missing alternate, means a second emitter crept in. + `rel="alternate"` (one per locale the page publishes route params for; + `strictSeo` omits the rest) — more than one canonical, or an alternate + missing for a locale that has the page, means a second emitter crept in. 4. CI gates: `.github/workflows/seo.yml` runs the same check plus Lighthouse against `.lighthouserc.json` (`categories:seo` ≥ 0.95). Repeat from step 1 until both pass locally. diff --git a/.claude/skills/nuxt-seo/references/i18n-sitemap-robots.md b/.claude/skills/nuxt-seo/references/i18n-sitemap-robots.md index 1a6d0161..97a14c62 100644 --- a/.claude/skills/nuxt-seo/references/i18n-sitemap-robots.md +++ b/.claude/skills/nuxt-seo/references/i18n-sitemap-robots.md @@ -9,9 +9,8 @@ each internally flagged `includeAppSources: true`. The children do **not** live at `/sitemap_.xml`. They live under `sitemapsPathPrefix` (default `/__sitemap__/`), named from -`_sitemap = locale.language || locale.code`. The module normalises a legacy -`iso:` key into `language` itself (`dist/module.mjs`, `normalizeLocales`), so -this repo's `iso: 'de-DE'` entries still yield `/__sitemap__/de-DE.xml` and +`_sitemap = locale.language || locale.code`. This repo's locales set `language` +(`de-DE`, `en-US`), so the children are `/__sitemap__/de-DE.xml` and `/__sitemap__/en-US.xml`. The index is `/sitemap_index.xml`; `/sitemap.xml` does not aggregate them — it gets a `routeRules` **redirect** to `/sitemap_index.xml`. Fetch `/sitemap_index.xml` when you want the list of @@ -44,10 +43,10 @@ used internally for i18n `pages` mappings, not something a hand-written Content that isn't a URL-bearing page collection (this repo's `team`, a `type: 'data'` collection) doesn't produce sitemap entries on its own. -`server/api/__sitemap__/team.ts` is registered via `sitemap.sources: ['/api/__sitemap__/team']` -in `nuxt.config.ts` and returns a flat `{ loc }[]` array, one entry per -locale × member — the same pattern to follow for any other non-page content -that needs sitemap URLs. See `nuxt-content-cms` for the split: page +`server/api/__sitemap__/team.ts` is one of three handlers registered via +`sitemap.sources` in `nuxt.config.ts` (with `events` and `blog-authors`). Each +returns a flat `{ loc }[]` array, one entry per locale × item — the same +pattern to follow for any other non-page content that needs sitemap URLs. See `nuxt-content-cms` for the split: page collections' own `path`/`loc` bugs are fixed in `content.config.ts`; sources like this one live in `nuxt.config.ts`/`server/api/__sitemap__/`. @@ -60,15 +59,12 @@ renders the columns `sitemap.xslColumns` declares, emitting each one as `` inside a `for-each` over `sitemap:urlset/sitemap:url`. -This repo's `nuxt.config.ts` adds a `Language` column with -`select: 'sitemap:hreflang'` — that XPath asks for a child *element* named -`hreflang` in the sitemap namespace, and no such element exists, so the column -renders empty on every row. The alternates are an *attribute* on an element in -the `xhtml` namespace (which the stylesheet does declare), so the expression -would have to be something like `xhtml:link/@hreflang`. Fixing `nuxt.config.ts` -is out of scope here — just don't read the empty column as "hreflang missing". -Diagnostic: never trust the XSL-rendered table; view-source or `curl` the XML -directly and grep for `hreflang=`. +A `sitemap.xslColumns` entry that shows alternates must select +`xhtml:link/@hreflang` (this repo's `Language` column does). `sitemap:hreflang` +asks for a child element in the sitemap namespace that does not exist, so that +column renders empty on every row. Don't read an empty column as "hreflang +missing". Diagnostic: never trust the XSL-rendered table; view-source or `curl` +the XML directly and grep for `hreflang=`. ## Robots: `routeRules` is not auto-translated per locale diff --git a/.claude/skills/nuxt-seo/references/og-images.md b/.claude/skills/nuxt-seo/references/og-images.md index ae57ec27..d144f609 100644 --- a/.claude/skills/nuxt-seo/references/og-images.md +++ b/.claude/skills/nuxt-seo/references/og-images.md @@ -19,7 +19,7 @@ composable, and pass it in as a prop: ```ts // composable — resolve locale-aware strings first -defineOgImage('NuxtSeo', { +defineOgImage('NuxtSeo.satori', { title: opts.title || site.name, description: pageDescription.value }) @@ -70,14 +70,15 @@ automatically. `NuxtSeo` ships both a `.satori.vue` and a `.takumi.vue`). Don't read the `COMMUNITY_TEMPLATES = ["NuxtSeo", "Brutalist", "SimpleBlog"]` constant in `dist/cli.cjs` as the full list: it is only the subset the v6 migration -command scans for. Calling `defineOgImage('NuxtSeo', …)` -works in dev because the dev server can render the module's own copy on -demand, but the module's own CLI treats this as unfinished migration work — -`npx nuxt-og-image eject ` copies the template source into -`components/OgImage/.satori.vue` in this repo, where you can edit it and -where `zeroRuntime`'s prerender pass can pick it up as a local file. See the -top-level "`zeroRuntime` has three preconditions" section — ejecting the -template is precondition 2, independent of prerendering (precondition 1). +command scans for. A `defineOgImage('NuxtSeo…')` call works in dev because the +dev server can render the module's own copy on demand, but the module's CLI +treats that as unfinished migration work — `npx nuxt-og-image eject ` +copies the template source into `components/OgImage/.satori.vue` in this +repo, where you can edit it and where `zeroRuntime`'s prerender pass can pick +it up as a local file. The `NuxtSeo` template is ejected here +(`components/OgImage/NuxtSeo.satori.vue`). See the top-level "`zeroRuntime` has +three preconditions" section — ejecting is precondition 2, independent of +prerendering (precondition 1). ## Fonts diff --git a/.claude/skills/nuxt-seo/references/seo-utils-defaults.md b/.claude/skills/nuxt-seo/references/seo-utils-defaults.md index 68c5bd5f..71a3106e 100644 --- a/.claude/skills/nuxt-seo/references/seo-utils-defaults.md +++ b/.claude/skills/nuxt-seo/references/seo-utils-defaults.md @@ -3,24 +3,20 @@ `nuxt-seo-utils` (bundled by `@nuxtjs/seo`) runs an `applyDefaults()` plugin with `automaticDefaults: true` by default — no config in this repo turns it on, it just runs. It pushes its own `og:locale`, `og:url`, `og:site_name`, -title template and a `rel="canonical"` link, all at `tagPriority: 'low'`. In -practice this means it's a **fallback**, not a second live emitter: Unhead's -tag-priority dedup keeps `layouts/default.vue`'s normal-priority -`useLocaleHead()` canonical over this low-priority one, so the "one emitter" -rule in the top-level `Emitter ownership map` still holds today. It only -becomes live output if the app-wide `useLocaleHead()` call is ever removed — -worth knowing before you assume canonical has no other source when tracing a -head-rendering bug. +title template and a `rel="canonical"` link, all at `tagPriority: 'low'`. It is +a **fallback**, not a second live emitter: a low-priority tag yields to any +normal-priority canonical, and in this repo the canonical comes from +`@nuxtjs/i18n` (`strictSeo`). It only shows when nothing else sets one. Check +view-source before assuming which canonical is live. ## `canonicalLowercase` (default: `true`) The fallback canonical above lowercases the URL path -(`url.toLocaleLowerCase(locale)`). `useLocaleHead()`'s hreflang self-reference -does **not** lowercase. `usePageSeo.ts`'s own comment states canonical "must -be byte-identical to this locale's self-referencing hreflang entry" — if this -fallback ever becomes the active canonical (see above) on a route with any -uppercase path segment (a slug, for instance), that byte-identity breaks -silently. Currently masked by the low-priority override, not fixed. +(`url.toLocaleLowerCase(locale)`). `usePageSeo.ts:40` requires the canonical to +be byte-identical to this locale's self-referencing hreflang entry (emitted by +`@nuxtjs/i18n`). If the fallback ever becomes the active canonical on a route +with an uppercase path segment (a slug, for instance), that identity breaks +silently. ## `canonicalQueryWhitelist` (default: a fixed list) diff --git a/content/about/de/home.json b/content/about/de/home.json index 9eb05d26..46586156 100644 --- a/content/about/de/home.json +++ b/content/about/de/home.json @@ -1,6 +1,6 @@ { "key": "about", - "who": "OneLiteFeather ist ein Minecraft-Netzwerk, das von seiner Community lebt – und von Menschen, die gerne bauen, spielen und entwickeln. 2021 als OneLiteFeather gegründet, wachsen wir seitdem Stück für Stück: mit eigenen Spielmodi, Gemeinschaftsbauten und Software, die wir offen teilen.", + "who": "OneLiteFeather ist ein Minecraft-Netzwerk, das von seiner Community lebt – und von Menschen, die gerne bauen, spielen und entwickeln. 2019 als OneLiteFeather gegründet, wachsen wir seitdem Stück für Stück: mit eigenen Spielmodi, Gemeinschaftsbauten und Software, die wir offen teilen.", "pillars": [ { "icon": "cube", diff --git a/content/about/en/home.json b/content/about/en/home.json index b9d939c1..9c1a2932 100644 --- a/content/about/en/home.json +++ b/content/about/en/home.json @@ -1,6 +1,6 @@ { "key": "about", - "who": "OneLiteFeather is a Minecraft network that lives on its community – and on people who love to build, play and develop. Founded in 2021 as OneLiteFeather, we have been growing step by step ever since: with our own game modes, community builds and software that we share openly.", + "who": "OneLiteFeather is a Minecraft network that lives on its community – and on people who love to build, play and develop. Founded in 2019 as OneLiteFeather, we have been growing step by step ever since: with our own game modes, community builds and software that we share openly.", "pillars": [ { "icon": "cube", diff --git a/content/blog/de/dev-blog-1.md b/content/blog/de/dev-blog-1.md index 4863c879..5f5e3a6a 100644 --- a/content/blog/de/dev-blog-1.md +++ b/content/blog/de/dev-blog-1.md @@ -4,7 +4,7 @@ alternativeTitle: 'Welche Technologien wir nutzen und warum' description: 'Heute möchte ich aus unserem Team die aktiv verwenden Technologien vorstellen und was die für Vorteile uns bringen. ' pubDate: '2023-10-21' headerImage: 'images/blog/dev-blog-1.webp' -headerImageAlt: 'Title Bild für DevBlog #1' +headerImageAlt: 'Konferenzraum mit einem Vortragenden vor einer Wand voller Dashboards und Diagramme' slug: 'dev-blog-1-was-wir-verwenden' translationKey: 'dev-blog-1-what-we-using' author: themeinerlp diff --git a/content/blog/de/everything-you-need-to-know-about-ethanol.md b/content/blog/de/everything-you-need-to-know-about-ethanol.md index 2749c052..a67c3bec 100644 --- a/content/blog/de/everything-you-need-to-know-about-ethanol.md +++ b/content/blog/de/everything-you-need-to-know-about-ethanol.md @@ -1,10 +1,10 @@ --- -title: 'Alles, was du über Ethanol wissen solltest' +title: 'Ethanol-Malware: Was du wissen solltest' alternativeTitle: 'Schon jetzt auf mehreren hundert Servern, was sich dahinter verbirgt' -description: 'Relativ unbekannt und trotzdem schon jetzt auf mehreren hundert Servern. Genauso wie Fractureiser wird diese von dem User mori0 / Riesenrad beworben, um Server zu “trollen”. Was sich dahinter verbirgt.' +description: 'Kaum bekannt und doch auf mehreren hundert Servern. Wie Fractureiser wird es von mori0 / Riesenrad beworben, um Server zu „trollen“. Was dahintersteckt.' pubDate: '2024-07-23' headerImage: 'images/blog/cyber-theDigitalArtist-Ethanol-Post.webp' -headerImageAlt: 'Ethanol Malware Post Bild' +headerImageAlt: 'Grüner Textregen aus Sicherheitsbegriffen wie Malware, Trojaner und Ransomware, im Stil der Matrix' slug: 'alles-was-man-ueber-ethanol-wissen-sollte' translationKey: 'everything-you-need-to-know-about-ethanol' tags: @@ -26,7 +26,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/everything-you-need-to-know-about-ethanol' schemaOrg: - type: 'BlogPosting' - headline: 'Alles, was du über Ethanol wissen solltest' + headline: 'Ethanol-Malware: Was du wissen solltest' alternativeHeadline: 'Schon jetzt auf mehreren hundert Servern, was sich dahinter verbirgt' description: 'Relativ unbekannt und trotzdem schon jetzt auf mehreren hundert Servern. Genauso wie Fractureiser wird diese von dem User mori0 / Riesenrad beworben, um Server zu “trollen”. Was sich dahinter verbirgt.' author: diff --git a/content/blog/de/otis-spielerstammdaten-minecraft.md b/content/blog/de/otis-spielerstammdaten-minecraft.md index 1bc95712..7a8cda3f 100644 --- a/content/blog/de/otis-spielerstammdaten-minecraft.md +++ b/content/blog/de/otis-spielerstammdaten-minecraft.md @@ -1,7 +1,7 @@ --- -title: 'Otis: Zentrale Spielerstammdaten für Minecraft (Java & Micronaut)' +title: 'Otis: Spielerstammdaten für Minecraft' alternativeTitle: 'Mojang UUID v4, interne UUID v7, Name & Sprache als stabile Basis fürs Ökosystem' -description: 'Otis stellt Spielerstammdaten zentral bereit: Mojang UUID (v4), interne UUID (v7), Minecraft-Name und Sprache sowie optional erster/letzter Beitritt. Das reduziert manuelle Inselprozesse, unterstützt DSGVO-orientierte Datenflüsse und bildet die Grundlage für nachgelagerte Services wie Metadata, Bans und Discord Bots.' +description: 'Otis bündelt Spielerstammdaten: Mojang-UUID (v4), interne UUID (v7), Name, Sprache und optional Beitrittsdaten. Basis für Metadata, Bans und Discord-Bots.' pubDate: '2025-11-29' headerImage: 'images/blog/otis-player-data-header.png' headerImageAlt: 'Otis Microservice: zentrale Minecraft-Spielerdaten (Java, Micronaut)' @@ -28,7 +28,7 @@ alternates: schemaOrg: - type: 'BlogPosting' - headline: 'Otis: Zentrale Spielerstammdaten für Minecraft' + headline: 'Otis: Spielerstammdaten für Minecraft' alternativeHeadline: 'Java/Micronaut Microservice für UUIDs, Name und Sprache' description: 'Zentrale Spielerdaten als Schnittstelle im Ökosystem: weniger manuelle Dateninseln, klarere Verantwortlichkeiten und eine Basis für zuverlässige Services.' author: diff --git a/content/blog/de/plugins-open-for-adoption.md b/content/blog/de/plugins-open-for-adoption.md index 99c6b681..5575706f 100644 --- a/content/blog/de/plugins-open-for-adoption.md +++ b/content/blog/de/plugins-open-for-adoption.md @@ -1,10 +1,10 @@ --- -title: 'Wer möchte "Plugins" adoptieren ?' +title: 'Wer möchte Plugins adoptieren?' alternativeTitle: 'Maintainer für unsere Plugins gesucht' -description: 'Adoption von Plugins hilft uns und der Minecraft Community weiterhin sinnvolle Plugins/Software zu warten, dabei helfen wir als Organisation den Maintainer die Richtigen Personen oder Resourcen zu finden.' +description: 'Adoption hält sinnvolle Plugins für die Minecraft-Community am Leben. Wir helfen dabei, Maintainer und passende Ressourcen zu finden.' pubDate: '2025-02-05' headerImage: 'images/blog/plugin-adoptation.webp' -headerImageAlt: 'Wer möchte Plugins adoptieren ? Bild' +headerImageAlt: 'Illustration: Eine Person hält einen losen Stecker in der Hand, ein zweiter Stecker liegt am Boden' slug: 'wer-moechte-plugins-adoptieren' translationKey: 'plugins-open-for-adoption' tags: @@ -22,7 +22,7 @@ alternates: href: 'https://onelitefeather.net/de/blog/wer-moechte-plugins-adoptieren' schemaOrg: - type: 'BlogPosting' - headline: 'Wer möchte "Plugins" adoptieren ?' + headline: 'Wer möchte Plugins adoptieren?' alternativeHeadline: 'Maintainer für unsere Plugins gesucht' description: 'Adoption von Plugins hilft uns und der Minecraft Community weiterhin sinnvolle Plugins/Software zu warten, dabei helfen wir als Organisation den Maintainer die Richtigen Personen oder Resourcen zu finden.' author: diff --git a/content/blog/de/resilienz-im-detail-priorityclasses-pdb-affinitaeten.md b/content/blog/de/resilienz-im-detail-priorityclasses-pdb-affinitaeten.md index 3744491d..01f4c20e 100644 --- a/content/blog/de/resilienz-im-detail-priorityclasses-pdb-affinitaeten.md +++ b/content/blog/de/resilienz-im-detail-priorityclasses-pdb-affinitaeten.md @@ -1,7 +1,7 @@ --- -title: 'Resilienz im Detail: PriorityClasses, PDBs und (Anti-)Affinitäten in unserem Kubernetes-Cluster' +title: 'Resilienz im Detail: PriorityClasses und PDBs' alternativeTitle: 'Deep Dive: Wie ich unser GitOps-Cluster gegen Ausfälle gehärtet habe' -description: 'Die technische Langfassung: Wie ich unser Kubernetes-Cluster über drei Härtungs-Säulen – Ressourcen, PodDisruptionBudgets und ein fünfstufiges PriorityClass-Schema – plus Zone- und Pod-Anti-Affinitäten ausfallsicherer gemacht habe. Inklusive der Stolpersteine: stillschweigend ignorierte Helm-Values, webhook-immutable Felder und ein MaxScale-Pod, der hartnäckig im Pending hängen blieb.' +description: 'Technische Langfassung: Unser Kubernetes-Cluster mit Ressourcen, PodDisruptionBudgets, PriorityClasses und Anti-Affinitäten härten – samt Stolpersteinen.' pubDate: '2026-06-13' headerImage: 'images/blog/cluster-topology-social.png' headerImageAlt: 'Topologie des feather-core-Clusters: Control-Plane, Ceph-Storage und Worker in Zone fr01' @@ -28,7 +28,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/resilience-in-detail-priorityclasses-pdb-affinities' schemaOrg: - type: 'BlogPosting' - headline: 'Resilienz im Detail: PriorityClasses, PDBs und (Anti-)Affinitäten in unserem Kubernetes-Cluster' + headline: 'Resilienz im Detail: PriorityClasses und PDBs' alternativeHeadline: 'Deep Dive: Wie ich unser GitOps-Cluster gegen Ausfälle gehärtet habe' description: 'Die technische Langfassung: Wie ich unser Kubernetes-Cluster über drei Härtungs-Säulen plus Zone- und Pod-Anti-Affinitäten ausfallsicherer gemacht habe.' author: diff --git a/content/blog/de/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md b/content/blog/de/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md index 0b752762..d4cc62ae 100644 --- a/content/blog/de/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md +++ b/content/blog/de/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md @@ -1,10 +1,10 @@ --- -title: 'Mit Proxmox und Ansible in eine Achterbahn der Automatisierung' +title: 'Mit Proxmox und Ansible automatisieren' alternativeTitle: 'Wie wir Infrastruktur mit Proxmox und Ansible automatisieren' -description: 'In der unendlichen Weite der digitalen Welt hat unser Team, OneLiteFeather, eine kleine aber feine Spielwiese gefunden, auf der wir unsere kreativen und technischen Visionen verwirklichen. Im Zentrum dieser Entdeckungsreise stehen Proxmox und Ansible, zwei Werkzeuge, die uns nicht nur den Rücken freihalten, sondern auch die Türen zu unerforschten Gebieten öffnen.' +description: 'Wie OneLiteFeather Infrastruktur mit Proxmox und Ansible automatisiert: zwei Werkzeuge, die uns den Rücken freihalten und neue Wege öffnen.' pubDate: '2023-10-15' headerImage: 'images/blog/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.webp' -headerImageAlt: 'Mit Proxmox und Ansible in eine Achterbahn der Automatisierung Bild' +headerImageAlt: 'Illustration: Felder, eine Stadt und eine Achterbahn, im Vordergrund ein Planer mit Bauplänen und ein Roboter' slug: 'mit-proxmox-und-ansible-in-eine-achterbahn-der-automatisierung-vserver' translationKey: 'proxmox-ansible-automation' tags: @@ -27,7 +27,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible' schemaOrg: - type: 'BlogPosting' - headline: 'Mit Proxmox und Ansible in eine Achterbahn der Automatisierung' + headline: 'Mit Proxmox und Ansible automatisieren' alternativeHeadline: 'Wie wir Infrastruktur mit Proxmox und Ansible automatisieren' description: 'In der unendlichen Weite der digitalen Welt hat unser Team, OneLiteFeather, eine kleine aber feine Spielwiese gefunden, auf der wir unsere kreativen und technischen Visionen verwirklichen. Im Zentrum dieser Entdeckungsreise stehen Proxmox und Ansible, zwei Werkzeuge, die uns nicht nur den Rücken freihalten, sondern auch die Türen zu unerforschten Gebieten öffnen.' author: diff --git a/content/blog/de/wenn-ein-server-ausfaellt-cluster-resilienz.md b/content/blog/de/wenn-ein-server-ausfaellt-cluster-resilienz.md index 3410129f..b3d9a005 100644 --- a/content/blog/de/wenn-ein-server-ausfaellt-cluster-resilienz.md +++ b/content/blog/de/wenn-ein-server-ausfaellt-cluster-resilienz.md @@ -1,7 +1,7 @@ --- -title: 'Wenn ein Server ausfällt: Wie wir unser Cluster widerstandsfähig gemacht haben' +title: 'Wenn ein Server ausfällt: Cluster-Resilienz' alternativeTitle: 'Resilienz für Einsteiger: Unsere Reise zu einem ausfallsicheren Kubernetes-Cluster' -description: 'Ein Server fällt aus – und niemand merkt es. Genau das war mein Ziel. Ich erzähle in Alltagsbildern, wie wir unser Cluster Stück für Stück widerstandsfähiger gemacht haben, welche Höhen und Tiefen die Reise mit sich brachte und was das ganz konkret für OneLiteFeather bedeutet.' +description: 'Ein Server fällt aus, und niemand merkt es – genau das war das Ziel. In Alltagsbildern, wie wir unseren Cluster Stück für Stück ausfallsicherer machten.' pubDate: '2026-06-13' headerImage: 'images/blog/cluster-topology-social.png' headerImageAlt: 'Topologie des feather-core-Clusters: Control-Plane, Ceph-Storage und Worker in Zone fr01' @@ -27,7 +27,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/when-a-server-fails-cluster-resilience' schemaOrg: - type: 'BlogPosting' - headline: 'Wenn ein Server ausfällt: Wie wir unser Cluster widerstandsfähig gemacht haben' + headline: 'Wenn ein Server ausfällt: Cluster-Resilienz' alternativeHeadline: 'Resilienz für Einsteiger: Unsere Reise zu einem ausfallsicheren Kubernetes-Cluster' description: 'Ein Server fällt aus – und niemand merkt es. Genau das war mein Ziel. Ich erzähle in Alltagsbildern, wie wir unser Cluster Stück für Stück widerstandsfähiger gemacht haben.' author: diff --git a/content/blog/en/dev-blog-1.md b/content/blog/en/dev-blog-1.md index 04ec7b65..383d3f85 100644 --- a/content/blog/en/dev-blog-1.md +++ b/content/blog/en/dev-blog-1.md @@ -4,7 +4,7 @@ alternativeTitle: 'What we use and why it helps' description: 'Today I would like to present you the active used technologies from our team and which advantages these offer to us...' pubDate: '2023-10-21' headerImage: 'images/blog/dev-blog-1.webp' -headerImageAlt: 'Title image for DevBlog #1' +headerImageAlt: 'Boardroom with a presenter in front of a wall of dashboards and diagrams' slug: 'dev-blog-1-what-we-using' translationKey: 'dev-blog-1-what-we-using' author: themeinerlp diff --git a/content/blog/en/everything-you-need-to-know-about-ethanol.md b/content/blog/en/everything-you-need-to-know-about-ethanol.md index bd8caa73..7767e4c4 100644 --- a/content/blog/en/everything-you-need-to-know-about-ethanol.md +++ b/content/blog/en/everything-you-need-to-know-about-ethanol.md @@ -1,10 +1,10 @@ --- -title: 'Everything You Need to Know About Ethanol' +title: 'Ethanol malware: what you need to know' alternativeTitle: 'Already present on several hundred servers, what lies behind it' -description: 'Relatively unknown yet already present on several hundred servers. Like Fractureiser, it is promoted by the user mori0 / Riesenrad to “troll” servers. What lies behind it.' +description: 'Little known, yet already on several hundred servers. Like Fractureiser, it is promoted by mori0 / Riesenrad to “troll” servers. What lies behind it.' pubDate: '2024-07-23' headerImage: 'images/blog/cyber-theDigitalArtist-Ethanol-Post.webp' -headerImageAlt: 'Ethanol Malware Post Image' +headerImageAlt: 'Green falling text of security terms such as malware, trojan and ransomware, in Matrix style' slug: 'everything-you-need-to-know-about-ethanol' translationKey: 'everything-you-need-to-know-about-ethanol' tags: @@ -22,7 +22,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/everything-you-need-to-know-about-ethanol' schemaOrg: - type: 'BlogPosting' - headline: 'Everything You Need to Know About Ethanol' + headline: 'Ethanol malware: what you need to know' alternativeHeadline: 'Already present on several hundred servers, what lies behind it' description: 'Relatively unknown yet already present on several hundred servers. Like Fractureiser, it is promoted by the user mori0 / Riesenrad to “troll” servers. What lies behind it.' author: diff --git a/content/blog/en/otis-regular-data-minecraft.md b/content/blog/en/otis-regular-data-minecraft.md index 1883f8cb..da008537 100644 --- a/content/blog/en/otis-regular-data-minecraft.md +++ b/content/blog/en/otis-regular-data-minecraft.md @@ -1,7 +1,7 @@ --- -title: 'Otis: Central player data for Minecraft (Java & Micronaut)' +title: 'Otis: Central player data for Minecraft' alternativeTitle: 'Mojang UUID v4, internal UUID v7, name & language as a stable base for the ecosystem' -description: 'Otis provides central player master data: Mojang UUID (v4), internal UUID (v7), Minecraft name, language, and optionally first/last join. This cuts manual one-off processes, supports GDPR-oriented data flows, and forms the foundation for downstream services like metadata, bans, and Discord bots.' +description: 'Central player data: Mojang UUID (v4), internal UUID (v7), name, language and optional join dates. Foundation for metadata, bans and Discord bots.' pubDate: '2025-11-29' headerImage: 'images/blog/otis-player-data-header.png' headerImageAlt: 'Otis Microservice: central Minecraft player data (Java, Micronaut)' diff --git a/content/blog/en/resilience-in-detail-priorityclasses-pdb-affinities.md b/content/blog/en/resilience-in-detail-priorityclasses-pdb-affinities.md index 7a87a0e7..99274c8b 100644 --- a/content/blog/en/resilience-in-detail-priorityclasses-pdb-affinities.md +++ b/content/blog/en/resilience-in-detail-priorityclasses-pdb-affinities.md @@ -1,7 +1,7 @@ --- -title: 'Resilience in Detail: PriorityClasses, PDBs and (Anti-)Affinities in Our Kubernetes Cluster' +title: 'Resilience in Detail: PriorityClasses and PDBs' alternativeTitle: 'Deep Dive: How I Hardened Our GitOps Cluster Against Failures' -description: 'The technical long-form: how I made our Kubernetes cluster more failure-resistant across three hardening pillars – resources, PodDisruptionBudgets and a five-tier PriorityClass scheme – plus zone and pod anti-affinities. Including the pitfalls: silently ignored Helm values, webhook-immutable fields, and a MaxScale pod that stubbornly stayed Pending.' +description: 'The technical deep dive: hardening our Kubernetes cluster with resources, PodDisruptionBudgets, PriorityClasses and anti-affinities, pitfalls included.' pubDate: '2026-06-13' headerImage: 'images/blog/cluster-topology-social.png' headerImageAlt: 'Topology of the feather-core cluster: control-plane, Ceph storage and workers in zone fr01' @@ -24,7 +24,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/resilience-in-detail-priorityclasses-pdb-affinities' schemaOrg: - type: 'BlogPosting' - headline: 'Resilience in Detail: PriorityClasses, PDBs and (Anti-)Affinities in Our Kubernetes Cluster' + headline: 'Resilience in Detail: PriorityClasses and PDBs' alternativeHeadline: 'Deep Dive: How I Hardened Our GitOps Cluster Against Failures' description: 'The technical long-form: how I made our Kubernetes cluster more failure-resistant across three hardening pillars plus zone and pod anti-affinities.' author: diff --git a/content/blog/en/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md b/content/blog/en/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md index 9830f638..a21d5dd3 100644 --- a/content/blog/en/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md +++ b/content/blog/en/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.md @@ -1,10 +1,10 @@ --- -title: 'Riding the Rollercoaster of Automation with Proxmox and Ansible' +title: 'Automating Proxmox with Ansible' alternativeTitle: 'How we automate infrastructure with Proxmox and Ansible' -description: 'In the boundless expanse of the digital world, our team, OneLiteFeather, has discovered a quaint yet vibrant playground where we realize our creative and technical visions. At the heart of this expedition are Proxmox and Ansible, two tools that not only have our backs but also open doors to uncharted territories.' +description: 'How OneLiteFeather automates its infrastructure with Proxmox and Ansible: two tools that have our backs and open doors to new territory.' pubDate: '2023-10-15' headerImage: 'images/blog/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible.webp' -headerImageAlt: 'Riding the Rollercoaster of Automation with Proxmox and Ansible Image' +headerImageAlt: 'Illustration of fields, a city and a roller coaster, with a planner studying blueprints and a robot in the foreground' slug: 'riding-the-rollercoaster-of-automation-with-proxmox-and-ansible' translationKey: 'proxmox-ansible-automation' tags: @@ -23,7 +23,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/riding-the-rollercoaster-of-automation-with-proxmox-and-ansible' schemaOrg: - type: 'BlogPosting' - headline: 'Riding the Rollercoaster of Automation with Proxmox and Ansible' + headline: 'Automating Proxmox with Ansible' alternativeHeadline: 'How we automate infrastructure with Proxmox and Ansible' description: 'In the boundless expanse of the digital world, our team, OneLiteFeather, has discovered a quaint yet vibrant playground where we realize our creative and technical visions. At the heart of this expedition are Proxmox and Ansible, two tools that not only have our backs but also open doors to uncharted territories.' author: diff --git a/content/blog/en/when-a-server-fails-cluster-resilience.md b/content/blog/en/when-a-server-fails-cluster-resilience.md index 112eb590..7659dc5e 100644 --- a/content/blog/en/when-a-server-fails-cluster-resilience.md +++ b/content/blog/en/when-a-server-fails-cluster-resilience.md @@ -1,7 +1,7 @@ --- -title: 'When a Server Fails: How We Made Our Cluster Resilient' +title: 'When a server fails: cluster resilience' alternativeTitle: 'Resilience for Beginners: Our Journey to a Fail-Safe Kubernetes Cluster' -description: 'A server went down – and nobody noticed. That was exactly my goal. I tell the story in everyday images: how we made our cluster more resilient step by step, the highs and lows of the journey, and what it means in practice for OneLiteFeather.' +description: 'A server went down and nobody noticed – that was the goal. In everyday images: how OneLiteFeather made its cluster more resilient, step by step.' pubDate: '2026-06-13' headerImage: 'images/blog/cluster-topology-social.png' headerImageAlt: 'Topology of the feather-core cluster: control-plane, Ceph storage and workers in zone fr01' @@ -23,7 +23,7 @@ alternates: href: 'https://onelitefeather.net/en/blog/when-a-server-fails-cluster-resilience' schemaOrg: - type: 'BlogPosting' - headline: 'When a Server Fails: How We Made Our Cluster Resilient' + headline: 'When a server fails: cluster resilience' alternativeHeadline: 'Resilience for Beginners: Our Journey to a Fail-Safe Kubernetes Cluster' description: 'A server went down – and nobody noticed. That was exactly my goal. I tell the story in everyday images: how we made our cluster more resilient step by step.' author: diff --git a/content/community-poi/de/megabase.md b/content/community-poi/de/megabase.md index fde4a549..0fc869dd 100644 --- a/content/community-poi/de/megabase.md +++ b/content/community-poi/de/megabase.md @@ -1,7 +1,7 @@ --- slug: 'megabase' translationKey: 'megabase' -title: 'Megabase' +title: 'Megabase von blndr2' summary: 'blndr2s größtes Bauvorhaben: Ein Areal von mehreren hundert Blöcken Kantenlänge ist ausgehoben und abgesteckt, die Schematic für den Aufbau liegt bereit.' status: 'in-progress' progress: 5 diff --git a/content/community-poi/en/megabase.md b/content/community-poi/en/megabase.md index 35b6e6dd..d24651dd 100644 --- a/content/community-poi/en/megabase.md +++ b/content/community-poi/en/megabase.md @@ -1,7 +1,7 @@ --- slug: 'megabase' translationKey: 'megabase' -title: 'Megabase' +title: 'Megabase by blndr2' summary: 'The largest undertaking by blndr2: a site several hundred blocks across has been excavated and marked out, and the schematic for the build is ready.' status: 'in-progress' progress: 5 diff --git a/content/projects/de/anti-redstoneclock-remastered.md b/content/projects/de/anti-redstoneclock-remastered.md index 3cfe1af0..126914bd 100644 --- a/content/projects/de/anti-redstoneclock-remastered.md +++ b/content/projects/de/anti-redstoneclock-remastered.md @@ -1,8 +1,8 @@ --- slug: 'anti-redstoneclock-remastered' translationKey: 'arcr' -title: 'Anti-RedstoneClock Remastered' -summary: 'Ein Paper-Plugin, das Redstone-Clocks erkennt, Teammitglieder im Spiel, auf Discord oder in der Konsole benachrichtigt und sie auf Wunsch abschaltet oder zerstört.' +title: 'Anti-RedstoneClock Remastered: Paper-Plugin' +summary: 'Ein Paper-Plugin, das Redstone-Clocks erkennt, das Team im Spiel, per Discord oder Konsole benachrichtigt und sie auf Wunsch abschaltet oder zerstört.' status: 'active' releasedAt: '2024-01-30' publishedAt: '2026-10-07' diff --git a/content/projects/de/falco.md b/content/projects/de/falco.md index 19d394b8..b51b7b3b 100644 --- a/content/projects/de/falco.md +++ b/content/projects/de/falco.md @@ -1,7 +1,7 @@ --- slug: 'falco' translationKey: 'falco' -title: 'Falco' +title: 'Falco: Minestom-Bibliothek für Chunks und Licht' summary: 'Ein Anvil-Chunk-Loader, eine Lichtberechnung und eine Instanzimplementierung für Minestom.' status: 'active' releasedAt: '2026-07-31' diff --git a/content/projects/en/anti-redstoneclock-remastered.md b/content/projects/en/anti-redstoneclock-remastered.md index c35dd51d..3552bbb3 100644 --- a/content/projects/en/anti-redstoneclock-remastered.md +++ b/content/projects/en/anti-redstoneclock-remastered.md @@ -1,7 +1,7 @@ --- slug: 'anti-redstoneclock-remastered' translationKey: 'arcr' -title: 'Anti-RedstoneClock Remastered' +title: 'Anti-RedstoneClock Remastered: Paper plugin' summary: 'A Paper plugin that detects redstone clocks, alerts staff in game, on Discord or in the console, and can optionally disable or destroy them.' status: 'active' releasedAt: '2024-01-30' diff --git a/content/projects/en/falco.md b/content/projects/en/falco.md index 766aaf9d..73151d7e 100644 --- a/content/projects/en/falco.md +++ b/content/projects/en/falco.md @@ -1,7 +1,7 @@ --- slug: 'falco' translationKey: 'falco' -title: 'Falco' +title: 'Falco: Minestom library for chunks and lighting' summary: 'An Anvil chunk loader, a light engine and an instance implementation for Minestom.' status: 'active' releasedAt: '2026-07-31' diff --git a/i18n/locales/de.json b/i18n/locales/de.json index 8d1bc645..8a94afee 100644 --- a/i18n/locales/de.json +++ b/i18n/locales/de.json @@ -90,7 +90,7 @@ } }, "index": { - "title": "Startseite", + "title": "Deutscher Minecraft-Server & Open Source", "carousel_aria": "Startseiten-Highlight-Karussell" }, "home": { @@ -109,6 +109,8 @@ "title": "Team", "profile_title_fallback": "Team", "profile_description_fallback": "Teammitglied-Profil bei OneLiteFeather.", + "profile_description_in_area": "{name} arbeitet bei OneLiteFeather, einem deutschsprachigen Minecraft-Netzwerk, im Bereich {area}.", + "profile_description_plain": "{name} ist Teil des Teams bei OneLiteFeather, einem deutschsprachigen Minecraft-Netzwerk.", "no_results": "Keine Ergebnisse gefunden", "card_aria": "Profil von {name}, {role}", "avatar_alt": "Avatar von {name}", @@ -205,7 +207,7 @@ "github": "GitHub" }, "seo": { - "default_description": "OneLiteFeather ist ein Minecraft-Netzwerk, das Entwicklungstools mit der Community teilt.", + "default_description": "OneLiteFeather ist ein deutschsprachiges Minecraft-Netzwerk seit 2019 – mit Community-Bauten, Events und Open-Source-Software für Paper und Minestom.", "default_keywords": "OneLiteFeather, Minecraft, Minecraft Server, Minecraft Netzwerk, Plugins, Open Source, Paper, Velocity, Java, Bedrock, Entwicklungstools, Community" }, "faq": { @@ -239,7 +241,7 @@ "community_poi": { "overview": { "title": "Community Points of Interest", - "description": "Bauprojekte aus unserer Community: Vom Konzept über laufende Baustellen bis zur fertigen Sehenswürdigkeit – mit Fortschritt, Galerie und Litematica-Schematics zum Nachbauen.", + "description": "Community-Bauprojekte: vom Konzept über laufende Baustellen bis zur fertigen Sehenswürdigkeit – mit Fortschritt, Galerie und Litematica-Schematics.", "count": "{count} POIs", "empty": "Aktuell sind noch keine Community-POIs veröffentlicht. Schau bald wieder rein – oder reiche dein Projekt ein!" }, @@ -298,7 +300,9 @@ "open": "Auf BlueMap öffnen", "link_aria": "{title} auf der BlueMap öffnen (neuer Tab)", "iframe_title": "BlueMap-Vorschau für {title}", - "embed_note": "Vorschau geladen von {host}. Für volle Funktionen die externe Seite öffnen." + "load": "Karte laden", + "load_hint": "Die interaktive Karte lädt erst, wenn du sie startest.", + "embed_note": "Vorschau von {host}. Für volle Funktionen die externe Seite öffnen." }, "facing": { "north": "Norden", @@ -559,7 +563,7 @@ }, "projects": { "overview": { - "title": "Projekte", + "title": "Open-Source-Projekte: Paper & Minestom", "description": "Software von OneLiteFeather: Plugins und Werkzeuge, die wir selbst entwickeln und auf unserem Server einsetzen — mit Dokumentation, Quellcode und Downloads.", "empty": "Noch keine Projekte veröffentlicht." }, diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 6b9d5e66..db9dbb50 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -70,7 +70,7 @@ } }, "index": { - "title": "Home", + "title": "German Minecraft server & open source", "carousel_aria": "Homepage highlight carousel" }, "home": { @@ -109,6 +109,8 @@ "title": "Team", "profile_title_fallback": "Team", "profile_description_fallback": "Team member profile on OneLiteFeather.", + "profile_description_in_area": "{name} works in {area} at OneLiteFeather, a German-speaking Minecraft network.", + "profile_description_plain": "{name} is part of the OneLiteFeather team, a German-speaking Minecraft network.", "no_results": "No results found", "card_aria": "Profile of {name}, {role}", "avatar_alt": "Avatar of {name}", @@ -205,7 +207,7 @@ "github": "GitHub" }, "seo": { - "default_description": "OneLiteFeather is a Minecraft Network sharing development tools with the community.", + "default_description": "OneLiteFeather is a German-speaking Minecraft network founded in 2019, with community builds, events and open-source software for Paper and Minestom.", "default_keywords": "OneLiteFeather, Minecraft, Minecraft server, Minecraft network, plugins, open source, Paper, Velocity, Java, Bedrock, development tools, community" }, "faq": { @@ -239,7 +241,7 @@ "community_poi": { "overview": { "title": "Community Points of Interest", - "description": "Building projects from our community: from concept to active build site to finished landmark — with progress, image galleries and Litematica schematics to rebuild them yourself.", + "description": "Community building projects, from concept and active build site to finished landmark – with progress, galleries and Litematica schematics to rebuild.", "count": "{count} POIs", "empty": "No community POIs published yet. Check back soon — or submit your own project!" }, @@ -298,7 +300,9 @@ "open": "Open on BlueMap", "link_aria": "Open {title} on BlueMap (new tab)", "iframe_title": "BlueMap preview for {title}", - "embed_note": "Preview loaded from {host}. Open the external site for full functionality." + "load": "Load map", + "load_hint": "The interactive map only loads when you start it.", + "embed_note": "Preview from {host}. Open the external site for full functionality." }, "facing": { "north": "North", @@ -559,7 +563,7 @@ }, "projects": { "overview": { - "title": "Projects", + "title": "Open-source projects: Paper & Minestom", "description": "Software by OneLiteFeather: plugins and tools we build ourselves and run on our server — with documentation, source code and downloads.", "empty": "No projects published yet." }, diff --git a/layers/base/plugins/posthog-recording.client.ts b/layers/base/plugins/posthog-recording.client.ts new file mode 100644 index 00000000..ede8e002 --- /dev/null +++ b/layers/base/plugins/posthog-recording.client.ts @@ -0,0 +1,7 @@ +// Recording is off at init (nuxt.config.ts) so the recorder loads after first paint. +// startSessionRecording still honours opt-out and the remote sampling/flag settings. +export default defineNuxtPlugin(() => { + const { $clientPosthog } = useNuxtApp() + if (!$clientPosthog) return + deferUntilIdle(() => $clientPosthog.startSessionRecording(), window) +}) diff --git a/layers/base/utils/deferUntilIdle.ts b/layers/base/utils/deferUntilIdle.ts new file mode 100644 index 00000000..384b2b9f --- /dev/null +++ b/layers/base/utils/deferUntilIdle.ts @@ -0,0 +1,17 @@ +export const IDLE_TIMEOUT_MS = 4000 +export const IDLE_FALLBACK_MS = 2000 + +/** The two schedulers the task may use; injected so tests need no browser. */ +export interface IdleHost { + requestIdleCallback?: (callback: () => void, options: { timeout: number }) => unknown + setTimeout: (callback: () => void, delay: number) => unknown +} + +/** The timeout keeps a busy page from postponing the task indefinitely. */ +export function deferUntilIdle(task: () => void, host: IdleHost): void { + if (host.requestIdleCallback) { + host.requestIdleCallback(task, { timeout: IDLE_TIMEOUT_MS }) + return + } + host.setTimeout(task, IDLE_FALLBACK_MS) +} diff --git a/layers/blog/composables/useArticleSeo.ts b/layers/blog/composables/useArticleSeo.ts index b788c452..5fb880ad 100644 --- a/layers/blog/composables/useArticleSeo.ts +++ b/layers/blog/composables/useArticleSeo.ts @@ -53,16 +53,16 @@ export function useArticleSeo( const previewSocial = computed(() => resolveSocialImage( blog.value?.headerImage, baseUrl.value, - (src) => img(src, { width: 1200, height: 630, format: 'webp', quality: 80 }) + (src) => img(src, { width: 1200, height: 630, fit: 'cover', format: 'webp', quality: 80 }) )) // Multiple aspect ratios are recommended by Google for article rich results. const articleImages = computed(() => { const source = blog.value?.headerImage || 'images/logo.svg' return [ - img(source, { width: 1200, height: 630, format: 'webp', quality: 80 }), - img(source, { width: 1200, height: 900, format: 'webp', quality: 80 }), - img(source, { width: 1200, height: 1200, format: 'webp', quality: 80 }) + img(source, { width: 1200, height: 630, fit: 'cover', format: 'webp', quality: 80 }), + img(source, { width: 1200, height: 900, fit: 'cover', format: 'webp', quality: 80 }), + img(source, { width: 1200, height: 1200, fit: 'cover', format: 'webp', quality: 80 }) ] }) @@ -71,6 +71,7 @@ export function useArticleSeo( img(blog.value?.headerImage || 'images/logo.svg', { width: 600, height: 400, + fit: 'cover', format: 'webp', quality: 75 }) diff --git a/layers/community-poi/components/CommunityPoiBluemap.vue b/layers/community-poi/components/CommunityPoiBluemap.vue index 58a203f9..a42833b2 100644 --- a/layers/community-poi/components/CommunityPoiBluemap.vue +++ b/layers/community-poi/components/CommunityPoiBluemap.vue @@ -1,5 +1,5 @@