This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new f79dbe4 feat(infra-3d): internationalize the 3D-map config admin page
(#36)
f79dbe4 is described below
commit f79dbe41f095112a77720ba86df3df574f0ed42b
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Mon Jun 1 22:43:56 2026 +0800
feat(infra-3d): internationalize the 3D-map config admin page (#36)
The /admin/3d-map config editor was the only admin page still rendering
hardcoded English in every locale — it landed after the 0.6.0 i18n pass.
Wire it through vue-i18n like the other admin pages and ship full
eight-locale coverage.
- Infra3dAdminView.vue: 58 t() keys plus 5 <i18n-t> blocks for the prose
hints with inline <code>/<strong>; named-arg interpolation for the
dynamic counts, the via tag, and the Push-button state. Layer keys,
MQE ids, and the .* regex stay verbatim.
- en.json: 55 new source keys.
- de/es/fr/ja/ko/pt/zh-CN: the 55 keys translated, matching each
catalog's existing terminology.
---
CHANGELOG.md | 2 +-
.../features/admin/infra-3d/Infra3dAdminView.vue | 158 +++++++++++----------
apps/ui/src/i18n/locales/de.json | 57 +++++++-
apps/ui/src/i18n/locales/en.json | 57 +++++++-
apps/ui/src/i18n/locales/es.json | 57 +++++++-
apps/ui/src/i18n/locales/fr.json | 57 +++++++-
apps/ui/src/i18n/locales/ja.json | 57 +++++++-
apps/ui/src/i18n/locales/ko.json | 57 +++++++-
apps/ui/src/i18n/locales/pt.json | 57 +++++++-
apps/ui/src/i18n/locales/zh-CN.json | 57 +++++++-
10 files changed, 532 insertions(+), 84 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e471c8..607bd9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,7 @@ per device.
- **UI chrome.** Every routed page and every shared sub-component
renders through `vue-i18n`; non-English locales now cover every
admin page (Roles, Users, Auth status, Alert page setup, Global
- defaults), every operate page (Alerting rules, DSL catalog / editor /
+ defaults, 3D-map config), every operate page (Alerting rules, DSL catalog /
editor /
dump, OAL catalog, Live debugger + MAL / LAL / OAL, Capture history,
Metrics inspect, OAP config, TTL), the alarms surface, and the
shared modals. Long lede paragraphs that previously rendered as
diff --git a/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
b/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
index 23a627b..4636d2c 100644
--- a/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
+++ b/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
@@ -42,6 +42,7 @@
-->
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
import { useLayers } from '@/shell/useLayers';
import {
bff,
@@ -68,6 +69,7 @@ import MonacoDiff from
'@/features/operate/_shared/MonacoDiff.vue';
// and an out-of-tree layer (config exists, OAP no longer reports it)
// also shows so the admin can remove it.
const { availableLayers } = useLayers();
+const { t } = useI18n();
// ── Template-sync state ───────────────────────────────────────────────
// The 3D-map config is a singleton template kind — ONE row,
@@ -415,8 +417,8 @@ const layersByTier = computed<Record<string,
LayerRow[]>>(() => {
// bucket (tagged "filtered out"), never under a tier even if pinned, so
// the editor matches what the scene renders.
if (resolvedLevels.value[r.key]?.via === 'filtered') continue;
- const t = explicitTier.value[r.key];
- if (t && out[t]) out[t].push(r);
+ const tierId = explicitTier.value[r.key];
+ if (tierId && out[tierId]) out[tierId].push(r);
}
return out;
});
@@ -567,47 +569,47 @@ const stats = computed(() => {
<div class="i3d-admin">
<header class="hd">
<div class="hd-text">
- <span class="kicker">Dashboard setup · 3D Infra Map</span>
- <h1>3D Infrastructure Map</h1>
+ <span class="kicker">{{ t('Dashboard setup · 3D Infra Map') }}</span>
+ <h1>{{ t('3D Infrastructure Map') }}</h1>
<p class="lede">
- Config for the <code>/3d/map</code> view, published to OAP. Levels
- control the vertical stack; per-layer color + metrics drive each
- cube. Edits save to a <strong>local draft in this browser</strong>;
- <strong>Check diff & push</strong> publishes to OAP — the map
- renders the remote, with bundled defaults as fallback.
+ <i18n-t keypath="Config for the {mapPath} view, published to OAP.
Levels control the vertical stack; per-layer color + metrics drive each cube.
Edits save to a {localDraft}; {checkDiffPush} publishes to OAP — the map
renders the remote, with bundled defaults as fallback." tag="span"
scope="global">
+ <template #mapPath><code>/3d/map</code></template>
+ <template #localDraft><strong>{{ t('local draft in this browser')
}}</strong></template>
+ <template #checkDiffPush><strong>{{ t('Check diff & push')
}}</strong></template>
+ </i18n-t>
</p>
</div>
<div class="hd-actions">
<span class="src-pill" :data-src="editorSource">
- editing: {{ editorSource }}
+ {{ t('editing: {source}', { source: editorSource }) }}
<TemplateStatusBadge v-if="badge" :status="badge" />
</span>
<div class="reset-wrap">
- <button class="btn" :disabled="pushing" @click="resetMenuOpen =
!resetMenuOpen">Reset to ▾</button>
+ <button class="btn" :disabled="pushing" @click="resetMenuOpen =
!resetMenuOpen">{{ t('Reset to') }} ▾</button>
<div v-if="resetMenuOpen" class="reset-menu">
<button
class="reset-item"
:disabled="isSynced"
- :title="isSynced ? 'Bundled equals OAP-live — nothing to reset
to.' : 'Discard current edits and reload the bundled (shipped) default.'"
+ :title="isSynced ? t('Bundled equals OAP-live — nothing to reset
to.') : t('Discard current edits and reload the bundled (shipped) default.')"
@click="resetTo('bundled')"
>
- Bundled<span v-if="isSynced" class="reset-suffix">
(synced)</span>
+ {{ t('Bundled') }}<span v-if="isSynced" class="reset-suffix">
({{ t('synced') }})</span>
</button>
<button
class="reset-item"
:disabled="!hasRemote"
- :title="hasRemote ? 'Discard current edits and reload OAP\'s
live version.' : 'OAP has no copy of this config yet.'"
+ :title="hasRemote ? t('Discard current edits and reload OAP\'s
live version.') : t('OAP has no copy of this config yet.')"
@click="resetTo('remote')"
>
- Remote
+ {{ t('Remote') }}
</button>
</div>
</div>
<button class="btn" :disabled="!dirty" @click="save">
- {{ hasLocalDraft && !dirty ? 'Saved local' : 'Save local' }}
+ {{ hasLocalDraft && !dirty ? t('Saved local') : t('Save local') }}
</button>
<button class="btn primary" :disabled="readOnly || (!dirty &&
!hasLocalDraft)" @click="openPushDiff">
- Check diff & push
+ {{ t('Check diff & push') }}
</button>
</div>
</header>
@@ -618,15 +620,19 @@ const stats = computed(() => {
</ul>
<div v-if="loadError" class="loading">
- Couldn't load the 3D-map config — the BFF may be unreachable. Refresh
the page to retry.
+ {{ t('Couldn\'t load the 3D-map config — the BFF may be unreachable.
Refresh the page to retry.') }}
</div>
- <div v-else-if="!ready" class="loading">Loading config…</div>
+ <div v-else-if="!ready" class="loading">{{ t('Loading config…') }}</div>
<template v-else-if="draft">
<!-- ── Global filter ─────────────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Global layer filter</h2>
- <span class="sec-hint">The one top-level gate — a layer this JS
regex excludes is off the map (it still shows below in Unpinned, tagged
"filtered out"). Default <code>.*</code> admits all.</span>
+ <h2>{{ t('Global layer filter') }}</h2>
+ <span class="sec-hint">
+ <i18n-t keypath='The one top-level gate — a layer this JS regex
excludes is off the map (it still shows below in Unpinned, tagged "filtered
out"). Default {dotStar} admits all.' tag="span" scope="global">
+ <template #dotStar><code>.*</code></template>
+ </i18n-t>
+ </span>
</header>
<div class="sect-body">
<label class="field">
@@ -639,14 +645,18 @@ const stats = computed(() => {
<!-- ── Tiers & layers ─────────────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Tiers & layers (top → bottom)</h2>
+ <h2>{{ t('Tiers & layers (top → bottom)') }}</h2>
<span class="sec-hint">
- Every layer belongs to one tier — set it with the tier dropdown;
order = vertical stacking.
- <strong>{{ stats.oap }}</strong> in OAP<template
v-if="stats.unclassified > 0"> ·
- <strong class="warn-count">{{ stats.unclassified }}</strong>
unpinned → fallback</template>
+ {{ t('Every layer belongs to one tier — set it with the tier
dropdown; order = vertical stacking.') }}
+ <i18n-t keypath="{count} in OAP" tag="span" scope="global">
+ <template #count><strong>{{ stats.oap }}</strong></template>
+ </i18n-t><template v-if="stats.unclassified > 0"> ·
+ <i18n-t keypath="{count} unpinned → fallback" tag="span"
scope="global">
+ <template #count><strong class="warn-count">{{
stats.unclassified }}</strong></template>
+ </i18n-t></template>
</span>
- <input class="inp search" v-model="layerSearch" placeholder="filter
layers…" />
- <button type="button" class="btn small" @click="addLevel">+ add
tier</button>
+ <input class="inp search" v-model="layerSearch"
:placeholder="t('filter layers…')" />
+ <button type="button" class="btn small" @click="addLevel">{{ t('+
add tier') }}</button>
</header>
<div class="sect-body">
<article v-for="(lvl, idx) in levelsSorted" :key="lvl.id"
class="tier-card">
@@ -654,11 +664,11 @@ const stats = computed(() => {
<span class="lvl-order">#{{ lvl.order }}</span>
<input class="inp lvl-id mono" v-model="lvl.id"
placeholder="apps" />
<input class="inp lvl-label" v-model="lvl.label"
placeholder="Apps" />
- <span class="tier-count">{{ layersByTier[lvl.id]?.length ?? 0 }}
layers</span>
+ <span class="tier-count">{{ t('{count} layers', { count:
layersByTier[lvl.id]?.length ?? 0 }) }}</span>
<div class="lvl-spacer" />
<button type="button" class="btn tiny" :disabled="idx === 0"
@click="moveLevel(idx, -1)">↑</button>
<button type="button" class="btn tiny" :disabled="idx ===
levelsSorted.length - 1" @click="moveLevel(idx, 1)">↓</button>
- <button type="button" class="btn tiny danger"
@click="removeLevel(lvl.id)">remove</button>
+ <button type="button" class="btn tiny danger"
@click="removeLevel(lvl.id)">{{ t('remove') }}</button>
</header>
<div class="tier-body">
<div class="layer-rows">
@@ -666,20 +676,20 @@ const stats = computed(() => {
<input type="color" class="color-pick"
:value="row.spec?.color ?? '#8a8a8a'" @input="(e) =>
(ensureLayerSpec(row.key).color = (e.target as HTMLInputElement).value)" />
<span class="layer-key">{{ row.key }}</span>
<template v-if="row.spec?.metric">
- <input class="inp mono metric-mqe"
v-model="row.spec.metric.mqe" placeholder="mqe e.g. service_cpm" />
- <input class="inp metric-lbl"
v-model="row.spec.metric.label" placeholder="label" />
- <input class="inp metric-unit"
v-model="row.spec.metric.unit" placeholder="unit" />
- <button class="btn tiny danger" type="button"
title="remove metric" @click="clearMetric(row.key)">⊘</button>
+ <input class="inp mono metric-mqe"
v-model="row.spec.metric.mqe" :placeholder="t('mqe e.g. service_cpm')" />
+ <input class="inp metric-lbl"
v-model="row.spec.metric.label" :placeholder="t('label')" />
+ <input class="inp metric-unit"
v-model="row.spec.metric.unit" :placeholder="t('unit')" />
+ <button class="btn tiny danger" type="button"
:title="t('remove metric')" @click="clearMetric(row.key)">⊘</button>
</template>
- <button v-else class="btn tiny ghost" type="button"
@click="ensureMetric(row.key)">+ metric</button>
+ <button v-else class="btn tiny ghost" type="button"
@click="ensureMetric(row.key)">{{ t('+ metric') }}</button>
<div class="row-spacer" />
<select class="inp tier-select"
:value="explicitTier[row.key] ?? ''" @change="(e) => onMoveTier(row.key, e)">
- <option value="">— unpinned —</option>
- <option v-for="t in levelsSorted" :key="t.id"
:value="t.id">{{ t.label }}</option>
+ <option value="">{{ t('— unpinned —') }}</option>
+ <option v-for="tier in levelsSorted" :key="tier.id"
:value="tier.id">{{ tier.label }}</option>
</select>
- <button class="btn tiny ghost" type="button" title="Remove
from this tier (moves to Unpinned)" @click="assignLayerToLevel(row.key,
null)">×</button>
+ <button class="btn tiny ghost" type="button"
:title="t('Remove from this tier (moves to Unpinned)')"
@click="assignLayerToLevel(row.key, null)">×</button>
</div>
- <div v-if="(layersByTier[lvl.id]?.length ?? 0) === 0"
class="tier-empty">No layers pinned — use a layer's tier dropdown to move one
here.</div>
+ <div v-if="(layersByTier[lvl.id]?.length ?? 0) === 0"
class="tier-empty">{{ t('No layers pinned — use a layer\'s tier dropdown to
move one here.') }}</div>
</div>
</div>
</article>
@@ -689,10 +699,11 @@ const stats = computed(() => {
<!-- ── Unpinned → failover ────────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Unpinned layers</h2>
+ <h2>{{ t('Unpinned layers') }}</h2>
<span class="sec-hint">
- Pinned to no tier — they land on the failover tier
- (<strong>{{ levelLabel(draft.unknownLayer.level) }}</strong>).
Pick a tier to pin one.
+ <i18n-t keypath="Pinned to no tier — they land on the failover
tier ({failoverTier}). Pick a tier to pin one." tag="span" scope="global">
+ <template #failoverTier><strong>{{
levelLabel(draft.unknownLayer.level) }}</strong></template>
+ </i18n-t>
</span>
</header>
<div class="sect-body">
@@ -707,22 +718,22 @@ const stats = computed(() => {
<span class="layer-key">{{ row.key }}</span>
<template v-if="row.spec?.metric">
<input class="inp mono metric-mqe"
v-model="row.spec.metric.mqe" placeholder="mqe" />
- <input class="inp metric-lbl" v-model="row.spec.metric.label"
placeholder="label" />
- <input class="inp metric-unit" v-model="row.spec.metric.unit"
placeholder="unit" />
- <button class="btn tiny danger" type="button" title="remove
metric" @click="clearMetric(row.key)">⊘</button>
+ <input class="inp metric-lbl" v-model="row.spec.metric.label"
:placeholder="t('label')" />
+ <input class="inp metric-unit" v-model="row.spec.metric.unit"
:placeholder="t('unit')" />
+ <button class="btn tiny danger" type="button"
:title="t('remove metric')" @click="clearMetric(row.key)">⊘</button>
</template>
- <button v-else class="btn tiny ghost" type="button"
@click="ensureMetric(row.key)">+ metric</button>
+ <button v-else class="btn tiny ghost" type="button"
@click="ensureMetric(row.key)">{{ t('+ metric') }}</button>
<div class="row-spacer" />
- <span class="via-tag" :data-via="resolvedLevels[row.key]?.via"
:title="`falls to ${resolvedLevels[row.key]?.via}`">
- {{ resolvedLevels[row.key]?.via === 'filtered' ? 'filtered
out' : '→ ' + levelLabel(resolvedLevels[row.key]?.levelId ?? null) }}
+ <span class="via-tag" :data-via="resolvedLevels[row.key]?.via"
:title="t('falls to {via}', { via: resolvedLevels[row.key]?.via })">
+ {{ resolvedLevels[row.key]?.via === 'filtered' ? t('filtered
out') : '→ ' + levelLabel(resolvedLevels[row.key]?.levelId ?? null) }}
</span>
<select class="inp tier-select" :value="explicitTier[row.key] ??
''" @change="(e) => onMoveTier(row.key, e)">
- <option value="">— pin to tier —</option>
- <option v-for="t in levelsSorted" :key="t.id" :value="t.id">{{
t.label }}</option>
+ <option value="">{{ t('— pin to tier —') }}</option>
+ <option v-for="tier in levelsSorted" :key="tier.id"
:value="tier.id">{{ tier.label }}</option>
</select>
- <button class="btn tiny danger" type="button" title="Delete this
layer from the config entirely"
@click="removeLayerFromConfig(row.key)">×</button>
+ <button class="btn tiny danger" type="button" :title="t('Delete
this layer from the config entirely')"
@click="removeLayerFromConfig(row.key)">×</button>
</div>
- <div v-if="unpinnedRows.length === 0" class="empty">No unpinned
layers.</div>
+ <div v-if="unpinnedRows.length === 0" class="empty">{{ t('No
unpinned layers.') }}</div>
</div>
</div>
</section>
@@ -730,9 +741,9 @@ const stats = computed(() => {
<!-- ── Groups ────────────────────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Logic groups</h2>
- <span class="sec-hint">Several layers drawn as one labelled block on
a tier (e.g. Self-Observability). Each member keeps its own cube color.</span>
- <button type="button" class="btn small" @click="addGroup">+ add
group</button>
+ <h2>{{ t('Logic groups') }}</h2>
+ <span class="sec-hint">{{ t('Several layers drawn as one labelled
block on a tier (e.g. Self-Observability). Each member keeps its own cube
color.') }}</span>
+ <button type="button" class="btn small" @click="addGroup">{{ t('+
add group') }}</button>
</header>
<div class="sect-body">
<div class="groups-grid">
@@ -747,40 +758,40 @@ const stats = computed(() => {
<input class="inp group-id mono" v-model="g.id"
placeholder="self-observability" />
<input class="inp group-label" v-model="g.label"
placeholder="Self-Observability" />
<div class="lvl-spacer" />
- <button type="button" class="btn tiny danger"
@click="removeGroup(g.id)">remove</button>
+ <button type="button" class="btn tiny danger"
@click="removeGroup(g.id)">{{ t('remove') }}</button>
</header>
<div class="group-body">
<div class="row-2">
<label class="field">
- <span class="lbl small">level</span>
+ <span class="lbl small">{{ t('level') }}</span>
<select class="inp" v-model="g.level">
<option v-for="lvl in levelsSorted" :key="lvl.id"
:value="lvl.id">{{ lvl.label }} ({{ lvl.id }})</option>
</select>
</label>
<label class="field">
- <span class="lbl small">icon</span>
+ <span class="lbl small">{{ t('icon') }}</span>
<select class="inp" v-model="g.icon">
<option v-for="ic in ICON_NAMES" :key="ic"
:value="ic">{{ ic }}</option>
</select>
</label>
</div>
<div class="field">
- <span class="lbl small">member layers ({{ g.layers.length
}})</span>
+ <span class="lbl small">{{ t('member layers ({count})', {
count: g.layers.length }) }}</span>
<div class="chips">
<span v-for="k in g.layers" :key="k" class="chip">
{{ k }}
- <button class="x" type="button" title="remove from
group" @click="removeLayerFromGroup(g, k)">×</button>
+ <button class="x" type="button" :title="t('remove from
group')" @click="removeLayerFromGroup(g, k)">×</button>
</span>
<select class="add-layer" :value="''" @change="(e) =>
onAddLayerToGroup(g, e)">
- <option value="">+ add layer…</option>
+ <option value="">{{ t('+ add layer…') }}</option>
<option v-for="k in groupCandidates(g)" :key="k"
:value="k">{{ k }}</option>
</select>
</div>
- <p v-if="g.layers.length === 0" class="hint-sm warn">A group
needs at least one layer — pushing will be rejected until you add one.</p>
+ <p v-if="g.layers.length === 0" class="hint-sm warn">{{ t('A
group needs at least one layer — pushing will be rejected until you add one.')
}}</p>
</div>
</div>
</article>
- <div v-if="(draft.groups ?? []).length === 0" class="empty">No
logic groups. Add one to cluster related layers into a single block.</div>
+ <div v-if="(draft.groups ?? []).length === 0" class="empty">{{
t('No logic groups. Add one to cluster related layers into a single block.')
}}</div>
</div>
</div>
</section>
@@ -788,17 +799,15 @@ const stats = computed(() => {
<!-- ── Service-map layers ─────────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Service-map layers</h2>
+ <h2>{{ t('Service-map layers') }}</h2>
<span class="sec-hint">
- These layers' templates provide a service map, so their cubes
render
- as a call graph (and seed the cross-layer hierarchy). Read-only —
- it's a layer-template property, not a 3D-map setting.
+ {{ t('These layers\' templates provide a service map, so their
cubes render as a call graph (and seed the cross-layer hierarchy). Read-only —
it\'s a layer-template property, not a 3D-map setting.') }}
</span>
</header>
<div class="sect-body">
<div class="chips">
<span v-for="k in serviceMapLayers" :key="k" class="chip">{{ k
}}</span>
- <span v-if="serviceMapLayers.length === 0"
class="chips-empty">None reporting a service map.</span>
+ <span v-if="serviceMapLayers.length === 0" class="chips-empty">{{
t('None reporting a service map.') }}</span>
</div>
</div>
</section>
@@ -806,12 +815,12 @@ const stats = computed(() => {
<!-- ── Unknown layer fallback ─────────────────────────────────── -->
<section class="sect">
<header class="sect-head">
- <h2>Failover tier</h2>
- <span class="sec-hint">The single catch-all: any layer no tier pins
lands here.</span>
+ <h2>{{ t('Failover tier') }}</h2>
+ <span class="sec-hint">{{ t('The single catch-all: any layer no tier
pins lands here.') }}</span>
</header>
<div class="sect-body">
<label class="field">
- <span class="lbl">level</span>
+ <span class="lbl">{{ t('level') }}</span>
<select class="inp fallback-level"
v-model="draft.unknownLayer.level">
<option v-for="lvl in levelsSorted" :key="lvl.id"
:value="lvl.id">{{ lvl.label }} ({{ lvl.id }})</option>
</select>
@@ -822,11 +831,10 @@ const stats = computed(() => {
</template>
<!-- ── Check diff & push ──────────────────────────────────────────── -->
- <Modal :open="pushDiffOpen" title="Push 3D-map config to OAP"
width="min(1100px, 94vw)" @close="pushDiffOpen = false">
+ <Modal :open="pushDiffOpen" :title="t('Push 3D-map config to OAP')"
width="min(1100px, 94vw)" @close="pushDiffOpen = false">
<div class="push-modal">
<p class="push-lede">
- Left = live on OAP (remote). Right = your local draft. Pushing
- replaces the OAP copy — the map renders it on the next visit.
+ {{ t('Left = live on OAP (remote). Right = your local draft. Pushing
replaces the OAP copy — the map renders it on the next visit.') }}
</p>
<ul v-if="pushErr && pushErr.length" class="issues">
<li v-for="(it, i) in pushErr" :key="i"><code>{{ it }}</code></li>
@@ -834,9 +842,9 @@ const stats = computed(() => {
<MonacoDiff :original="pushRemotePretty" :modified="pushLocalPretty"
language="json" />
</div>
<template #footer>
- <button class="btn" :disabled="pushing" @click="pushDiffOpen =
false">Cancel</button>
+ <button class="btn" :disabled="pushing" @click="pushDiffOpen =
false">{{ t('Cancel') }}</button>
<button class="btn primary" :disabled="pushing || readOnly"
@click="pushToOap">
- {{ pushing ? 'Pushing…' : 'Push to OAP' }}
+ {{ pushing ? t('Pushing…') : t('Push to OAP') }}
</button>
</template>
</Modal>
diff --git a/apps/ui/src/i18n/locales/de.json b/apps/ui/src/i18n/locales/de.json
index 013daa3..0d800f8 100644
--- a/apps/ui/src/i18n/locales/de.json
+++ b/apps/ui/src/i18n/locales/de.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— Wähle einen laufenden Pod oder prüfe, ob
On-Demand-Pod-Logs in OAP aktiviert sind.",
"Select a service to begin.": "Wähle einen Service, um zu beginnen.",
"Select a pod to list its containers.": "Wähle einen Pod, um seine Container
aufzulisten.",
- "Select a container, then press Start to tail its logs.": "Wähle einen
Container und drücke Start, um seine Logs live zu verfolgen."
+ "Select a container, then press Start to tail its logs.": "Wähle einen
Container und drücke Start, um seine Logs live zu verfolgen.",
+ "Dashboard setup · 3D Infra Map": "Dashboard-Einrichtung · 3D Infra Map",
+ "3D Infrastructure Map": "3D-Infrastrukturkarte",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "Konfiguration für die Ansicht {mapPath},
auf OAP veröffentlicht. Ebenen steuern die vertikale Stapelung; Farbe und
Metriken pro Ebene bestimmen jeden Würfel. Änderungen werden in einem
{localDraft} gespeichert; {checkDiffPush} verö [...]
+ "local draft in this browser": "lokaler Entwurf in diesem Browser",
+ "editing: {source}": "Bearbeitung: {source}",
+ "Reset to": "Zurücksetzen auf",
+ "Bundled equals OAP-live — nothing to reset to.": "Mitgeliefert entspricht
der OAP-Live-Version — nichts zum Zurücksetzen.",
+ "synced": "synchron",
+ "OAP has no copy of this config yet.": "OAP hat noch keine Kopie dieser
Konfiguration.",
+ "Saved local": "Lokal gespeichert",
+ "Save local": "Lokal speichern",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "3D-map-Konfiguration konnte nicht geladen werden — der BFF
ist möglicherweise nicht erreichbar. Seite neu laden, um es erneut zu
versuchen.",
+ "Loading config…": "Konfiguration wird geladen…",
+ "Global layer filter": "Globaler Ebenen-Filter",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "Die einzige übergeordnete Schranke — eine Ebene, die diese
JS-regex ausschließt, erscheint nicht auf der Karte (sie wird unten weiterhin
unter „Nicht angeheftet“ mit der Markierung „filtered out“ angezeigt). Der
Standard {dotStar} lässt alle zu.",
+ "Tiers & layers (top → bottom)": "Stufen & Ebenen (oben → unten)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "Jede Ebene gehört zu genau einer Stufe — lege sie über
das Stufen-Dropdown fest; die Reihenfolge entspricht der vertikalen Stapelung.",
+ "{count} in OAP": "{count} in OAP",
+ "{count} unpinned → fallback": "{count} nicht angeheftet → Fallback",
+ "filter layers…": "Ebenen filtern…",
+ "+ add tier": "+ Stufe hinzufügen",
+ "{count} layers": "{count} Ebenen",
+ "mqe e.g. service_cpm": "mqe, z. B. service_cpm",
+ "label": "Bezeichnung",
+ "unit": "Einheit",
+ "remove metric": "Metrik entfernen",
+ "+ metric": "+ Metrik",
+ "— unpinned —": "— nicht angeheftet —",
+ "Remove from this tier (moves to Unpinned)": "Aus dieser Stufe entfernen
(wandert zu „Nicht angeheftet“)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "Keine
Ebenen angeheftet — verschiebe eine über das Stufen-Dropdown einer Ebene
hierher.",
+ "Unpinned layers": "Nicht angeheftete Ebenen",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "An keine Stufe angeheftet — sie landen auf der
Failover-Stufe ({failoverTier}). Wähle eine Stufe aus, um eine anzuheften.",
+ "falls to {via}": "fällt auf {via} zurück",
+ "filtered out": "filtered out",
+ "— pin to tier —": "— an Stufe anheften —",
+ "Delete this layer from the config entirely": "Diese Ebene vollständig aus
der Konfiguration löschen",
+ "No unpinned layers.": "Keine nicht angehefteten Ebenen.",
+ "Logic groups": "Logikgruppen",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "Mehrere Ebenen,
die als ein beschrifteter Block auf einer Stufe dargestellt werden (z. B.
Self-Observability). Jedes Mitglied behält seine eigene Würfelfarbe.",
+ "+ add group": "+ Gruppe hinzufügen",
+ "level": "Ebene",
+ "icon": "Symbol",
+ "member layers ({count})": "Mitglieds-Ebenen ({count})",
+ "remove from group": "aus Gruppe entfernen",
+ "+ add layer…": "+ Ebene hinzufügen…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "Eine Gruppe braucht mindestens eine Ebene — die Veröffentlichung wird
abgelehnt, bis du eine hinzufügst.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"Keine Logikgruppen. Füge eine hinzu, um zusammengehörige Ebenen zu einem
einzigen Block zu bündeln.",
+ "Service-map layers": "Service-Map-Ebenen",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "Die Vorlagen dieser Ebenen
liefern eine Service-Map, daher werden ihre Würfel als Aufrufgraph dargestellt
(und legen den Grundstein für die ebenenübergreifende Hierarchie).
Schreibgeschützt — es ist eine Eigenschaft der Ebenen-Vorlage, keine
3D-map-Einstellung.",
+ "None reporting a service map.": "Keine meldet eine Service-Map.",
+ "Failover tier": "Failover-Stufe",
+ "The single catch-all: any layer no tier pins lands here.": "Die einzige
Auffangstufe: jede Ebene, die an keine Stufe angeheftet ist, landet hier.",
+ "Push 3D-map config to OAP": "3D-map-Konfiguration auf OAP veröffentlichen",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "Links = live auf OAP
(Remote). Rechts = dein lokaler Entwurf. Die Veröffentlichung ersetzt die
OAP-Kopie — die Karte zeigt sie beim nächsten Besuch.",
+ "Push to OAP": "Auf OAP veröffentlichen"
}
diff --git a/apps/ui/src/i18n/locales/en.json b/apps/ui/src/i18n/locales/en.json
index 236a926..1780696 100644
--- a/apps/ui/src/i18n/locales/en.json
+++ b/apps/ui/src/i18n/locales/en.json
@@ -1225,5 +1225,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— pick a currently-running pod, or check that on-demand pod
logs are enabled on OAP.",
"Select a service to begin.": "Select a service to begin.",
"Select a pod to list its containers.": "Select a pod to list its
containers.",
- "Select a container, then press Start to tail its logs.": "Select a
container, then press Start to tail its logs."
+ "Select a container, then press Start to tail its logs.": "Select a
container, then press Start to tail its logs.",
+ "Dashboard setup · 3D Infra Map": "Dashboard setup · 3D Infra Map",
+ "3D Infrastructure Map": "3D Infrastructure Map",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "Config for the {mapPath} view, published
to OAP. Levels control the vertical stack; per-layer color + metrics drive each
cube. Edits save to a {localDraft}; {checkDiffPush} publishes to OAP — the map
renders the remote, with bundle [...]
+ "local draft in this browser": "local draft in this browser",
+ "editing: {source}": "editing: {source}",
+ "Reset to": "Reset to",
+ "Bundled equals OAP-live — nothing to reset to.": "Bundled equals OAP-live —
nothing to reset to.",
+ "synced": "synced",
+ "OAP has no copy of this config yet.": "OAP has no copy of this config yet.",
+ "Saved local": "Saved local",
+ "Save local": "Save local",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "Couldn't load the 3D-map config — the BFF may be unreachable.
Refresh the page to retry.",
+ "Loading config…": "Loading config…",
+ "Global layer filter": "Global layer filter",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "The one top-level gate — a layer this JS regex excludes is off
the map (it still shows below in Unpinned, tagged \"filtered out\"). Default
{dotStar} admits all.",
+ "Tiers & layers (top → bottom)": "Tiers & layers (top → bottom)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "Every layer belongs to one tier — set it with the tier
dropdown; order = vertical stacking.",
+ "{count} in OAP": "{count} in OAP",
+ "{count} unpinned → fallback": "{count} unpinned → fallback",
+ "filter layers…": "filter layers…",
+ "+ add tier": "+ add tier",
+ "{count} layers": "{count} layers",
+ "mqe e.g. service_cpm": "mqe e.g. service_cpm",
+ "label": "label",
+ "unit": "unit",
+ "remove metric": "remove metric",
+ "+ metric": "+ metric",
+ "— unpinned —": "— unpinned —",
+ "Remove from this tier (moves to Unpinned)": "Remove from this tier (moves
to Unpinned)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "No
layers pinned — use a layer's tier dropdown to move one here.",
+ "Unpinned layers": "Unpinned layers",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "Pinned to no tier — they land on the failover tier
({failoverTier}). Pick a tier to pin one.",
+ "falls to {via}": "falls to {via}",
+ "filtered out": "filtered out",
+ "— pin to tier —": "— pin to tier —",
+ "Delete this layer from the config entirely": "Delete this layer from the
config entirely",
+ "No unpinned layers.": "No unpinned layers.",
+ "Logic groups": "Logic groups",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "Several layers
drawn as one labelled block on a tier (e.g. Self-Observability). Each member
keeps its own cube color.",
+ "+ add group": "+ add group",
+ "level": "level",
+ "icon": "icon",
+ "member layers ({count})": "member layers ({count})",
+ "remove from group": "remove from group",
+ "+ add layer…": "+ add layer…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "A group needs at least one layer — pushing will be rejected until you
add one.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"No logic groups. Add one to cluster related layers into a single block.",
+ "Service-map layers": "Service-map layers",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "These layers' templates
provide a service map, so their cubes render as a call graph (and seed the
cross-layer hierarchy). Read-only — it's a layer-template property, not a
3D-map setting.",
+ "None reporting a service map.": "None reporting a service map.",
+ "Failover tier": "Failover tier",
+ "The single catch-all: any layer no tier pins lands here.": "The single
catch-all: any layer no tier pins lands here.",
+ "Push 3D-map config to OAP": "Push 3D-map config to OAP",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "Left = live on OAP
(remote). Right = your local draft. Pushing replaces the OAP copy — the map
renders it on the next visit.",
+ "Push to OAP": "Push to OAP"
}
diff --git a/apps/ui/src/i18n/locales/es.json b/apps/ui/src/i18n/locales/es.json
index 304d877..4df1934 100644
--- a/apps/ui/src/i18n/locales/es.json
+++ b/apps/ui/src/i18n/locales/es.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— elige un Pod en ejecución, o verifica que los logs de Pod
bajo demanda estén habilitados en OAP.",
"Select a service to begin.": "Selecciona un servicio para empezar.",
"Select a pod to list its containers.": "Selecciona un Pod para listar sus
contenedores.",
- "Select a container, then press Start to tail its logs.": "Selecciona un
contenedor y pulsa Inicio para seguir sus logs en vivo."
+ "Select a container, then press Start to tail its logs.": "Selecciona un
contenedor y pulsa Inicio para seguir sus logs en vivo.",
+ "Dashboard setup · 3D Infra Map": "Configuración de paneles · 3D Infra Map",
+ "3D Infrastructure Map": "3D Infrastructure Map",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "Configuración de la vista {mapPath},
publicada en OAP. Los niveles controlan el apilado vertical; el color y las
métricas de cada capa definen cada cubo. Las ediciones se guardan en un
{localDraft}; {checkDiffPush} publica en OAP — [...]
+ "local draft in this browser": "borrador local en este navegador",
+ "editing: {source}": "editando: {source}",
+ "Reset to": "Restablecer a",
+ "Bundled equals OAP-live — nothing to reset to.": "Bundled coincide con
OAP-live — no hay nada a lo que restablecer.",
+ "synced": "sincronizado",
+ "OAP has no copy of this config yet.": "OAP aún no tiene ninguna copia de
esta configuración.",
+ "Saved local": "Guardado localmente",
+ "Save local": "Guardar localmente",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "No se pudo cargar la configuración del 3D-map — puede que el
BFF no esté disponible. Recarga la página para reintentar.",
+ "Loading config…": "Cargando configuración…",
+ "Global layer filter": "Filtro global de capas",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "El único filtro de nivel superior — una capa que este regex de
JS excluye queda fuera del mapa (aún aparece más abajo en No fijadas, marcada
como \"filtered out\"). El valor predeterminado {dotStar} admite todas.",
+ "Tiers & layers (top → bottom)": "Niveles y capas (arriba → abajo)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "Cada capa pertenece a un nivel — asígnalo con el menú
desplegable de nivel; el orden = apilado vertical.",
+ "{count} in OAP": "{count} en OAP",
+ "{count} unpinned → fallback": "{count} no fijadas → respaldo",
+ "filter layers…": "filtrar capas…",
+ "+ add tier": "+ agregar nivel",
+ "{count} layers": "{count} capas",
+ "mqe e.g. service_cpm": "mqe p. ej. service_cpm",
+ "label": "etiqueta",
+ "unit": "unidad",
+ "remove metric": "quitar métrica",
+ "+ metric": "+ métrica",
+ "— unpinned —": "— no fijadas —",
+ "Remove from this tier (moves to Unpinned)": "Quitar de este nivel (pasa a
No fijadas)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "No hay
capas fijadas — usa el menú desplegable de nivel de una capa para mover una
aquí.",
+ "Unpinned layers": "Capas no fijadas",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "Sin nivel asignado — caen en el nivel de respaldo
({failoverTier}). Elige un nivel para fijar una.",
+ "falls to {via}": "cae a {via}",
+ "filtered out": "filtered out",
+ "— pin to tier —": "— fijar a un nivel —",
+ "Delete this layer from the config entirely": "Eliminar esta capa por
completo de la configuración",
+ "No unpinned layers.": "No hay capas sin fijar.",
+ "Logic groups": "Grupos lógicos",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "Varias capas
dibujadas como un único bloque con etiqueta en un nivel (p. ej.
Self-Observability). Cada miembro conserva el color de su propio cubo.",
+ "+ add group": "+ agregar grupo",
+ "level": "nivel",
+ "icon": "icono",
+ "member layers ({count})": "capas miembro ({count})",
+ "remove from group": "quitar del grupo",
+ "+ add layer…": "+ agregar capa…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "Un grupo necesita al menos una capa — el push se rechazará hasta que
agregues una.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"No hay grupos lógicos. Agrega uno para agrupar capas relacionadas en un solo
bloque.",
+ "Service-map layers": "Capas con service map",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "Las plantillas de estas capas
proporcionan un service map, por lo que sus cubos se muestran como un call
graph (y siembran la jerarquía entre capas). Solo lectura — es una propiedad de
la plantilla de la capa, no un ajuste del 3D-map.",
+ "None reporting a service map.": "Ninguna reporta un service map.",
+ "Failover tier": "Nivel de respaldo",
+ "The single catch-all: any layer no tier pins lands here.": "El único nivel
comodín: cualquier capa que ningún nivel fije termina aquí.",
+ "Push 3D-map config to OAP": "Publicar la configuración del 3D-map en OAP",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "Izquierda = en vivo en OAP
(remote). Derecha = tu borrador local. El push reemplaza la copia en OAP — el
mapa la mostrará en la próxima visita.",
+ "Push to OAP": "Publicar en OAP"
}
diff --git a/apps/ui/src/i18n/locales/fr.json b/apps/ui/src/i18n/locales/fr.json
index 9d97b22..d0fd337 100644
--- a/apps/ui/src/i18n/locales/fr.json
+++ b/apps/ui/src/i18n/locales/fr.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— choisissez un Pod en cours d'exécution, ou vérifiez que
les logs de Pod à la demande sont activés sur OAP.",
"Select a service to begin.": "Sélectionnez un service pour commencer.",
"Select a pod to list its containers.": "Sélectionnez un Pod pour lister ses
conteneurs.",
- "Select a container, then press Start to tail its logs.": "Sélectionnez un
conteneur, puis appuyez sur Début pour suivre ses logs en direct."
+ "Select a container, then press Start to tail its logs.": "Sélectionnez un
conteneur, puis appuyez sur Début pour suivre ses logs en direct.",
+ "Dashboard setup · 3D Infra Map": "Configuration du tableau de bord · 3D
Infra Map",
+ "3D Infrastructure Map": "Carte d'infrastructure 3D",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "Configuration de la vue {mapPath},
publiée sur OAP. Les niveaux contrôlent l'empilement vertical ; la couleur et
les métriques propres à chaque couche pilotent chaque cube. Les modifications
sont enregistrées dans un {localDraft} ; [...]
+ "local draft in this browser": "brouillon local dans ce navigateur",
+ "editing: {source}": "édition : {source}",
+ "Reset to": "Réinitialiser à",
+ "Bundled equals OAP-live — nothing to reset to.": "Bundled est identique à
la version OAP en ligne — rien à réinitialiser.",
+ "synced": "synchronisé",
+ "OAP has no copy of this config yet.": "OAP n'a pas encore de copie de cette
configuration.",
+ "Saved local": "Enregistré en local",
+ "Save local": "Enregistrer en local",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "Impossible de charger la configuration de la 3D-map — le BFF
est peut-être inaccessible. Actualisez la page pour réessayer.",
+ "Loading config…": "Chargement de la configuration…",
+ "Global layer filter": "Filtre global des couches",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "L'unique filtre de premier niveau — une couche que ce regex JS
exclut disparaît de la carte (elle reste affichée plus bas dans les Non
épinglées, marquée « filtré »). La valeur par défaut {dotStar} admet tout.",
+ "Tiers & layers (top → bottom)": "Paliers et couches (haut → bas)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "Chaque couche appartient à un seul palier — définissez-le
via le menu déroulant du palier ; l'ordre correspond à l'empilement vertical.",
+ "{count} in OAP": "{count} dans OAP",
+ "{count} unpinned → fallback": "{count} non épinglées → repli",
+ "filter layers…": "filtrer les couches…",
+ "+ add tier": "+ ajouter un palier",
+ "{count} layers": "{count} couches",
+ "mqe e.g. service_cpm": "mqe, p. ex. service_cpm",
+ "label": "libellé",
+ "unit": "unité",
+ "remove metric": "supprimer la métrique",
+ "+ metric": "+ métrique",
+ "— unpinned —": "— non épinglées —",
+ "Remove from this tier (moves to Unpinned)": "Retirer de ce palier (déplace
vers Non épinglées)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "Aucune
couche épinglée — utilisez le menu déroulant de palier d'une couche pour en
déplacer une ici.",
+ "Unpinned layers": "Couches non épinglées",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "Épinglées à aucun palier — elles atterrissent sur le palier
de secours ({failoverTier}). Choisissez un palier pour en épingler une.",
+ "falls to {via}": "bascule vers {via}",
+ "filtered out": "filtré",
+ "— pin to tier —": "— épingler à un palier —",
+ "Delete this layer from the config entirely": "Supprimer entièrement cette
couche de la configuration",
+ "No unpinned layers.": "Aucune couche non épinglée.",
+ "Logic groups": "Groupes logiques",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "Plusieurs couches
dessinées comme un seul bloc étiqueté sur un palier (p. ex.
Self-Observability). Chaque membre conserve la couleur de son propre cube.",
+ "+ add group": "+ ajouter un groupe",
+ "level": "niveau",
+ "icon": "icône",
+ "member layers ({count})": "couches membres ({count})",
+ "remove from group": "retirer du groupe",
+ "+ add layer…": "+ ajouter une couche…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "Un groupe nécessite au moins une couche — la publication sera refusée
tant que vous n'en ajoutez pas une.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"Aucun groupe logique. Ajoutez-en un pour regrouper des couches apparentées en
un seul bloc.",
+ "Service-map layers": "Couches à carte de services",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "Les modèles de ces couches
fournissent une carte de services, leurs cubes s'affichent donc comme un graphe
d'appels (et amorcent la hiérarchie inter-couches). Lecture seule — c'est une
propriété du modèle de couche, pas un paramètre de la 3D-map.",
+ "None reporting a service map.": "Aucune ne signale de carte de services.",
+ "Failover tier": "Palier de secours",
+ "The single catch-all: any layer no tier pins lands here.": "Le palier
fourre-tout unique : toute couche qu'aucun palier n'épingle atterrit ici.",
+ "Push 3D-map config to OAP": "Publier la configuration de la 3D-map sur OAP",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "À gauche = en ligne sur OAP
(Remote). À droite = votre brouillon local. La publication remplace la copie
OAP — la carte l'affiche à la prochaine visite.",
+ "Push to OAP": "Publier sur OAP"
}
diff --git a/apps/ui/src/i18n/locales/ja.json b/apps/ui/src/i18n/locales/ja.json
index 54be75d..81511ff 100644
--- a/apps/ui/src/i18n/locales/ja.json
+++ b/apps/ui/src/i18n/locales/ja.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— 実行中の Pod を選択するか、オンデマンド Pod ログが OAP で有効になっているか確認してください。",
"Select a service to begin.": "サービスを選択して開始してください。",
"Select a pod to list its containers.": "Pod を選択してコンテナを一覧表示します。",
- "Select a container, then press Start to tail its logs.":
"コンテナを選択し、「開始」を押してログをライブで追尾します。"
+ "Select a container, then press Start to tail its logs.":
"コンテナを選択し、「開始」を押してログをライブで追尾します。",
+ "Dashboard setup · 3D Infra Map": "ダッシュボード設定 · 3D Infra Map",
+ "3D Infrastructure Map": "3D インフラマップ",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "{mapPath} ビューの設定で、OAP
に公開されます。レベルは垂直方向の積み重ねを制御し、レイヤーごとの色とメトリックが各キューブを表現します。編集内容は {localDraft}
に保存され、{checkDiffPush} で OAP に公開されます。マップはリモート版を表示し、バンドルのデフォルトがフォールバックとなります。",
+ "local draft in this browser": "このブラウザ内のローカル草稿",
+ "editing: {source}": "編集中: {source}",
+ "Reset to": "次にリセット",
+ "Bundled equals OAP-live — nothing to reset to.": "バンドル版が OAP の公開版と一致しています —
リセット先はありません。",
+ "synced": "同期済み",
+ "OAP has no copy of this config yet.": "OAP にはこの設定のコピーがまだありません。",
+ "Saved local": "ローカルに保存しました",
+ "Save local": "ローカルに保存",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "3D-map の設定を読み込めませんでした — BFF
に接続できない可能性があります。ページを再読み込みして再試行してください。",
+ "Loading config…": "設定を読み込んでいます…",
+ "Global layer filter": "グローバルレイヤーフィルター",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "唯一の最上位ゲートです — この JS regex が除外したレイヤーはマップから外れます(「filtered
out」のタグ付きで下の未ピン留め欄には表示されます)。デフォルトの {dotStar} はすべてを許可します。",
+ "Tiers & layers (top → bottom)": "ティアとレイヤー(上 → 下)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "各レイヤーはいずれか 1 つのティアに属します —
ティアのドロップダウンで設定します。順序は垂直方向の積み重ねを表します。",
+ "{count} in OAP": "OAP に {count} 件",
+ "{count} unpinned → fallback": "未ピン留め {count} 件 → フォールバック",
+ "filter layers…": "レイヤーを絞り込み…",
+ "+ add tier": "+ ティアを追加",
+ "{count} layers": "{count} レイヤー",
+ "mqe e.g. service_cpm": "mqe 例: service_cpm",
+ "label": "ラベル",
+ "unit": "単位",
+ "remove metric": "メトリックを削除",
+ "+ metric": "+ メトリック",
+ "— unpinned —": "— 未ピン留め —",
+ "Remove from this tier (moves to Unpinned)": "このティアから外す(未ピン留めに移動)",
+ "No layers pinned — use a layer's tier dropdown to move one here.":
"ピン留めされたレイヤーがありません — レイヤーのティアドロップダウンを使ってここに移動してください。",
+ "Unpinned layers": "未ピン留めのレイヤー",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.":
"どのティアにもピン留めされておらず、フェイルオーバーティア({failoverTier})に配置されます。ティアを選択してピン留めしてください。",
+ "falls to {via}": "{via} に落ちる",
+ "filtered out": "filtered out",
+ "— pin to tier —": "— ティアにピン留め —",
+ "Delete this layer from the config entirely": "このレイヤーを設定から完全に削除",
+ "No unpinned layers.": "未ピン留めのレイヤーはありません。",
+ "Logic groups": "ロジックグループ",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "複数のレイヤーを 1
つのラベル付きブロックとしてティア上に描画します(例: Self-Observability)。各メンバーはそれぞれのキューブの色を保持します。",
+ "+ add group": "+ グループを追加",
+ "level": "レベル",
+ "icon": "アイコン",
+ "member layers ({count})": "メンバーレイヤー({count})",
+ "remove from group": "グループから外す",
+ "+ add layer…": "+ レイヤーを追加…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "グループには少なくとも 1 つのレイヤーが必要です — 追加するまで公開は拒否されます。",
+ "No logic groups. Add one to cluster related layers into a single block.":
"ロジックグループがありません。追加すると、関連するレイヤーを 1 つのブロックにまとめられます。",
+ "Service-map layers": "サービスマップのレイヤー",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.":
"これらのレイヤーのテンプレートはサービスマップを提供するため、キューブはコールグラフとして描画されます(レイヤー横断の階層の起点にもなります)。読み取り専用です
— これは 3D-map の設定ではなく、レイヤーテンプレートのプロパティです。",
+ "None reporting a service map.": "サービスマップを報告しているものはありません。",
+ "Failover tier": "フェイルオーバーティア",
+ "The single catch-all: any layer no tier pins lands here.":
"唯一の受け皿です。どのティアにもピン留めされていないレイヤーはここに配置されます。",
+ "Push 3D-map config to OAP": "3D-map 設定を OAP に公開",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "左 = OAP 上の公開版(リモート)。右 =
あなたのローカル草稿。公開すると OAP 上のコピーが置き換えられ、次回アクセス時にマップへ反映されます。",
+ "Push to OAP": "OAP に公開"
}
diff --git a/apps/ui/src/i18n/locales/ko.json b/apps/ui/src/i18n/locales/ko.json
index d549051..4b1f50b 100644
--- a/apps/ui/src/i18n/locales/ko.json
+++ b/apps/ui/src/i18n/locales/ko.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— 실행 중인 Pod를 선택하거나, OAP에서 온디맨드 Pod 로그가 활성화되어 있는지 확인하세요.",
"Select a service to begin.": "시작하려면 서비스를 선택하세요.",
"Select a pod to list its containers.": "컨테이너를 나열하려면 Pod를 선택하세요.",
- "Select a container, then press Start to tail its logs.": "컨테이너를 선택한 다음 시작을
눌러 로그를 실시간으로 확인하세요."
+ "Select a container, then press Start to tail its logs.": "컨테이너를 선택한 다음 시작을
눌러 로그를 실시간으로 확인하세요.",
+ "Dashboard setup · 3D Infra Map": "대시보드 설정 · 3D Infra Map",
+ "3D Infrastructure Map": "3D 인프라 맵",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "OAP에 게시되는 {mapPath} 화면의 설정입니다. 레벨은 수직 적층
순서를 정하고, 각 큐브는 레이어별 색상과 메트릭으로 표현됩니다. 편집 내용은 {localDraft}에 저장되며,
{checkDiffPush}로 OAP에 게시합니다 — 맵은 원격 버전을 렌더링하고 번들 기본값을 폴백으로 사용합니다.",
+ "local draft in this browser": "이 브라우저의 로컬 초안",
+ "editing: {source}": "편집 중: {source}",
+ "Reset to": "재설정 대상",
+ "Bundled equals OAP-live — nothing to reset to.": "번들과 OAP 라이브가 동일 — 재설정할
대상이 없습니다.",
+ "synced": "동기화됨",
+ "OAP has no copy of this config yet.": "OAP에 아직 이 설정의 복사본이 없습니다.",
+ "Saved local": "로컬에 저장됨",
+ "Save local": "로컬에 저장",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "3D-map 설정을 불러오지 못했습니다 — BFF에 연결할 수 없을 수 있습니다. 페이지를 새로 고쳐 다시
시도하세요.",
+ "Loading config…": "설정을 불러오는 중…",
+ "Global layer filter": "전역 레이어 필터",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "최상위 단일 관문입니다 — 이 JS regex가 제외하는 레이어는 맵에서 빠집니다(아래 미고정 목록에는
\"filtered out\"으로 표시되어 그대로 남습니다). 기본값 {dotStar}은 모두 허용합니다.",
+ "Tiers & layers (top → bottom)": "티어 및 레이어 (위 → 아래)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "모든 레이어는 하나의 티어에 속합니다 — 티어 드롭다운으로 지정하며, 순서가 곧 수직 적층
순서입니다.",
+ "{count} in OAP": "OAP에 {count}개",
+ "{count} unpinned → fallback": "미고정 {count}개 → 폴백",
+ "filter layers…": "레이어 필터…",
+ "+ add tier": "+ 티어 추가",
+ "{count} layers": "레이어 {count}개",
+ "mqe e.g. service_cpm": "mqe 예: service_cpm",
+ "label": "라벨",
+ "unit": "단위",
+ "remove metric": "메트릭 제거",
+ "+ metric": "+ 메트릭",
+ "— unpinned —": "— 미고정 —",
+ "Remove from this tier (moves to Unpinned)": "이 티어에서 제거 (미고정으로 이동)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "고정된
레이어가 없습니다 — 레이어의 티어 드롭다운으로 여기로 옮기세요.",
+ "Unpinned layers": "미고정 레이어",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "어느 티어에도 고정되지 않아 폴백 티어({failoverTier})에 배치됩니다. 고정하려면 티어를
선택하세요.",
+ "falls to {via}": "{via}(으)로 폴백",
+ "filtered out": "필터로 제외됨",
+ "— pin to tier —": "— 티어에 고정 —",
+ "Delete this layer from the config entirely": "이 레이어를 설정에서 완전히 삭제",
+ "No unpinned layers.": "미고정 레이어가 없습니다.",
+ "Logic groups": "로직 그룹",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "여러 레이어를 티어 위에 라벨이
붙은 하나의 블록으로 그립니다(예: Self-Observability). 각 멤버는 자신의 큐브 색상을 그대로 유지합니다.",
+ "+ add group": "+ 그룹 추가",
+ "level": "레벨",
+ "icon": "아이콘",
+ "member layers ({count})": "멤버 레이어 ({count}개)",
+ "remove from group": "그룹에서 제거",
+ "+ add layer…": "+ 레이어 추가…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "그룹에는 레이어가 최소 하나 필요합니다 — 하나를 추가하기 전까지 게시가 거부됩니다.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"로직 그룹이 없습니다. 관련 레이어를 하나의 블록으로 묶으려면 추가하세요.",
+ "Service-map layers": "서비스 맵 레이어",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "이 레이어들의 템플릿은 서비스 맵을 제공하므로, 해당
큐브는 호출 그래프로 렌더링되고 레이어 간 계층 구조의 기반이 됩니다. 읽기 전용입니다 — 이는 레이어 템플릿의 속성이며 3D-map 설정이
아닙니다.",
+ "None reporting a service map.": "서비스 맵을 보고하는 레이어가 없습니다.",
+ "Failover tier": "폴백 티어",
+ "The single catch-all: any layer no tier pins lands here.": "단일 포괄 티어입니다: 어느
티어에도 고정되지 않은 레이어가 모두 여기에 배치됩니다.",
+ "Push 3D-map config to OAP": "3D-map 설정을 OAP에 게시",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "왼쪽 = OAP 라이브(원격). 오른쪽 = 내
로컬 초안. 게시하면 OAP 복사본을 교체하며, 다음 방문 시 맵이 이를 렌더링합니다.",
+ "Push to OAP": "OAP에 게시"
}
diff --git a/apps/ui/src/i18n/locales/pt.json b/apps/ui/src/i18n/locales/pt.json
index 007a655..7228384 100644
--- a/apps/ui/src/i18n/locales/pt.json
+++ b/apps/ui/src/i18n/locales/pt.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— escolha um Pod em execução, ou verifique se os logs de Pod
sob demanda estão habilitados no OAP.",
"Select a service to begin.": "Selecione um serviço para começar.",
"Select a pod to list its containers.": "Selecione um Pod para listar seus
contêineres.",
- "Select a container, then press Start to tail its logs.": "Selecione um
contêiner e pressione Início para acompanhar seus logs ao vivo."
+ "Select a container, then press Start to tail its logs.": "Selecione um
contêiner e pressione Início para acompanhar seus logs ao vivo.",
+ "Dashboard setup · 3D Infra Map": "Configuração de painéis · 3D Infra Map",
+ "3D Infrastructure Map": "3D Infrastructure Map",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "Configuração da visão {mapPath},
publicada no OAP. Os níveis controlam o empilhamento vertical; a cor e as
métricas de cada camada definem cada cubo. As edições são salvas em um
{localDraft}; {checkDiffPush} publica no OAP — o mapa [...]
+ "local draft in this browser": "rascunho local neste navegador",
+ "editing: {source}": "editando: {source}",
+ "Reset to": "Redefinir para",
+ "Bundled equals OAP-live — nothing to reset to.": "Bundled coincide com a
versão em produção do OAP — nada para redefinir.",
+ "synced": "sincronizado",
+ "OAP has no copy of this config yet.": "O OAP ainda não tem uma cópia desta
configuração.",
+ "Saved local": "Salvo localmente",
+ "Save local": "Salvar localmente",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "Não foi possível carregar a configuração do 3D-map — o BFF
pode estar indisponível. Atualize a página para tentar de novo.",
+ "Loading config…": "Carregando a configuração…",
+ "Global layer filter": "Filtro global de camadas",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "O único filtro de nível superior — uma camada excluída por este
regex JS fica fora do mapa (ainda aparece abaixo em Não fixadas, marcada como
\"filtered out\"). O padrão {dotStar} admite todas.",
+ "Tiers & layers (top → bottom)": "Níveis e camadas (topo → base)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "Cada camada pertence a um nível — defina-o pela lista
suspensa de nível; a ordem = empilhamento vertical.",
+ "{count} in OAP": "{count} no OAP",
+ "{count} unpinned → fallback": "{count} não fixadas → fallback",
+ "filter layers…": "filtrar camadas…",
+ "+ add tier": "+ adicionar nível",
+ "{count} layers": "{count} camadas",
+ "mqe e.g. service_cpm": "mqe ex.: service_cpm",
+ "label": "rótulo",
+ "unit": "unidade",
+ "remove metric": "remover métrica",
+ "+ metric": "+ métrica",
+ "— unpinned —": "— não fixadas —",
+ "Remove from this tier (moves to Unpinned)": "Remover deste nível (move para
Não fixadas)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "Nenhuma
camada fixada — use a lista de nível de uma camada para mover uma para cá.",
+ "Unpinned layers": "Camadas não fixadas",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "Sem nível fixado — caem no nível de failover
({failoverTier}). Escolha um nível para fixar uma delas.",
+ "falls to {via}": "cai para {via}",
+ "filtered out": "filtered out",
+ "— pin to tier —": "— fixar em nível —",
+ "Delete this layer from the config entirely": "Excluir esta camada
completamente da configuração",
+ "No unpinned layers.": "Nenhuma camada não fixada.",
+ "Logic groups": "Grupos lógicos",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "Várias camadas
desenhadas como um único bloco rotulado em um nível (p.ex. Self-Observability).
Cada membro mantém a cor do próprio cubo.",
+ "+ add group": "+ adicionar grupo",
+ "level": "nível",
+ "icon": "ícone",
+ "member layers ({count})": "camadas membro ({count})",
+ "remove from group": "remover do grupo",
+ "+ add layer…": "+ adicionar camada…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "Um grupo precisa de pelo menos uma camada — a publicação será rejeitada
até você adicionar uma.",
+ "No logic groups. Add one to cluster related layers into a single block.":
"Nenhum grupo lógico. Adicione um para agrupar camadas relacionadas em um único
bloco.",
+ "Service-map layers": "Camadas com mapa de serviços",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "Os templates destas camadas
fornecem um mapa de serviços, então seus cubos são renderizados como um grafo
de chamadas (e alimentam a hierarquia entre camadas). Somente leitura — é uma
propriedade do template da camada, não uma configuração do 3D-map.",
+ "None reporting a service map.": "Nenhuma reportando um mapa de serviços.",
+ "Failover tier": "Nível de failover",
+ "The single catch-all: any layer no tier pins lands here.": "O único nível
coringa: toda camada que nenhum nível fixa cai aqui.",
+ "Push 3D-map config to OAP": "Publicar a configuração do 3D-map no OAP",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "Esquerda = em produção no
OAP (remote). Direita = seu rascunho local. Publicar substitui a cópia do OAP —
o mapa passa a renderizá-la na próxima visita.",
+ "Push to OAP": "Publicar no OAP"
}
diff --git a/apps/ui/src/i18n/locales/zh-CN.json
b/apps/ui/src/i18n/locales/zh-CN.json
index 883551a..9ba66c1 100644
--- a/apps/ui/src/i18n/locales/zh-CN.json
+++ b/apps/ui/src/i18n/locales/zh-CN.json
@@ -1214,5 +1214,60 @@
"— pick a currently-running pod, or check that on-demand pod logs are
enabled on OAP.": "— 请选择一个正在运行的 Pod,或确认 OAP 已启用按需 Pod 日志。",
"Select a service to begin.": "请选择服务以开始。",
"Select a pod to list its containers.": "选择一个 Pod 以列出其容器。",
- "Select a container, then press Start to tail its logs.":
"选择一个容器,然后点击“开始”以实时查看其日志。"
+ "Select a container, then press Start to tail its logs.":
"选择一个容器,然后点击“开始”以实时查看其日志。",
+ "Dashboard setup · 3D Infra Map": "仪表板设置 · 3D Infra Map",
+ "3D Infrastructure Map": "3D 基础设施地图",
+ "Config for the {mapPath} view, published to OAP. Levels control the
vertical stack; per-layer color + metrics drive each cube. Edits save to a
{localDraft}; {checkDiffPush} publishes to OAP — the map renders the remote,
with bundled defaults as fallback.": "{mapPath} 视图的配置,发布到
OAP。层级(level)控制垂直堆叠;每个 layer
的颜色与指标决定对应方块的呈现。修改会保存到{localDraft};{checkDiffPush}将其发布到 OAP ——
地图渲染的是远端版本,内置默认值作为兜底。",
+ "local draft in this browser": "当前浏览器中的本地草稿",
+ "editing: {source}": "正在编辑:{source}",
+ "Reset to": "重置为",
+ "Bundled equals OAP-live — nothing to reset to.": "内置版与 OAP 实时版本一致 —— 无需重置。",
+ "synced": "已同步",
+ "OAP has no copy of this config yet.": "OAP 还没有此配置的副本。",
+ "Saved local": "已保存到本地",
+ "Save local": "保存到本地",
+ "Couldn't load the 3D-map config — the BFF may be unreachable. Refresh the
page to retry.": "无法加载 3D-map 配置 —— BFF 可能不可达。请刷新页面重试。",
+ "Loading config…": "正在加载配置…",
+ "Global layer filter": "全局 layer 过滤",
+ "The one top-level gate — a layer this JS regex excludes is off the map (it
still shows below in Unpinned, tagged \"filtered out\"). Default {dotStar}
admits all.": "唯一的顶级开关 —— 被这段 JS regex 排除的 layer
不会出现在地图上(它仍会显示在下方的“未固定”中,并标记为“filtered out”)。默认 {dotStar} 放行所有 layer。",
+ "Tiers & layers (top → bottom)": "层组与 layer(从上到下)",
+ "Every layer belongs to one tier — set it with the tier dropdown; order =
vertical stacking.": "每个 layer 都归属于一个层组 —— 通过层组下拉框设置;顺序即垂直堆叠次序。",
+ "{count} in OAP": "OAP 中 {count} 个",
+ "{count} unpinned → fallback": "{count} 个未固定 → 兜底",
+ "filter layers…": "过滤 layer…",
+ "+ add tier": "+ 添加层组",
+ "{count} layers": "{count} 个 layer",
+ "mqe e.g. service_cpm": "mqe,例如 service_cpm",
+ "label": "标签",
+ "unit": "单位",
+ "remove metric": "移除指标",
+ "+ metric": "+ 指标",
+ "— unpinned —": "— 未固定 —",
+ "Remove from this tier (moves to Unpinned)": "从该层组移除(移至“未固定”)",
+ "No layers pinned — use a layer's tier dropdown to move one here.": "尚无固定的
layer —— 使用某个 layer 的层组下拉框将其移到这里。",
+ "Unpinned layers": "未固定的 layer",
+ "Pinned to no tier — they land on the failover tier ({failoverTier}). Pick a
tier to pin one.": "未固定到任何层组 —— 它们会落到兜底层组({failoverTier})。选择一个层组即可固定。",
+ "falls to {via}": "落到 {via}",
+ "filtered out": "已过滤",
+ "— pin to tier —": "— 固定到层组 —",
+ "Delete this layer from the config entirely": "将该 layer 从配置中彻底删除",
+ "No unpinned layers.": "没有未固定的 layer。",
+ "Logic groups": "逻辑分组",
+ "Several layers drawn as one labelled block on a tier (e.g.
Self-Observability). Each member keeps its own cube color.": "将多个 layer
在某个层组上绘制为一个带标签的整块(例如 Self-Observability)。每个成员仍保留各自方块的颜色。",
+ "+ add group": "+ 添加分组",
+ "level": "层级",
+ "icon": "图标",
+ "member layers ({count})": "成员 layer({count})",
+ "remove from group": "从分组移除",
+ "+ add layer…": "+ 添加 layer…",
+ "A group needs at least one layer — pushing will be rejected until you add
one.": "分组至少需要一个 layer —— 添加之前推送会被拒绝。",
+ "No logic groups. Add one to cluster related layers into a single block.":
"暂无逻辑分组。添加一个,即可将相关 layer 聚合为同一整块。",
+ "Service-map layers": "服务地图 layer",
+ "These layers' templates provide a service map, so their cubes render as a
call graph (and seed the cross-layer hierarchy). Read-only — it's a
layer-template property, not a 3D-map setting.": "这些 layer
的模板提供服务地图,因此其方块会渲染为调用关系图(并作为跨 layer 层级关系的起点)。只读 —— 这是 layer 模板的属性,而非 3D-map
的设置。",
+ "None reporting a service map.": "没有 layer 上报服务地图。",
+ "Failover tier": "兜底层组",
+ "The single catch-all: any layer no tier pins lands here.":
"唯一的兜底层组:任何未被固定到具体层组的 layer 都会落到这里。",
+ "Push 3D-map config to OAP": "将 3D-map 配置推送至 OAP",
+ "Left = live on OAP (remote). Right = your local draft. Pushing replaces the
OAP copy — the map renders it on the next visit.": "左侧 = OAP 上的线上版本(远端)。右侧 =
你的本地草稿。推送会替换 OAP 上的副本 —— 下次访问时地图便以其渲染。",
+ "Push to OAP": "推送至 OAP"
}