@elytracms/studio 0.0.80 → 0.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/contract-parity.test.ts +1 -1
- package/convex/functions.ts +13 -3
- package/convex/http-routes.test.ts +143 -0
- package/convex/http.ts +19 -9
- package/convex/redirect-revalidate.test.ts +130 -0
- package/convex/revalidate.ts +9 -7
- package/convex/schema.ts +0 -19
- package/convex/triggers.ts +33 -3
- package/convex/users.ts +1 -1
- package/dist/components/ai/ai-chat-panel.js +26 -4
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +27 -0
- package/dist/components/ai/proposal-card.js +31 -3
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.d.ts +55 -0
- package/dist/components/ai/proposal-issues.js +69 -0
- package/dist/components/ai/proposal-issues.js.map +1 -0
- package/dist/components/ai/translate-document-dialog.js +38 -3
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +141 -44
- package/dist/components/app-views/default-modules.js +229 -65
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +38 -0
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.d.ts +5 -2
- package/dist/components/app-views/sidebar.js +16 -5
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +28 -1
- package/dist/components/app-views/studio-config.js +1 -1
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/builder/canvas-render-context.js +10 -2
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/clipboard-actions.js +5 -3
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/reuse-context.js +7 -1
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +94 -16
- package/dist/components/builder/reuse.js +201 -19
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/cms/bulk-actions.js +10 -5
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +18 -3
- package/dist/components/cms/cell-edit.js +29 -7
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +54 -14
- package/dist/components/cms/cms-store.js +142 -45
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +2 -2
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +15 -14
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-editor-context.d.ts +12 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/field-control.js +22 -8
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.js +8 -1
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +36 -0
- package/dist/components/cms/gate-validation.js +17 -0
- package/dist/components/cms/gate-validation.js.map +1 -0
- package/dist/components/cms/import-export.js +2 -1
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/inline-field-errors.d.ts +8 -0
- package/dist/components/cms/inline-field-errors.js +52 -0
- package/dist/components/cms/inline-field-errors.js.map +1 -0
- package/dist/components/cms/issues-pane.d.ts +4 -2
- package/dist/components/cms/issues-pane.js +13 -7
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/slug-collision-report.d.ts +41 -0
- package/dist/components/cms/slug-collision-report.js +52 -0
- package/dist/components/cms/slug-collision-report.js.map +1 -0
- package/dist/components/components/transfer-dialog.js +10 -12
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/deployment-setup-gate.d.ts +24 -0
- package/dist/components/deployment-setup-gate.js +29 -4
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/issue-severity.d.ts +73 -0
- package/dist/components/issue-severity.js +56 -0
- package/dist/components/issue-severity.js.map +1 -0
- package/dist/components/nav-issues.d.ts +68 -0
- package/dist/components/nav-issues.js +78 -0
- package/dist/components/nav-issues.js.map +1 -0
- package/dist/components/operation-error.js +3 -3
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/project-list.d.ts +9 -3
- package/dist/components/project-list.js +11 -5
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-switcher.d.ts +3 -1
- package/dist/components/project-switcher.js +4 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/redirects/redirects-store.d.ts +21 -0
- package/dist/components/redirects/redirects-store.js +17 -4
- package/dist/components/redirects/redirects-store.js.map +1 -1
- package/dist/components/section-nav.js +75 -10
- package/dist/components/section-nav.js.map +1 -1
- package/dist/customization.d.ts +7 -0
- package/dist/customization.js.map +1 -1
- package/dist/lib/ai/apply-content.js +6 -7
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +32 -26
- package/dist/lib/ai/chat-context.js +81 -9
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +42 -0
- package/dist/lib/ai/constraints.js +259 -0
- package/dist/lib/ai/constraints.js.map +1 -0
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.js +2 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/proposal-validation.d.ts +26 -0
- package/dist/lib/ai/proposal-validation.js +17 -0
- package/dist/lib/ai/proposal-validation.js.map +1 -0
- package/dist/lib/ai/shape.js +60 -13
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +8 -4
- package/dist/lib/ai/tools.js +43 -14
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +35 -1
- package/dist/lib/ai/translate-document.js +16 -1
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +11 -6
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +8 -6
- package/dist/lib/ai/update-fields.js +9 -22
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.d.ts +5 -0
- package/dist/lib/clipboard/paste-issues.js +16 -6
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/cms/document-gate.d.ts +37 -0
- package/dist/lib/cms/document-gate.js +40 -0
- package/dist/lib/cms/document-gate.js.map +1 -0
- package/dist/lib/cms-views/view-model.d.ts +5 -0
- package/dist/lib/cms-views/view-model.js +10 -4
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +6 -1
- package/dist/lib/data/collection-content.js +51 -15
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/i18n/i18n.d.ts +51 -16
- package/dist/lib/i18n/i18n.js +119 -43
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/index.d.ts +8 -1
- package/dist/lib/i18n/index.js +1 -1
- package/dist/lib/i18n/index.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +200 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +162 -0
- package/dist/lib/i18n/messages.en.js +226 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/i18n/types.d.ts +15 -9
- package/dist/lib/i18n/types.js.map +1 -1
- package/dist/lib/locale/binding-fallback.d.ts +8 -1
- package/dist/lib/locale/binding-fallback.js +8 -1
- package/dist/lib/locale/binding-fallback.js.map +1 -1
- package/dist/lib/locale/locale-store.d.ts +41 -9
- package/dist/lib/locale/locale-store.js +34 -15
- package/dist/lib/locale/locale-store.js.map +1 -1
- package/dist/lib/locale/locale-switcher.d.ts +3 -1
- package/dist/lib/locale/locale-switcher.js +4 -2
- package/dist/lib/locale/locale-switcher.js.map +1 -1
- package/dist/lib/locale/use-studio-locale.d.ts +9 -4
- package/dist/lib/locale/use-studio-locale.js.map +1 -1
- package/dist/lib/operations/studio-operations.js +19 -2
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +1 -3
- package/dist/lib/persistence/convex-adapter.js +0 -55
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.js +10 -0
- package/dist/lib/persistence/deployment-readiness.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +85 -9
- package/dist/lib/persona/labels.js +198 -58
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/routes/route-issues.d.ts +9 -0
- package/dist/lib/routes/route-issues.js +117 -0
- package/dist/lib/routes/route-issues.js.map +1 -0
- package/dist/lib/webhooks/dispatcher.d.ts +5 -2
- package/dist/lib/webhooks/dispatcher.js.map +1 -1
- package/dist/lib/webhooks/tags.js +150 -17
- package/dist/lib/webhooks/tags.js.map +1 -1
- package/dist/lib/workspace/config-issues.d.ts +56 -0
- package/dist/lib/workspace/config-issues.js +72 -0
- package/dist/lib/workspace/config-issues.js.map +1 -0
- package/dist/lib/workspace/index.d.ts +24 -8
- package/dist/lib/workspace/index.js +28 -18
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/switcher-source.d.ts +20 -3
- package/dist/lib/workspace/switcher-source.js.map +1 -1
- package/dist/lib/workspace/workspace-source.js +27 -0
- package/dist/lib/workspace/workspace-source.js.map +1 -1
- package/dist/previews/manifest-format.d.ts +13 -6
- package/dist/previews/manifest-format.js +9 -2
- package/dist/previews/manifest-format.js.map +1 -1
- package/dist/router/root.d.ts +36 -1
- package/dist/router/root.js +54 -7
- package/dist/router/root.js.map +1 -1
- package/package.json +2 -2
- package/convex/cliTokens.ts +0 -63
- package/convex/sync.ts +0 -297
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../src/lib/i18n/i18n.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { LocalizedString } from '@elytracms/core/cms-core'\nimport { resolveDeploymentI18n } from '@elytracms/studio/lib/workspace'\nimport { EN_MESSAGES } from './messages.en'\nimport type { MessageCatalog, SourceCatalog } from './types'\n\n/**\n * Studio-UI localization runtime (EC-241). Resolves the active UI locale from the\n * **browser's preferred locale** (no persisted selector in v1 — that rides EC-240's\n * deferred account settings), then translates message keys with an\n * active-locale → English → visible-fallback chain. The studio config is static per\n * boot, so resolution is memoised once (like `getWorkspaceState`); a locale switch\n * needs a reload, which is all v1 offers.\n */\n\nconst BUILTIN_DEFAULT_LOCALE = 'en'\n\n/**\n * Pick the best available locale for the browser's ordered language preferences\n * (EC-241): an exact tag match wins, else the base language (`de-DE` → `de`), else\n * `defaultLocale`. Pure + deterministic — the browser list is the only input.\n */\nexport function resolveActiveLocale(\n available: readonly string[],\n preferred: readonly string[],\n defaultLocale: string,\n): string {\n const lower = available.map((a) => a.toLowerCase())\n for (const pref of preferred) {\n const p = pref.toLowerCase()\n const base = p.split('-')[0] ?? p\n const at = lower.findIndex((a) => a === p || a === base)\n if (at >= 0) return available[at]!\n }\n return defaultLocale\n}\n\ninterface ResolvedI18n {\n locale: string\n defaultLocale: string\n /** The active-locale catalog (empty when active === default → the English source is used). */\n active: MessageCatalog\n source: SourceCatalog\n}\n\nlet localeOverride: string | null = null\n\ntype LanguagesResolver = () => readonly string[] | null | undefined\nlet serverLanguagesResolver: LanguagesResolver | null = null\n\n/**\n * Provide the request's preferred languages for **SSR** (EC-241). The server has no\n * `navigator`, so without this the server renders the default locale while the client\n * renders the browser locale — a hydration mismatch (server text ≠ client text). A\n * host (e.g. the TanStack Start root) sets a resolver that reads the request's\n * `Accept-Language` header. It is invoked per render, so when the host reads from\n * request-scoped storage (TanStack's AsyncLocalStorage) it stays concurrency-safe —\n * the engine keeps no request state. A no-op on the client, where `navigator` wins.\n */\nexport function setServerLanguagesResolver(fn: LanguagesResolver | null): void {\n serverLanguagesResolver = fn\n}\n\n/**\n * Parse an HTTP `Accept-Language` header into an ordered list of language tags,\n * highest q-weight first: `de,en-US;q=0.7,en;q=0.3` → `['de', 'en-US', 'en']`. The\n * server equivalent of `navigator.languages`, so SSR resolves the same locale the\n * client will.\n */\nexport function parseAcceptLanguage(header: string | null | undefined): string[] {\n if (!header) return []\n return header\n .split(',')\n .map((part) => {\n const [tag, ...params] = part.trim().split(';')\n const q = params.map((p) => p.trim()).find((p) => p.startsWith('q='))\n const weight = q ? Number.parseFloat(q.slice(2)) : 1\n return { tag: (tag ?? '').trim(), weight: Number.isFinite(weight) ? weight : 1 }\n })\n .filter((entry) => entry.tag !== '' && entry.tag !== '*')\n .sort((a, b) => b.weight - a.weight)\n .map((entry) => entry.tag)\n}\n\nfunction navigatorLanguages(): string[] {\n if (typeof navigator !== 'undefined') {\n if (navigator.languages && navigator.languages.length > 0) return [...navigator.languages]\n if (navigator.language) return [navigator.language]\n }\n // SSR (no navigator): a host may resolve the request's Accept-Language.\n const server = serverLanguagesResolver?.()\n return server ? [...server] : []\n}\n\nconst warned = new Set<string>()\nfunction warnOnce(message: string): void {\n if (warned.has(message)) return\n warned.add(message)\n if (typeof console !== 'undefined') console.warn(message)\n}\n\n/**\n * Resolve the studio i18n from the config holder + the browser locale (EC-241).\n *\n * NOT cached: the studio config holder (`createStudio`) is seeded at the mount, which\n * runs AFTER the consumer's `studio.config` module built its sidebar — so an eager\n * `translate` during that sidebar build (e.g. a built-in view label) sees an empty\n * config. Caching the first result would poison every later render-time call with\n * that empty-config English. Re-resolving each call is cheap (a few array ops) and\n * always reflects the config once the mount has run. `translate`/`localizedLabel`\n * are the hot callers; built-in view labels are lazy getters so they re-resolve too.\n */\nexport function resolveStudioI18n(): ResolvedI18n {\n const config = resolveDeploymentI18n() ?? {}\n const defaultLocale = config.defaultLocale ?? BUILTIN_DEFAULT_LOCALE\n const locales = config.locales ?? {}\n const available = [defaultLocale, ...Object.keys(locales).filter((l) => l !== defaultLocale)]\n // A config-pinned locale (EC-241) takes precedence over browser detection: it is read\n // from config — identical on server and client — so SSR and hydration agree (no\n // navigator-vs-server split). An unavailable pin is ignored with a visible signal\n // (\"failures are first-class\"), and detection resumes.\n let pinned: string | undefined\n if (config.locale !== undefined) {\n if (available.includes(config.locale)) pinned = config.locale\n else\n warnOnce(\n `[i18n] pinned locale \"${config.locale}\" is not available (default \"${defaultLocale}\" + ${JSON.stringify(Object.keys(locales))}); using browser detection.`,\n )\n }\n const locale =\n localeOverride ?? pinned ?? resolveActiveLocale(available, navigatorLanguages(), defaultLocale)\n const active = locale === defaultLocale ? {} : (locales[locale] ?? {})\n // Coverage lint: warn once when the active locale doesn't cover every English key.\n if (locale !== defaultLocale && locales[locale]) {\n const missing = Object.keys(EN_MESSAGES).filter((k) => !(k in active))\n if (missing.length > 0) {\n warnOnce(\n `[i18n] locale \"${locale}\" is missing ${missing.length} message(s); falling back to English for those.`,\n )\n }\n }\n return { locale, defaultLocale, active, source: EN_MESSAGES }\n}\n\nfunction interpolate(text: string, vars: Record<string, string | number>): string {\n return text.replace(/\\{(\\w+)\\}/g, (whole, key: string) => (key in vars ? String(vars[key]) : whole))\n}\n\n/**\n * Translate a message key for the active UI locale (EC-241): active-locale string\n * ?? English source ?? **the key itself** (a visible fallback + a one-time console\n * signal — \"failures are first-class\", never a silent blank). Non-hook — reads the\n * per-boot singleton, so config-build and pure code can localize too; {@link useT}\n * is the React-idiomatic wrapper.\n */\nexport function translate(key: string, vars?: Record<string, string | number>): string {\n const i18n = resolveStudioI18n()\n const hit = i18n.active[key] ?? i18n.source[key]?.message\n if (hit === undefined) {\n warnOnce(`[i18n] missing message key: \"${key}\"`)\n return key\n }\n return vars ? interpolate(hit, vars) : hit\n}\n\n/**\n * A **catalog-backed** schema label (EC-241): a label whose text comes from the message\n * catalog by `key`, not a hard-coded string or a per-locale map. This is how the\n * engine's own built-in schemas (the redirects/users rails) localize — their default\n * labels resolve through the *same* consumer catalog as the rest of the chrome, so one\n * German catalog covers them with no per-rail override and no per-language data in\n * engine code. Consumers never author these (their labels are plain strings or\n * `{ en, de }` maps); it is an engine-internal seam.\n *\n * Encoded as a sentinel-prefixed string so it is a valid {@link LocalizedString} (no\n * change to the serializable schema type) and survives zod parse / object spread — it\n * is decoded only at the final render site by {@link resolveMsgLabel}. On the delivery\n * side (no catalog) `resolveLabel` returns it verbatim, which is harmless: the built-in\n * rails never render there.\n */\nconst MSG_LABEL_SENTINEL = '@@msg:'\nexport function msgLabel(key: string): LocalizedString {\n return MSG_LABEL_SENTINEL + key\n}\n\n/**\n * Decode a {@link msgLabel} to its translated string for the active locale, or\n * `undefined` when the value is not a catalog-backed label (a plain string / locale\n * map / absent). Every studio label read site checks this first, then falls back to\n * {@link resolveLabel} for ordinary labels.\n */\nexport function resolveMsgLabel(value: unknown): string | undefined {\n return typeof value === 'string' && value.startsWith(MSG_LABEL_SENTINEL)\n ? translate(value.slice(MSG_LABEL_SENTINEL.length))\n : undefined\n}\n\n/**\n * Resolve a {@link LocalizedString} (field/collection/option label) against the\n * active UI locale (EC-241) — **non-hook**, reading the per-boot singleton, so it is\n * safe to call anywhere: a component body, a nested render helper, or config-build\n * code. Returns `fallback` (default `''`) when the value is absent. Prefer this over\n * `resolveLabel(x, useUiLocale())` at render sites — it localizes without a hook, so\n * it can't trip the rules-of-hooks in a conditional/nested position. A catalog-backed\n * {@link msgLabel} (the built-in rails) resolves through the message catalog.\n */\nexport function localizedLabel(value: LocalizedString | undefined, fallback = ''): string {\n const fromCatalog = resolveMsgLabel(value)\n if (fromCatalog !== undefined) return fromCatalog\n const { locale, defaultLocale } = resolveStudioI18n()\n return resolveLabel(value, locale, defaultLocale) ?? fallback\n}\n\n// --- React hooks ---\n\n/** The active studio-UI locale (browser-resolved). */\nexport function useUiLocale(): string {\n return resolveStudioI18n().locale\n}\n\n/** A `t(key, vars?)` translator bound to the active locale. */\nexport function useT(): (key: string, vars?: Record<string, string | number>) => string {\n return translate\n}\n\n/**\n * Resolve a {@link LocalizedString} label (field/collection/option) for the active\n * locale. Re-reads the locale each render (keyed into the memo) so it never captures\n * a locale resolved before the mount seeded the i18n config.\n */\nexport function useLabel(): (value: LocalizedString | undefined, fallback?: string) => string {\n const { locale, defaultLocale } = resolveStudioI18n()\n return useMemo(\n () => (value, fallback) =>\n resolveMsgLabel(value) ?? resolveLabel(value, locale, defaultLocale) ?? fallback ?? '',\n [locale, defaultLocale],\n )\n}\n\n/** Test seam: force the active locale. Never used by app code. */\nexport function setUiLocaleForTests(locale: string | null): void {\n localeOverride = locale\n warned.clear()\n}\n\n/** Test seam: reset i18n state (once-per-boot warnings + the SSR resolver). Never used by app code. */\nexport function resetStudioI18nForTests(): void {\n warned.clear()\n serverLanguagesResolver = null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../src/lib/i18n/i18n.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useSyncExternalStore } from 'react'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { LocalizedString } from '@elytracms/core/cms-core'\nimport { resolveDeploymentI18n } from '@elytracms/studio/lib/workspace'\nimport { EN_MESSAGES } from './messages.en'\nimport type { MessageCatalog, SourceCatalog } from './types'\n\n/**\n * Studio-UI localization runtime (EC-241). Resolves the active UI locale, then\n * translates message keys with an active-locale → English → visible-fallback chain.\n *\n * **Locale resolution is client-side** (EC-513, owner-ratified 2026-08-01: \"the studio\n * is primarily client-side and locales should be too\"). Two mechanisms, in precedence\n * order:\n *\n * 1. **`defineStudio({ i18n: { locale } })` — the supported way to fix the UI language.**\n * A pin is read from *config*, which is imported identically on server and client, so\n * SSR and the browser render the same locale with no header plumbing and no flash.\n * Both production consumers pin `de`.\n * 2. **Browser detection** (`navigator.languages`) when nothing is pinned. It is\n * **deferred until after hydration** — see {@link subscribeToUiLocale}. The server\n * has no `navigator`, so it renders the default/pinned locale; the first client\n * render must produce byte-identical output or React discards the whole tree with a\n * hydration error (measured EC-513 under `de-DE`: the auth gate's \"Checking session…\").\n *\n * There is deliberately **no server-side resolver seam**. `setServerLanguagesResolver` +\n * `parseAcceptLanguage` shipped exported and unit-tested from EC-241 but were never\n * called by anything in production — not the demo, not the scaffold template, neither\n * consumer — and EC-241 itself had already measured that TanStack Start's request-header\n * helper is unreachable from a normal SSR route. EC-513 deleted both.\n *\n * No persisted locale selector in v1 (that rides EC-240's deferred account settings).\n */\n\nconst BUILTIN_DEFAULT_LOCALE = 'en'\n\n/**\n * Pick the best available locale for the browser's ordered language preferences\n * (EC-241): an exact tag match wins, else the base language (`de-DE` → `de`), else\n * `defaultLocale`. Pure + deterministic — the browser list is the only input.\n */\nexport function resolveActiveLocale(\n available: readonly string[],\n preferred: readonly string[],\n defaultLocale: string,\n): string {\n const lower = available.map((a) => a.toLowerCase())\n for (const pref of preferred) {\n const p = pref.toLowerCase()\n const base = p.split('-')[0] ?? p\n const at = lower.findIndex((a) => a === p || a === base)\n if (at >= 0) return available[at]!\n }\n return defaultLocale\n}\n\ninterface ResolvedI18n {\n locale: string\n defaultLocale: string\n /** The active-locale catalog (empty when active === default → the English source is used). */\n active: MessageCatalog\n source: SourceCatalog\n}\n\nlet localeOverride: string | null = null\n\n// --- post-hydration browser-locale detection (EC-513) ---\n\nlet browserDetectionEnabled = false\nconst localeListeners = new Set<() => void>()\n\n/**\n * Is `navigator` allowed to influence the resolved locale yet?\n *\n * In a **browser** it is not, until the first committed render of a locale consumer has\n * run — the hydration render has to reproduce the server's markup exactly, and the\n * server never sees `navigator`. Outside a browser (SSR, node tests) there is nothing\n * to defer: `navigatorLanguages()` there is either genuinely absent (server → `[]`, the\n * default locale, which is precisely what must be reproduced) or deliberately stubbed\n * by a test.\n */\nfunction browserDetectionReady(): boolean {\n return browserDetectionEnabled || typeof window === 'undefined'\n}\n\n/**\n * Subscribe to the resolved UI locale, and — on the first subscription — switch browser\n * detection on (EC-513).\n *\n * This is the whole post-hydration handover. `useSyncExternalStore` subscribes in a\n * commit effect, i.e. **after** React has hydrated the server markup, so every render up\n * to that point resolves the same default/pinned locale the server did. Flipping the flag\n * changes the snapshot, React re-renders, and from then on `navigator.languages` decides.\n *\n * The flip is one-way and global rather than per-subscriber because the runtime is a\n * per-boot singleton: 948 of the studio's ~956 localized render sites call the non-hook\n * {@link translate} (measured 2026-08-01), so a per-hook flag would leave almost the whole\n * chrome reading a locale nobody re-rendered for. The studio root subscribes once\n * (`router/root.tsx`), which re-renders the tree exactly once when the handover happens.\n *\n * Exported (module-level only — deliberately not on the `lib/i18n` barrel) so the\n * handover is testable without a React renderer, per the house test pattern.\n */\nexport function subscribeToUiLocale(listener: () => void): () => void {\n localeListeners.add(listener)\n if (!browserDetectionEnabled) {\n browserDetectionEnabled = true\n for (const l of [...localeListeners]) l()\n }\n return () => {\n localeListeners.delete(listener)\n }\n}\n\nfunction uiLocaleSnapshot(): string {\n return resolveStudioI18n().locale\n}\n\n// --- post-hydration rendering gate (EC-523) ---\n\nconst noopUnsubscribe = () => {}\nconst subscribeNever = () => noopUnsubscribe\nconst hydratedSnapshot = () => true\nconst preHydrationSnapshot = () => false\n\n/**\n * Has React finished hydrating? `false` for every server render **and** for the\n * hydration render itself; `true` from the first committed client render onwards.\n *\n * **Gate any localized text that can reach the prerendered shell on this** (EC-523).\n * The studio builds as a static SPA, so `dist/client/_shell.html` is rendered at BUILD\n * time: no request, no `Accept-Language`, no session. Every localized string baked into\n * that file is therefore guaranteed wrong for some visitor — and that includes\n * `aria-label` / `sr-only` text, because an accessible name is still a string and a\n * screen-reader user cannot even notice it flip. The honest shape is to render no words\n * at all until hydration, then announce in the visitor's real locale.\n *\n * Mechanism, and why it cannot itself cause a mismatch: `useSyncExternalStore` reads\n * `getServerSnapshot` for the prerender *and* for the hydration render, so the two agree\n * (a text mismatch makes React discard the whole tree — EC-513), then switches to\n * `getSnapshot` in the commit that follows. The store never changes again, so `subscribe`\n * is an inert module-level constant; a fresh closure per render would resubscribe on\n * every render.\n *\n * Lives beside the EC-513 locale handover deliberately: both answer the same question —\n * *is it safe to render locale-dependent output yet?* — and a contributor chasing \"why is\n * my string baked into the shell?\" lands here.\n */\nexport function useHydrated(): boolean {\n return useSyncExternalStore(subscribeNever, hydratedSnapshot, preHydrationSnapshot)\n}\n\nfunction navigatorLanguages(): string[] {\n if (!browserDetectionReady()) return []\n if (typeof navigator !== 'undefined') {\n if (navigator.languages && navigator.languages.length > 0) return [...navigator.languages]\n if (navigator.language) return [navigator.language]\n }\n return []\n}\n\nconst warned = new Set<string>()\nfunction warnOnce(message: string): void {\n if (warned.has(message)) return\n warned.add(message)\n if (typeof console !== 'undefined') console.warn(message)\n}\n\n/**\n * Resolve the studio i18n from the config holder + the browser locale (EC-241).\n *\n * NOT cached: the studio config holder (`createStudio`) is seeded at the mount, which\n * runs AFTER the consumer's `studio.config` module built its sidebar — so an eager\n * `translate` during that sidebar build (e.g. a built-in view label) sees an empty\n * config. Caching the first result would poison every later render-time call with\n * that empty-config English. Re-resolving each call is cheap (a few array ops) and\n * always reflects the config once the mount has run. `translate`/`localizedLabel`\n * are the hot callers; built-in view labels are lazy getters so they re-resolve too.\n */\nexport function resolveStudioI18n(): ResolvedI18n {\n const config = resolveDeploymentI18n() ?? {}\n const defaultLocale = config.defaultLocale ?? BUILTIN_DEFAULT_LOCALE\n const locales = config.locales ?? {}\n const available = [defaultLocale, ...Object.keys(locales).filter((l) => l !== defaultLocale)]\n // A config-pinned locale (EC-241) takes precedence over browser detection: it is read\n // from config — identical on server and client — so SSR and hydration agree (no\n // navigator-vs-server split). An unavailable pin is ignored with a visible signal\n // (\"failures are first-class\"), and detection resumes.\n let pinned: string | undefined\n if (config.locale !== undefined) {\n if (available.includes(config.locale)) pinned = config.locale\n else\n warnOnce(\n `[i18n] pinned locale \"${config.locale}\" is not available (default \"${defaultLocale}\" + ${JSON.stringify(Object.keys(locales))}); using browser detection.`,\n )\n }\n const locale =\n localeOverride ?? pinned ?? resolveActiveLocale(available, navigatorLanguages(), defaultLocale)\n const active = locale === defaultLocale ? {} : (locales[locale] ?? {})\n // Coverage lint: warn once when the active locale doesn't cover every English key.\n if (locale !== defaultLocale && locales[locale]) {\n const missing = Object.keys(EN_MESSAGES).filter((k) => !(k in active))\n if (missing.length > 0) {\n warnOnce(\n `[i18n] locale \"${locale}\" is missing ${missing.length} message(s); falling back to English for those.`,\n )\n }\n }\n return { locale, defaultLocale, active, source: EN_MESSAGES }\n}\n\nfunction interpolate(text: string, vars: Record<string, string | number>): string {\n return text.replace(/\\{(\\w+)\\}/g, (whole, key: string) => (key in vars ? String(vars[key]) : whole))\n}\n\n/**\n * Translate a message key for the active UI locale (EC-241): active-locale string\n * ?? English source ?? **the key itself** (a visible fallback + a one-time console\n * signal — \"failures are first-class\", never a silent blank). Non-hook — reads the\n * per-boot singleton, so config-build and pure code can localize too; {@link useT}\n * is the React-idiomatic wrapper.\n */\nexport function translate(key: string, vars?: Record<string, string | number>): string {\n const i18n = resolveStudioI18n()\n const hit = i18n.active[key] ?? i18n.source[key]?.message\n if (hit === undefined) {\n warnOnce(`[i18n] missing message key: \"${key}\"`)\n return key\n }\n return vars ? interpolate(hit, vars) : hit\n}\n\n/**\n * A **catalog-backed** schema label (EC-241): a label whose text comes from the message\n * catalog by `key`, not a hard-coded string or a per-locale map. This is how the\n * engine's own built-in schemas (the redirects/users rails) localize — their default\n * labels resolve through the *same* consumer catalog as the rest of the chrome, so one\n * German catalog covers them with no per-rail override and no per-language data in\n * engine code. Consumers never author these (their labels are plain strings or\n * `{ en, de }` maps); it is an engine-internal seam.\n *\n * Encoded as a sentinel-prefixed string so it is a valid {@link LocalizedString} (no\n * change to the serializable schema type) and survives zod parse / object spread — it\n * is decoded only at the final render site by {@link resolveMsgLabel}. On the delivery\n * side (no catalog) `resolveLabel` returns it verbatim, which is harmless: the built-in\n * rails never render there.\n */\nconst MSG_LABEL_SENTINEL = '@@msg:'\nexport function msgLabel(key: string): LocalizedString {\n return MSG_LABEL_SENTINEL + key\n}\n\n/**\n * Decode a {@link msgLabel} to its translated string for the active locale, or\n * `undefined` when the value is not a catalog-backed label (a plain string / locale\n * map / absent). Every studio label read site checks this first, then falls back to\n * {@link resolveLabel} for ordinary labels.\n */\nexport function resolveMsgLabel(value: unknown): string | undefined {\n return typeof value === 'string' && value.startsWith(MSG_LABEL_SENTINEL)\n ? translate(value.slice(MSG_LABEL_SENTINEL.length))\n : undefined\n}\n\n/**\n * Resolve a {@link LocalizedString} (field/collection/option label) against the\n * active UI locale (EC-241) — **non-hook**, reading the per-boot singleton, so it is\n * safe to call anywhere: a component body, a nested render helper, or config-build\n * code. Returns `fallback` (default `''`) when the value is absent. Prefer this over\n * `resolveLabel(x, useUiLocale())` at render sites — it localizes without a hook, so\n * it can't trip the rules-of-hooks in a conditional/nested position. A catalog-backed\n * {@link msgLabel} (the built-in rails) resolves through the message catalog.\n */\nexport function localizedLabel(value: LocalizedString | undefined, fallback = ''): string {\n const fromCatalog = resolveMsgLabel(value)\n if (fromCatalog !== undefined) return fromCatalog\n const { locale, defaultLocale } = resolveStudioI18n()\n return resolveLabel(value, locale, defaultLocale) ?? fallback\n}\n\n// --- React hooks ---\n\n/**\n * The active studio-UI locale — config-pinned, else browser-resolved.\n *\n * Subscribing (EC-513) is what makes the post-hydration handover visible: the hydration\n * render gets the server's locale, and the component re-renders once when browser\n * detection takes over. `router/root.tsx` calls this at the root so the whole chrome —\n * including the ~948 non-hook {@link translate} sites — re-renders with it.\n */\nexport function useUiLocale(): string {\n return useSyncExternalStore(subscribeToUiLocale, uiLocaleSnapshot, uiLocaleSnapshot)\n}\n\n/** A `t(key, vars?)` translator bound to the active locale. */\nexport function useT(): (key: string, vars?: Record<string, string | number>) => string {\n useUiLocale() // re-render this component when the locale handover happens (EC-513)\n return translate\n}\n\n/**\n * Resolve a {@link LocalizedString} label (field/collection/option) for the active\n * locale. Re-reads the locale each render (keyed into the memo) so it never captures\n * a locale resolved before the mount seeded the i18n config.\n */\nexport function useLabel(): (value: LocalizedString | undefined, fallback?: string) => string {\n const locale = useUiLocale() // subscribes to the EC-513 post-hydration handover\n const { defaultLocale } = resolveStudioI18n()\n return useMemo(\n () => (value, fallback) =>\n resolveMsgLabel(value) ?? resolveLabel(value, locale, defaultLocale) ?? fallback ?? '',\n [locale, defaultLocale],\n )\n}\n\n/** Test seam: force the active locale. Never used by app code. */\nexport function setUiLocaleForTests(locale: string | null): void {\n localeOverride = locale\n warned.clear()\n}\n\n/**\n * Test seam: reset i18n state — once-per-boot warnings + the EC-513 browser-detection\n * handover flag and its subscribers. Never used by app code.\n */\nexport function resetStudioI18nForTests(): void {\n warned.clear()\n browserDetectionEnabled = false\n localeListeners.clear()\n}\n"]}
|
package/dist/lib/i18n/index.d.ts
CHANGED
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
* from `@elytracms/studio/customization` so a consumer authors catalogs there.
|
|
6
6
|
* - Studio components localize chrome via `translate`/`useT` and schema labels via
|
|
7
7
|
* `useLabel`.
|
|
8
|
+
* - **`defineStudio({ i18n: { locale } })` is the supported way to fix the UI language**
|
|
9
|
+
* (EC-513); unpinned deployments follow the browser, resolved client-side after
|
|
10
|
+
* hydration. There is no server-side resolver seam — `setServerLanguagesResolver` and
|
|
11
|
+
* `parseAcceptLanguage` were deleted in EC-513, having never had a production caller.
|
|
12
|
+
* - **Anything that can render into the build-time SPA shell must gate its text on
|
|
13
|
+
* `useHydrated`** (EC-523). The shell is prerendered with no request, so a localized
|
|
14
|
+
* string baked into it — visible copy *or* an accessible name — is wrong for someone.
|
|
8
15
|
*/
|
|
9
16
|
export type { SourceMessage, SourceCatalog, MessageCatalog, StudioI18nConfig } from './types.js';
|
|
10
17
|
export { msg } from './types.js';
|
|
11
18
|
export { EN_MESSAGES } from './messages.en.js';
|
|
12
19
|
export type { MessageKey, TranslationCatalog } from './messages.en.js';
|
|
13
|
-
export { resolveActiveLocale, resolveStudioI18n, translate, localizedLabel, msgLabel, resolveMsgLabel, useT, useUiLocale, useLabel,
|
|
20
|
+
export { resolveActiveLocale, resolveStudioI18n, translate, localizedLabel, msgLabel, resolveMsgLabel, useT, useUiLocale, useLabel, useHydrated, setUiLocaleForTests, resetStudioI18nForTests, } from './i18n.js';
|
package/dist/lib/i18n/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { msg } from './types.js';
|
|
2
2
|
export { EN_MESSAGES } from './messages.en.js';
|
|
3
|
-
export { resolveActiveLocale, resolveStudioI18n, translate, localizedLabel, msgLabel, resolveMsgLabel, useT, useUiLocale, useLabel,
|
|
3
|
+
export { resolveActiveLocale, resolveStudioI18n, translate, localizedLabel, msgLabel, resolveMsgLabel, useT, useUiLocale, useLabel, useHydrated, setUiLocaleForTests, resetStudioI18nForTests, } from './i18n.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/i18n/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/i18n/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,QAAQ,EACR,eAAe,EACf,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,QAAQ,CAAA","sourcesContent":["/**\n * `@elytracms/studio/lib/i18n` — studio-UI localization (EC-241).\n *\n * - Public config surface (`StudioI18nConfig`, `MessageCatalog`, `msg`) is re-exported\n * from `@elytracms/studio/customization` so a consumer authors catalogs there.\n * - Studio components localize chrome via `translate`/`useT` and schema labels via\n * `useLabel`.\n * - **`defineStudio({ i18n: { locale } })` is the supported way to fix the UI language**\n * (EC-513); unpinned deployments follow the browser, resolved client-side after\n * hydration. There is no server-side resolver seam — `setServerLanguagesResolver` and\n * `parseAcceptLanguage` were deleted in EC-513, having never had a production caller.\n * - **Anything that can render into the build-time SPA shell must gate its text on\n * `useHydrated`** (EC-523). The shell is prerendered with no request, so a localized\n * string baked into it — visible copy *or* an accessible name — is wrong for someone.\n */\nexport type { SourceMessage, SourceCatalog, MessageCatalog, StudioI18nConfig } from './types'\nexport { msg } from './types'\nexport { EN_MESSAGES } from './messages.en'\nexport type { MessageKey, TranslationCatalog } from './messages.en'\nexport {\n resolveActiveLocale,\n resolveStudioI18n,\n translate,\n localizedLabel,\n msgLabel,\n resolveMsgLabel,\n useT,\n useUiLocale,\n useLabel,\n useHydrated,\n setUiLocaleForTests,\n resetStudioI18nForTests,\n} from './i18n'\n"]}
|
|
@@ -146,6 +146,32 @@ export const deMessages = {
|
|
|
146
146
|
'shell.playground.hint': 'Playground-Modus: Daten liegen im Arbeitsspeicher dieses Geräts und werden beim Neuladen zurückgesetzt. Es wird nichts in einem Backend gespeichert.',
|
|
147
147
|
'shell.deliveryHealth.dismiss': 'Schließen',
|
|
148
148
|
// ─────────────────────────────────────────────────────────────────────────
|
|
149
|
+
// Customization issues — the section-rail foot (`NavIssues`)
|
|
150
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
151
|
+
// EC-478. Jede `{var}` ist ein BEZEICHNER (Eintrags-id, Erweiterungspunkt,
|
|
152
|
+
// Komponenten-/Sammlungs-id) und bleibt bewusst unübersetzt. Fachbegriffe, die
|
|
153
|
+
// im Code stehen (`reuse.component`, `reuse.relationProp`, `studio.config`,
|
|
154
|
+
// Prop, Slot, Block), bleiben ebenfalls unübersetzt — der Entwickler muss sie
|
|
155
|
+
// in seiner Konfiguration wiederfinden.
|
|
156
|
+
// EC-479 — das Projekt deklariert nichts, womit das Studio arbeiten könnte.
|
|
157
|
+
'customization.issue.noCollections': 'Dieses Projekt deklariert keine Inhaltssammlungen. Fügen Sie sie in „{file}“ hinzu — das Studio zeigt Ihr Schema oder keines, niemals Beispielinhalte.',
|
|
158
|
+
'customization.issue.duplicateOverrideId': 'Doppelte Überschreibungs-id „{id}“ für „{point}“ — die spätere wird ignoriert.',
|
|
159
|
+
'customization.issue.unknownOverrideTarget': 'Die Überschreibung „{id}“ für „{point}“ passt zu keinem eingebauten Baustein, den sie ersetzen könnte.',
|
|
160
|
+
'customization.issue.invalidOverride': 'Die Überschreibung „{id}“ für „{point}“ ist ungültig: {reason}',
|
|
161
|
+
'customization.issue.reason.rendererNotAComponent': 'der Renderer ist keine Komponente',
|
|
162
|
+
'customization.issue.reason.viewNoRender': 'die Ansicht hat keine render-Funktion',
|
|
163
|
+
'customization.issue.reason.viewNoLabel': 'die Ansicht hat keine Beschriftung',
|
|
164
|
+
'customization.issue.derivedIdBuiltinCollision': 'Die Ansicht „{label}“ für „{point}“ leitet die id „{id}“ aus ihrer Beschriftung ab, was der eingebauten Ansicht „{id}“ entspricht — die eingebaute Ansicht bleibt erhalten und diese wird ignoriert. Geben Sie eine explizite id an, um eine eingebaute Ansicht absichtlich zu ersetzen.',
|
|
165
|
+
'customization.issue.derivedIdCollectionCollision': 'Die Ansichts-id „{id}“ für „{point}“ ist mehrdeutig: Sie verbindet die Sammlungen {collections} mit „-“, und eine Sammlung heißt buchstäblich „{joined}“ — deren eigene Ansicht leitet dieselbe id ab. Geben Sie dieser Gruppe eine explizite id.',
|
|
166
|
+
// EC-467 — die Deklaration wiederverwendbarer Elemente (`defineStudio({ reuse })`).
|
|
167
|
+
'builder.reuse.config.unknownComponent': 'Wiederverwendbare Elemente: Die Studio-Konfiguration deklariert reuse.component „{component}“, was keine registrierte Komponente ist. Korrigieren Sie die id oder entfernen Sie den reuse-Block, damit das Studio die Einbettung selbst ermittelt.',
|
|
168
|
+
'builder.reuse.config.noRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat kein Relations-Prop für ein einzelnes Dokument und kann daher auf kein wiederverwendbares Dokument zeigen. Eine Einbettung benötigt genau ein Relations-Prop mit Kardinalität eins.',
|
|
169
|
+
'builder.reuse.config.unknownRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat kein Relations-Prop für ein einzelnes Dokument mit dem Namen „{relationProp}“. Ihre Relations-Props sind: {props}.',
|
|
170
|
+
'builder.reuse.config.ambiguousRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat {count} Relations-Props für ein einzelnes Dokument ({props}) — ergänzen Sie reuse.relationProp, um anzugeben, welches auf das wiederverwendbare Dokument zeigt.',
|
|
171
|
+
'builder.reuse.config.unknownCollection': 'Wiederverwendbare Elemente: „{component}.{relationProp}“ verweist auf die Sammlung „{collection}“, die dieses Projekt nicht definiert.',
|
|
172
|
+
'builder.reuse.config.noBlocksField': 'Wiederverwendbare Elemente: Die Sammlung „{collection}“ hinter „{component}.{relationProp}“ hat kein Block-Feld (Komposition), sodass die Einbettung nichts einbinden kann.',
|
|
173
|
+
'builder.reuse.config.ambiguousInference': 'Wiederverwendbare Elemente: {count} Komponenten haben die Form einer wiederverwendbaren Einbettung ({components}). Ein Projekt hat genau eine wiederverwendbare Einbettung, deshalb rät das Studio nicht — deklarieren Sie sie in studio.config als reuse: { component: \'<id>\' }. Bis dahin sind die Wiederverwendungs-Gesten deaktiviert.',
|
|
174
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
149
175
|
// Auth / errors
|
|
150
176
|
// ─────────────────────────────────────────────────────────────────────────
|
|
151
177
|
'auth.signIn': 'Anmelden',
|
|
@@ -191,6 +217,44 @@ export const deMessages = {
|
|
|
191
217
|
// the session-expired predicate; re-auth happens in place, no navigation.
|
|
192
218
|
'error.sessionExpiredTitle': 'Sie wurden abgemeldet',
|
|
193
219
|
'error.sessionExpired': 'Sie wurden abgemeldet, daher werden Ihre letzten Änderungen nicht gespeichert. Melden Sie sich erneut an, um weiterzuarbeiten — Ihre ungespeicherten Änderungen bleiben genau hier erhalten.',
|
|
220
|
+
// EC-494 — Betriebs-Fehler, die die Operations-Grenze überqueren. Bezeichner
|
|
221
|
+
// (Operationsnamen, Entitätsarten, Dokumentschlüssel, Feldnamen) bleiben unübersetzt.
|
|
222
|
+
// EC-494 — die Titel der Fehler-Badges im Banner.
|
|
223
|
+
'operations.errorLabel.validation': 'Konnte nicht gespeichert werden',
|
|
224
|
+
'operations.errorLabel.not-found': 'Nicht gefunden',
|
|
225
|
+
'operations.errorLabel.conflict': 'Existiert bereits',
|
|
226
|
+
'operations.errorLabel.backend-unavailable': 'Verbindungsproblem',
|
|
227
|
+
'operations.errorLabel.unauthorized': 'Nicht erlaubt',
|
|
228
|
+
'operations.errorLabel.unknown': 'Etwas ist schiefgelaufen',
|
|
229
|
+
'operations.error.invalidInput': 'Die Eingabe für „{operation}“ ist ungültig.',
|
|
230
|
+
'operations.error.notFound': 'Kein {entity} für „{target}“ gefunden.',
|
|
231
|
+
'operations.error.conflict': '„{target}“ steht im Konflikt mit einem vorhandenen Datensatz.',
|
|
232
|
+
'operations.error.backendUnavailable': 'Das Persistenz-Backend ist nicht erreichbar.',
|
|
233
|
+
'operations.error.unauthorized': 'Die Operation „{operation}“ wurde vom Autorisierungs-Hook abgelehnt.',
|
|
234
|
+
'operations.error.unknown': 'Es ist ein unbekannter Fehler bei der Operation aufgetreten.',
|
|
235
|
+
'operations.error.documentSchemaViolation': 'Das Dokument „{collection}:{id}“ verletzt das Schema seiner Sammlung.',
|
|
236
|
+
'operations.error.strictReferencesBlockDelete': '„{target}“ kann nicht gelöscht werden: {count} Dokumente verweisen über strikte Beziehungsfelder darauf — {named}{more}. Entfernen Sie zuerst diese Verweise.',
|
|
237
|
+
'operations.error.strictReferencesBlockDeleteOne': '„{target}“ kann nicht gelöscht werden: {count} Dokument verweist über strikte Beziehungsfelder darauf — {named}. Entfernen Sie zuerst diese Verweise.',
|
|
238
|
+
'operations.error.strictReferencesBlockUnpublish': 'Die Veröffentlichung von „{target}“ kann nicht zurückgenommen werden: {count} Dokumente verweisen über strikte Beziehungsfelder darauf — {named}{more}. Entfernen Sie zuerst diese Verweise.',
|
|
239
|
+
'operations.error.strictReferencesBlockUnpublishOne': 'Die Veröffentlichung von „{target}“ kann nicht zurückgenommen werden: {count} Dokument verweist über strikte Beziehungsfelder darauf — {named}. Entfernen Sie zuerst diese Verweise.',
|
|
240
|
+
'operations.errorDetail.andMore': ' und {count} weitere',
|
|
241
|
+
// EC-494 — Fehler von Inhaltsabfragen (Filterleiste + Listing-Hinweis im Canvas).
|
|
242
|
+
// „filterable“, „asc“, „desc“, „in“, „empty“ sind Schlüsselwörter aus der Konfiguration
|
|
243
|
+
// und bleiben wörtlich stehen.
|
|
244
|
+
'content.queryError.nonFilterableField': 'Das Feld "{field}" in "{collection}" ist nicht als filterable deklariert.',
|
|
245
|
+
'content.queryError.unknownField': 'Das Feld "{field}" existiert in "{collection}" nicht.',
|
|
246
|
+
'content.queryError.unknownCollection': 'Die Sammlung "{collection}" ist nicht definiert.',
|
|
247
|
+
'content.queryError.filterShape': 'Der Filter für "{field}" muss ein Skalar oder ein Operator-Objekt sein.',
|
|
248
|
+
'content.queryError.filterNoOperators': 'Der Filter für "{field}" enthält keine Operatoren.',
|
|
249
|
+
'content.queryError.unknownOperator': 'Unbekannter Filteroperator "{operator}" auf "{field}" — das Vokabular ist genau {vocabulary}.',
|
|
250
|
+
'content.queryError.operatorInRequiresArray': 'Der Operator "in" auf "{field}" erwartet ein Array aus Skalaren.',
|
|
251
|
+
'content.queryError.operatorRequiresString': 'Der Operator "{operator}" auf "{field}" erwartet einen Zeichenkettenwert.',
|
|
252
|
+
'content.queryError.operatorEmptyRequiresBoolean': 'Der Operator "empty" auf "{field}" erwartet einen Wahrheitswert (true = ist leer).',
|
|
253
|
+
'content.queryError.operatorRequiresScalar': 'Der Operator "{operator}" auf "{field}" erwartet einen skalaren Wert.',
|
|
254
|
+
'content.queryError.sortNotFilterable': 'Das Sortierfeld "{field}" in "{collection}" ist nicht als filterable deklariert.',
|
|
255
|
+
'content.queryError.sortUnknownField': 'Das Sortierfeld "{field}" existiert in "{collection}" nicht.',
|
|
256
|
+
'content.queryError.sortDirection': 'Die Sortierrichtung muss "asc" oder "desc" sein.',
|
|
257
|
+
'content.queryError.limitPositiveInteger': 'Das Limit muss eine positive ganze Zahl sein.',
|
|
194
258
|
// ─────────────────────────────────────────────────────────────────────────
|
|
195
259
|
// CMS — collection list / view bar / filters / cells
|
|
196
260
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -232,6 +296,9 @@ export const deMessages = {
|
|
|
232
296
|
// EC-448 — dritter Dok-Status (veröffentlicht mit unveröffentlichten Änderungen).
|
|
233
297
|
'cms.docState.changed': 'geändert',
|
|
234
298
|
'cms.docState.draft': 'Entwurf',
|
|
299
|
+
// EC-479 — ohne Projektsprachen kann keine CMS-Sitzung geöffnet werden.
|
|
300
|
+
'cms.session.noLocales.title': 'Dieses Projekt deklariert keine Sprachen',
|
|
301
|
+
'cms.session.noLocales.body': 'Fügen Sie Ihrer Projektkonfiguration eine locales-Liste hinzu. Die Standardsprache ist der Schlüssel, unter dem Ihre Inhalte gespeichert werden — das Studio wählt sie deshalb nicht für Sie aus.',
|
|
235
302
|
'cms.list.view.all': 'Alle Einträge',
|
|
236
303
|
'cms.list.unknownCollection': 'Unbekannte Sammlung {id}.',
|
|
237
304
|
'cms.list.import.noId': '(keine ID)',
|
|
@@ -251,6 +318,9 @@ export const deMessages = {
|
|
|
251
318
|
'cms.list.bulk.noSettableFields': 'Keine filterbaren Skalarfelder zum Setzen.',
|
|
252
319
|
'cms.list.bulk.setField': 'Feld setzen',
|
|
253
320
|
'cms.list.bulk.clearSelection': 'Auswahl aufheben',
|
|
321
|
+
// EC-494 — die beiden studio-seitigen Fehlergründe je Eintrag.
|
|
322
|
+
'cms.list.bulk.error.notInSession': 'Das Dokument ist nicht mehr in der Sitzung.',
|
|
323
|
+
'cms.list.bulk.error.notValidToPublish': 'Das Dokument ist nicht gültig zum Veröffentlichen.',
|
|
254
324
|
'cms.list.bulk.set.set': 'Setze',
|
|
255
325
|
'cms.list.bulk.set.to': 'auf',
|
|
256
326
|
'cms.list.bulk.set.valueAria': 'Wert setzen',
|
|
@@ -274,6 +344,9 @@ export const deMessages = {
|
|
|
274
344
|
'cms.filter.fieldAria': 'Filterfeld',
|
|
275
345
|
'cms.filter.operatorAria': 'Filteroperator',
|
|
276
346
|
'cms.filter.remove': 'Filter entfernen',
|
|
347
|
+
// EC-494 — die beiden studio-seitigen Klausel-Probleme.
|
|
348
|
+
'cms.filter.issue.stateOperator': 'Status-Filter unterstützen nur "eq" und "in".',
|
|
349
|
+
'cms.filter.issue.stateValue': 'Der Status muss "draft" oder "published" sein.',
|
|
277
350
|
'cms.filter.valueAria': 'Filterwert',
|
|
278
351
|
'cms.filter.valueListPlaceholder': 'Wert, Wert, …',
|
|
279
352
|
'cms.filter.pickRelation': 'Auswählen…',
|
|
@@ -395,6 +468,7 @@ export const deMessages = {
|
|
|
395
468
|
'cms.issue.reserved-field-name': 'Reservierter Feldname',
|
|
396
469
|
'cms.issue.missing-required-field': 'Pflichtfeld fehlt',
|
|
397
470
|
'cms.issue.invalid-field-value': 'Ungültiger Wert',
|
|
471
|
+
'cms.issue.invalid-field-value.warning': 'Wert prüfen',
|
|
398
472
|
'cms.issue.unknown-relation-target': 'Fehlende Referenz',
|
|
399
473
|
'cms.issue.unpublished-relation-target': 'Unveröffentlichte Referenz',
|
|
400
474
|
'cms.issue.cardinality-violation': 'Falsche Anzahl von Werten',
|
|
@@ -412,6 +486,7 @@ export const deMessages = {
|
|
|
412
486
|
'cms.issue.unknown-alias': 'Unbekannter Slot',
|
|
413
487
|
'cms.issue.unfilled-slot': 'Leerer Slot',
|
|
414
488
|
'cms.issue.custom-validation': 'Validierungsregel',
|
|
489
|
+
'cms.issue.unaddressable-url': 'Nicht erreichbare URL',
|
|
415
490
|
'cms.issue.unknown-version': 'Unbekannte Version',
|
|
416
491
|
// EC-443 — issue-card body templates (see messages.en.ts for the fallback contract).
|
|
417
492
|
'cms.issueBody.missing-required-field': 'Das Pflichtfeld „{field}“ ist nicht ausgefüllt.',
|
|
@@ -422,6 +497,113 @@ export const deMessages = {
|
|
|
422
497
|
'cms.issueBody.document-nesting-depth.warning': 'Dieser Inhalt ist {depth} Ebenen tief verschachtelt und nähert sich dem Speicherlimit von {limit} Ebenen.',
|
|
423
498
|
'cms.issueBody.hierarchy-cycle': 'Dieser Eintrag ist sein eigener Vorfahr – die Kette der übergeordneten Einträge bildet eine Schleife.',
|
|
424
499
|
// ─────────────────────────────────────────────────────────────────────────
|
|
500
|
+
// CMS validation issue BODIES (EC-487)
|
|
501
|
+
//
|
|
502
|
+
// EC-443 localized the issue TITLES (`cms.issue.<code>` above); the bodies were
|
|
503
|
+
// still built as English template literals inside `packages/core`, which cannot
|
|
504
|
+
// import this catalog. EC-478 solved that shape for `CustomizationIssue` by having
|
|
505
|
+
// the producer carry a message KEY; these are the same carriage for
|
|
506
|
+
// `CmsValidationIssue`, so the rail foot, the Issues pane and the for-review lane
|
|
507
|
+
// never mix a German title with an English body.
|
|
508
|
+
//
|
|
509
|
+
// The English source is a SECOND copy of what the producer builds — the tie between
|
|
510
|
+
// them is `lib/persona/cms-issue-i18n.test.ts`, which renders every real producer's
|
|
511
|
+
// issue through this catalog and asserts byte-identity. Interpolated values are
|
|
512
|
+
// IDENTIFIERS (field/collection/route/locale ids, patterns, counts) and stay
|
|
513
|
+
// unlocalized in every language.
|
|
514
|
+
//
|
|
515
|
+
// `cms.issueDetail.*` are FRAGMENTS spliced into the sentences above.
|
|
516
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
517
|
+
'cms.issueMessage.duplicateDocument': 'Doppeltes Dokument „{id}“ in der Sammlung „{collection}“.',
|
|
518
|
+
'cms.issueMessage.fieldDetail': 'Das Feld „{field}“ {detail}.',
|
|
519
|
+
'cms.issueMessage.fieldReason': 'Feld „{field}“: {reason}',
|
|
520
|
+
'cms.issueMessage.propDetail': 'Die Eigenschaft „{field}“ {detail}.',
|
|
521
|
+
'cms.issueMessage.propReason': 'Eigenschaft „{field}“: {reason}',
|
|
522
|
+
'cms.issueMessage.relationUnknownTargetCollection': 'Das Beziehungsfeld „{field}“ zielt auf die unbekannte Sammlung „{target}“.',
|
|
523
|
+
'cms.issueMessage.relationSingleValued': 'Das Beziehungsfeld „{field}“ ist einwertig, hat aber {count} Ziele.',
|
|
524
|
+
'cms.issueMessage.relationNotAReference': 'Das Beziehungsfeld „{field}“ enthält einen Wert, der keine Dokumentreferenz ist.',
|
|
525
|
+
'cms.issueMessage.relationWrongCollection': 'Das Beziehungsfeld „{field}“ zeigt auf die Sammlung „{got}“, zielt aber auf „{target}“.',
|
|
526
|
+
'cms.issueMessage.relationMissingDocument': 'Das Beziehungsfeld „{field}“ verweist auf das fehlende Dokument „{id}“ in „{target}“.',
|
|
527
|
+
'cms.issueMessage.relationUnpublishedDocument': 'Das Beziehungsfeld „{field}“ verweist auf das unveröffentlichte Dokument „{id}“ in „{target}“ — auf der veröffentlichten Website erscheint dort ein Platzhalter.',
|
|
528
|
+
'cms.issueMessage.assetNoAssetCollection': 'Das Medienfeld „{field}“ benötigt eine Mediensammlung, es ist aber keine registriert.',
|
|
529
|
+
'cms.issueMessage.assetSingleValued': 'Das Medienfeld „{field}“ ist einwertig, hat aber {count} Ziele.',
|
|
530
|
+
'cms.issueMessage.assetExpectsId': 'Das Medienfeld „{field}“ erwartet eine Medien-id.',
|
|
531
|
+
'cms.issueMessage.assetUnknown': 'Das Medienfeld „{field}“ verweist auf das unbekannte Medium „{asset}“.',
|
|
532
|
+
'cms.issueMessage.requiredFieldMissing': 'Das Pflichtfeld „{field}“ fehlt.',
|
|
533
|
+
'cms.issueMessage.requiredPropMissing': 'Die Pflichteigenschaft „{field}“ fehlt.',
|
|
534
|
+
'cms.issueMessage.perLocaleMapValue': 'Das Feld „{field}“ erwartet einen Wert vom Typ „{type}“, enthält aber eine Sprach-Zuordnung (Schlüssel: {keys}) — sprachabhängige Werte liegen im „localized“-Bereich des Dokuments, niemals im Feldwert selbst.',
|
|
535
|
+
'cms.issueMessage.richTextHoldsArray': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein Array.',
|
|
536
|
+
'cms.issueMessage.richTextHoldsBareDoc': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein blankes Rich-Text-Dokument ohne die Hülle „{ version, doc }“.',
|
|
537
|
+
'cms.issueMessage.richTextHoldsObject': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein Objekt mit den Schlüsseln „{keys}“.',
|
|
538
|
+
'cms.issueMessage.unknownCollectionForDocument': 'Das Dokument „{id}“ verweist auf die unbekannte Sammlung „{collection}“.',
|
|
539
|
+
'cms.issueMessage.localizedPlainValue': 'Das sprachabhängige Feld „{field}“ enthält einen einfachen Wert im gemeinsamen „values“-Bereich — erwartet werden sprachabhängige Werte unter „localized.<locale>.{field}“. Der einfache Wert dient nur noch als letzte Rückfallebene.',
|
|
540
|
+
'cms.issueMessage.requiredLocalizedMissingDefault': 'Das sprachabhängige Pflichtfeld „{field}“ fehlt für die Standardsprache „{locale}“.',
|
|
541
|
+
'cms.issueMessage.localizedNoValueNoFallback': 'Das sprachabhängige Feld „{field}“ hat keinen Wert für die Sprache „{locale}“ und keine Rückfallebene.',
|
|
542
|
+
'cms.issueMessage.hierarchyCycle': 'Das Dokument „{id}“ ist sein eigener Vorfahre — die Elternkette bildet einen Zyklus.',
|
|
543
|
+
'cms.issueMessage.nestingDepthError': 'Das Dokument „{id}“ ist {depth} Ebenen tief verschachtelt und überschreitet damit das Schreib-Limit des Backends von {limit} Ebenen — selbst das Falten der Tiefe beim Speichern rettet ein so tiefes Dokument nicht, der Schreibvorgang kann nicht ausgeführt werden. Teilen Sie es in kleinere verknüpfte Dokumente auf.',
|
|
544
|
+
'cms.issueMessage.nestingDepthWarning': 'Das Dokument „{id}“ ist {depth} Ebenen tief verschachtelt und nähert sich damit dem Limit des Backends von {limit} Ebenen. Flachen Sie den tiefsten Pfad ab, bevor er weiter wächst.',
|
|
545
|
+
'cms.issueMessage.propFilterable': 'Die Komponenten-Eigenschaft „{field}“ deklariert „filterable“, was nur für Dokumentfelder gilt.',
|
|
546
|
+
'cms.issueMessage.propUnique': 'Die Komponenten-Eigenschaft „{field}“ deklariert „unique“, was nur über die Dokumente einer Sammlung hinweg gilt.',
|
|
547
|
+
'cms.issueMessage.singletonDrift': 'Das Einzelstück „{collection}“ hat {count} Dokumente — es sollte genau eines haben. Behalten Sie das richtige Dokument und entfernen Sie die übrigen, damit es sich direkt öffnen lässt.',
|
|
548
|
+
'cms.issueMessage.unknownAlias.one': 'Ein Dokument ist dem Alias „{alias}“ zugewiesen, der kein deklarierter Slot von „{collection}“ ist ({declared}). Weisen Sie einen gültigen Slot zu.',
|
|
549
|
+
'cms.issueMessage.unknownAlias.other': '{count} Dokumente sind dem Alias „{alias}“ zugewiesen, der kein deklarierter Slot von „{collection}“ ist ({declared}). Weisen Sie einen gültigen Slot zu.',
|
|
550
|
+
'cms.issueMessage.duplicateAlias': '{count} Dokumente teilen sich den Alias „{alias}“ in „{collection}“. Jeder Slot darf nur einem Dokument zugewiesen sein — weisen Sie neu zu oder entfernen Sie die Duplikate.',
|
|
551
|
+
'cms.issueMessage.unfilledSlot': 'Der Slot „{slot}“ von „{collection}“ hat noch kein Dokument. Weisen Sie eines zu, damit Code, der „{slot}“ auflöst, es findet.',
|
|
552
|
+
'cms.issueMessage.reservedFieldName': 'Der Feldname „{field}“ ist reserviert (der Diskriminator selbstbeschreibender Knoten).',
|
|
553
|
+
'cms.issueMessage.duplicateSelectOption': 'Doppelte Auswahloption „{option}“ im Feld „{field}“.',
|
|
554
|
+
'cms.issueMessage.duplicateObjectField': 'Doppeltes Feld „{field}“ im Objektfeld „{parent}“.',
|
|
555
|
+
'cms.issueMessage.assetCollectionSingleton': 'Die Mediensammlung „{collection}“ kann kein Einzelstück sein.',
|
|
556
|
+
'cms.issueMessage.unknownPresentationBodyField': 'Die Sammlung „{collection}“ deklariert „{field}“ als Inhaltsfeld der Darstellung, definiert dieses Feld aber nicht.',
|
|
557
|
+
'cms.issueMessage.duplicateCollectionField': 'Doppeltes Feld „{field}“ in der Sammlung „{collection}“.',
|
|
558
|
+
'cms.issueMessage.duplicateCollectionId': 'Doppelte Sammlungs-id „{collection}“.',
|
|
559
|
+
'cms.issueMessage.unknownLocale': 'Die Sprache „{locale}“ gehört nicht zum konfigurierten Sprachsatz.',
|
|
560
|
+
'cms.issueMessage.unknownVersion': 'Für das Dokument „{id}“ ist keine Version {version} aufgezeichnet.',
|
|
561
|
+
'cms.issueMessage.routeConflict': 'Die Routen „{first}“ und „{second}“ treffen auf dieselben URLs zu (Muster „{pattern}“).',
|
|
562
|
+
'cms.issueMessage.redirectLoop': 'Die Weiterleitungskette ab „{from}“ bildet eine Schleife (…{chain}).',
|
|
563
|
+
'cms.issueMessage.allDynamicShadowsMount': 'Die Route „{route}“ („{pattern}“) besteht nur aus dynamischen Segmenten und trifft damit auf JEDE URL mit {depth} Segmenten zu — die Hierarchie-Einhängung „{mountRoute}“ („{mountPattern}“) wird auf dieser Ebene nur dort herangezogen, wo die Route kein Dokument auflöst. Nichts ist dadurch grundsätzlich unerreichbar, aber der Vorrang entscheidet sich dort am INHALT statt an den Routen: {shadowed} werden stillschweigend verdeckt, sobald ein zusammengesetzter Pfad mit {claimed} kollidiert — und keine Seite erfährt davon. Geben Sie der Route ein statisches Präfix („/blog/:slug“), damit sie einen Namensraum beansprucht statt einer ganzen Ebene.',
|
|
564
|
+
'cms.issueMessage.mountCannotServe': 'Die Hierarchie-Einhängung „{route}“ („{pattern}“) kann niemals ein Dokument ausliefern: {why}. Der Resolver überspringt eine solche Einhängung stillschweigend, deshalb liefert jede URL unter „{space}“ einen 404, ohne dass ein Hinweis erscheint.',
|
|
565
|
+
'cms.issueMessage.deadCatchAll': 'Die Route „{route}“ („{pattern}“) liefert in beide Richtungen nichts: Sie endet in einem Catch-all, deshalb verwirft sie jeder Router — ihr Präfix enthält aber ein dynamisches Segment ({dynamic}), deshalb wird auch keine Hierarchie-Einhängung daraus gebaut. Das Präfix einer Einhängung muss statisch sein — deklarieren Sie eine Einhängung pro Sprache (`{ pattern: \'/de/:path*\', locale: \'de\' }`) oder entfernen Sie die Route.',
|
|
566
|
+
'cms.issueMessage.mountConflict': '{opening} — Einhängungen werden in Deklarationsreihenfolge aufgelöst, nicht nach Spezifität; deshalb beansprucht „{earlier}“ (Sammlung „{earlierCollection}“) dort jede URL, die der eigene Baum auflöst, und Dokumente aus „{laterCollection}“ sind an diesen URLs unerreichbar.',
|
|
567
|
+
'cms.issueMessage.routeBoundDocumentNotFound': 'Das an die Route gebundene Dokument „{collection}:{id}“ wurde nicht gefunden.',
|
|
568
|
+
'cms.issueMessage.routeParamNotCaptured': 'Die Route „{route}“ bindet ihr Dokument an den URL-Parameter „:{param}“, den das Muster „{pattern}“ nicht erfasst.',
|
|
569
|
+
'cms.issueMessage.collectionNotDefined': 'Die Sammlung „{collection}“ ist nicht definiert.',
|
|
570
|
+
'cms.issueMessage.unaddressableLocaleCollision': 'Keine URL erreicht „{id}“ in der Sprache „{locale}“: die Adresse wäre „{url}“, aber das erste Segment „{segment}“ ist ein deklarierter Sprachcode — die Website liest diese URL daher als Sprache „{segment}“, Pfad „{readPath}“. Benennen Sie dieses Segment um — an diesem Dokument oder an der übergeordneten Seite, zu der es gehört — damit es nicht mit einem Sprachcode kollidiert.',
|
|
571
|
+
'cms.issueDetail.expectsString': 'erwartet eine Zeichenkette',
|
|
572
|
+
'cms.issueDetail.expectsRichText': 'erwartet Rich-Text-Inhalt (eine Zeichenkette oder einen Rich-Text-Wert)',
|
|
573
|
+
'cms.issueDetail.expectsNumber': 'erwartet eine Zahl',
|
|
574
|
+
'cms.issueDetail.expectsBoolean': 'erwartet einen Wahrheitswert',
|
|
575
|
+
'cms.issueDetail.expectsIsoDate': 'erwartet ein ISO-8601-Datum (z. B. „2026-09-18“)',
|
|
576
|
+
'cms.issueDetail.expectsDate': 'erwartet ein Datum (ISO-8601-Zeichenkette oder Epochenzahl)',
|
|
577
|
+
'cms.issueDetail.valueNotAmongOptions': 'hat den Wert „{value}“, der nicht zu seinen Optionen gehört',
|
|
578
|
+
'cms.issueDetail.expectsObjectList': 'erwartet eine Liste von Objekten',
|
|
579
|
+
'cms.issueDetail.expectsObject': 'erwartet ein Objekt',
|
|
580
|
+
'cms.issueDetail.implausibleYear': 'hat das Jahr {year}, was unbeabsichtigt wirkt (erwartet wird eine vierstellige Jahreszahl)',
|
|
581
|
+
'cms.issueDetail.dateBoundNotADate': 'deklariert {bound} `{declared}` auf einem Datumsfeld, was kein Datum ist — verwenden Sie eine ISO-8601-Zeichenkette wie „1900-01-01“',
|
|
582
|
+
'cms.issueDetail.earlierThanMin': 'liegt vor {min}',
|
|
583
|
+
'cms.issueDetail.laterThanMax': 'liegt nach {max}',
|
|
584
|
+
'cms.issueDetail.stringBoundOffDate': 'deklariert {bound} „{declared}“ als Zeichenkette, was nur auf einem Datumsfeld sinnvoll ist — verwenden Sie eine Zahl',
|
|
585
|
+
'cms.issueDetail.shorterThanMinLength': 'ist kürzer als die Mindestlänge {min}',
|
|
586
|
+
'cms.issueDetail.longerThanMaxLength': 'ist länger als die Höchstlänge {max}',
|
|
587
|
+
'cms.issueDetail.doesNotMatchPattern': 'entspricht nicht dem Muster {pattern}',
|
|
588
|
+
'cms.issueDetail.lessThanMin': 'ist kleiner als das Minimum {min}',
|
|
589
|
+
'cms.issueDetail.greaterThanMax': 'ist größer als das Maximum {max}',
|
|
590
|
+
'cms.issueDetail.fewerThanMinItems': 'hat weniger als {min} Einträge',
|
|
591
|
+
'cms.issueDetail.moreThanMaxItems': 'hat mehr als {max} Einträge',
|
|
592
|
+
'cms.issueDetail.duplicateValue': 'muss innerhalb der Sammlung eindeutig sein, aber das Dokument „{other}“ hat diesen Wert bereits',
|
|
593
|
+
'cms.issueDetail.duplicateValueForLocale': 'muss innerhalb der Sammlung eindeutig sein, aber das Dokument „{other}“ hat diesen Wert bereits für die Sprache „{locale}“',
|
|
594
|
+
'cms.issueDetail.singleValuedManyTargets': 'ist einwertig, hat aber {count} Ziele',
|
|
595
|
+
'cms.issueDetail.notADocumentReference': 'enthält einen Wert, der keine Dokumentreferenz ist',
|
|
596
|
+
'cms.issueDetail.pointsAtOtherCollection': 'zeigt auf die Sammlung „{got}“, zielt aber auf „{target}“',
|
|
597
|
+
'cms.issueDetail.expectsAssetIdString': 'erwartet eine Medien-id als Zeichenkette',
|
|
598
|
+
'cms.issueDetail.shadowedRootDocuments': 'die WURZEL-Dokumente von „{collection}“ unter „{space}“',
|
|
599
|
+
'cms.issueDetail.shadowedNestedDocuments': '„{collection}“-Dokumente, die {levels} Ebenen unter „{space}“ verschachtelt sind',
|
|
600
|
+
'cms.issueDetail.claimedBySlug': 'einem Slug aus „{collection}“',
|
|
601
|
+
'cms.issueDetail.claimedByOwnDocument': 'dem eigenen Dokument der Route',
|
|
602
|
+
'cms.issueDetail.mountCollectionNotDeclared': 'die Sammlung „{collection}“ ist nicht deklariert',
|
|
603
|
+
'cms.issueDetail.mountCollectionNotHierarchical': 'die Sammlung „{collection}“ hat keine `parent`-Selbstbeziehung und damit keine verschachtelten Pfade zum Zusammensetzen',
|
|
604
|
+
'cms.issueDetail.mountsSamePath': 'Die Hierarchie-Einhängungen „{earlier}“ („{earlierPattern}“) und „{later}“ („{laterPattern}“) sind beide unter „{space}“ eingehängt',
|
|
605
|
+
'cms.issueDetail.mountsNested': 'Die Hierarchie-Einhängung „{later}“ („{laterPattern}“) liegt innerhalb von „{earlier}“ („{earlierPattern}“), das zuerst deklariert ist und ebenfalls „{space}“ vollständig beansprucht',
|
|
606
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
425
607
|
// CMS — details pane (slot + sidebar fields)
|
|
426
608
|
// ─────────────────────────────────────────────────────────────────────────
|
|
427
609
|
'cms.details.slot': 'Slot',
|
|
@@ -888,6 +1070,23 @@ export const deMessages = {
|
|
|
888
1070
|
'builder.clipboard.pastedInto': '{summary} · in {destination}',
|
|
889
1071
|
'builder.clipboard.issueComponent': 'Komponente',
|
|
890
1072
|
'builder.clipboard.issueBinding': 'Bindung',
|
|
1073
|
+
// EC-494 — Zwischenablage-Fehler aus der Engine plus der studio-seitige Einfüge-Diff.
|
|
1074
|
+
'builder.clipboard.error.corruptJson': 'Der Inhalt der Zwischenablage ist kein gültiges JSON: {detail}',
|
|
1075
|
+
'builder.clipboard.error.notEnvelope': 'Der Inhalt der Zwischenablage ist kein Elytra-Fragment-Umschlag.',
|
|
1076
|
+
'builder.clipboard.error.foreignFormat': 'Der Inhalt der Zwischenablage ist kein Elytra-Fragment (erwartetes Format "{expected}").',
|
|
1077
|
+
'builder.clipboard.error.versionNewer': 'Die Fragmentversion {version} ist neuer als von diesem Studio unterstützt (Version {supported}).',
|
|
1078
|
+
'builder.clipboard.error.versionUnsupported': 'Die Fragmentversion {version} wird nicht unterstützt (dieses Studio liest Version {supported}).',
|
|
1079
|
+
'builder.clipboard.error.invalidPayload': 'Das Fragment der Zwischenablage verletzt das Fragment-Schema.',
|
|
1080
|
+
'builder.clipboard.error.nodeNotFound': 'In der Komposition existiert kein Knoten "{nodeId}".',
|
|
1081
|
+
'builder.clipboard.error.fixtureNotFound': 'Die Komponente "{component}" deklariert kein Fixture namens "{fixture}".',
|
|
1082
|
+
'builder.clipboard.issue.unresolvedBinding': 'Die Datenquelle "{sourceId}" ist in diesem Projekt nicht verfügbar.',
|
|
1083
|
+
'builder.clipboard.issue.missingComponent': 'Die Komponente "{componentId}" ist in diesem Projekt nicht registriert.',
|
|
1084
|
+
'builder.clipboard.summary.pasted': 'Eingefügt',
|
|
1085
|
+
'builder.clipboard.summary.pastedWith': 'Eingefügt — {parts}',
|
|
1086
|
+
'builder.clipboard.summary.bindingUnresolvedOne': '{count} Bindung nicht aufgelöst',
|
|
1087
|
+
'builder.clipboard.summary.bindingsUnresolved': '{count} Bindungen nicht aufgelöst',
|
|
1088
|
+
'builder.clipboard.summary.componentMissingOne': '{count} Komponente fehlt',
|
|
1089
|
+
'builder.clipboard.summary.componentsMissing': '{count} Komponenten fehlen',
|
|
891
1090
|
'builder.reuse.makeReusable': 'Wiederverwendbar machen',
|
|
892
1091
|
'builder.reuse.detachToCopy': 'Als Kopie loslösen',
|
|
893
1092
|
'builder.reuse.defaultElementName': 'Wiederverwendbares Element',
|
|
@@ -1065,6 +1264,7 @@ export const deMessages = {
|
|
|
1065
1264
|
'ai.error.route': 'Der Assistent ist derzeit nicht verfügbar. Bitte versuchen Sie es erneut.',
|
|
1066
1265
|
'ai.error.generic': 'Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.',
|
|
1067
1266
|
'ai.error.applyFailed': 'Der Entwurf konnte nicht übernommen werden.',
|
|
1267
|
+
'ai.error.applyInvalid': 'Der Vorschlag passt nicht mehr zum aktuellen Dokument: {issues}',
|
|
1068
1268
|
'ai.error.stepCap': 'Der Assistent hat nach {max} Werkzeugschritten angehalten. Senden Sie eine weitere Nachricht, um fortzufahren.',
|
|
1069
1269
|
// EC-332 — full-document translation (header action + chat tool)
|
|
1070
1270
|
'ai.translate.action': 'Dokument übersetzen',
|