diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx index 1932ef4..cf7d9b9 100644 --- a/frontend/src/components/ui/dialog.tsx +++ b/frontend/src/components/ui/dialog.tsx @@ -1,3 +1,4 @@ +import i18n from '@/i18n' import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { XIcon } from "lucide-react" @@ -70,7 +71,7 @@ function DialogContent({ className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" > - Close + {i18n.t('ui.close', { ns: 'common' })} )} diff --git a/frontend/src/components/ui/pagination.tsx b/frontend/src/components/ui/pagination.tsx index 1dcfb0c..713d3d8 100644 --- a/frontend/src/components/ui/pagination.tsx +++ b/frontend/src/components/ui/pagination.tsx @@ -1,3 +1,4 @@ +import i18n from '@/i18n' import * as React from "react" import { ChevronLeftIcon, @@ -71,13 +72,13 @@ function PaginationPrevious({ }: React.ComponentProps) { return ( - Previous + {i18n.t('ui.prevPage', { ns: 'common' })} ) } @@ -88,12 +89,12 @@ function PaginationNext({ }: React.ComponentProps) { return ( - Next + {i18n.t('ui.nextPage', { ns: 'common' })} ) @@ -111,7 +112,7 @@ function PaginationEllipsis({ {...props} > - More pages + {i18n.t('ui.morePages', { ns: 'common' })} ) } diff --git a/frontend/src/components/ui/sheet.tsx b/frontend/src/components/ui/sheet.tsx index 6906f5b..6643740 100644 --- a/frontend/src/components/ui/sheet.tsx +++ b/frontend/src/components/ui/sheet.tsx @@ -1,3 +1,4 @@ +import i18n from '@/i18n' import * as React from "react" import * as SheetPrimitive from "@radix-ui/react-dialog" import { XIcon } from "lucide-react" @@ -72,7 +73,7 @@ function SheetContent({ {children} - Close + {i18n.t('ui.close', { ns: 'common' })} diff --git a/frontend/src/i18n/locales/en/alerts.ts b/frontend/src/i18n/locales/en/alerts.ts index a086d8a..3bc1ff8 100644 --- a/frontend/src/i18n/locales/en/alerts.ts +++ b/frontend/src/i18n/locales/en/alerts.ts @@ -69,7 +69,7 @@ export default { sourcePlaceholder: 'Select a data source', query: 'LogQL query *', queryHint: - 'Common: {app="xxx"} |= "FATAL" / {namespace=~".+"} |~ "(?i)error" / {app="x"} |= "OutOfMemoryError"', + 'Common: {app="xxx"} |= "FATAL"', channels: 'Channels (multi-select to fan out to multiple alert groups)', noChannels: 'Create channels first under "Settings → Alert Notifications"', channelDisabled: '(disabled)', diff --git a/frontend/src/i18n/locales/en/analysis.ts b/frontend/src/i18n/locales/en/analysis.ts index b6edaa4..ca86865 100644 --- a/frontend/src/i18n/locales/en/analysis.ts +++ b/frontend/src/i18n/locales/en/analysis.ts @@ -64,4 +64,13 @@ export default { untagged: '(untagged)', empty: 'No app-dimension data', }, -} \ No newline at end of file + ingress: { + title: 'Ingress access analysis', + noIngress: 'No ingress-nginx / ingress namespace access logs found on this source', + empty: 'No parseable ingress access logs in this time range', + loading: 'Loading ingress access logs…', + notDetected: 'No ingress-nginx detected', + toggleAria: 'Expand / collapse Ingress access analysis', + loadFailed: 'Failed to load ingress access logs', + }, +} diff --git a/frontend/src/i18n/locales/en/clusters.ts b/frontend/src/i18n/locales/en/clusters.ts index 3b73061..08d36b2 100644 --- a/frontend/src/i18n/locales/en/clusters.ts +++ b/frontend/src/i18n/locales/en/clusters.ts @@ -23,7 +23,7 @@ export default { }, delete: { warning: - 'Delete "{{name}}"? Warning: {{dep}} deployments and {{col}} collector policies still reference this cluster; they will stop working once it is deleted.', + 'Delete "{{name}}"? {{dep}} deployment(s) and {{col}} collector policy(ies) still reference it.', }, toast: { deleteFailed: 'Failed to delete cluster', @@ -52,13 +52,13 @@ export default { rbacScope: 'Grant permissions only in the log namespace so a leaked Token cannot affect other namespaces; do not use cluster-admin.', rbacScriptIntro: - 'Creates the log namespace, ServiceAccount, namespace-scoped Role and a long-lived Token in one shot (run it once in the cluster, then paste the Token into the field below):', + 'Run once in the cluster to create the namespace / SA / Role / long-lived Token:', rbacScriptComment1: '# 1) Create in one shot: namespace + SA + namespace-scoped Role + long-lived Token', rbacScriptComment2: '# 2) Extract the long-lived Token (it does not expire) and paste it into the Token field', rbacNamespaceNote: - 'The Namespace above is created by an administrator; the platform Token itself has no cluster-wide permissions. Only grant the namespaces: create/get ClusterRole if the platform really must create its own namespaces — note that namespace creation cannot be restricted by name, so pre-creating log is safer.', + 'The token has no cluster-level rights; grant namespaces: create/get only if the platform must create its own namespace.', rbacTokenNote: - 'The Token is long-lived but bound to a Role in the log namespace only (it can only touch Pods and workloads there), so a leak is limited to log. The server converts it to a kubeconfig and stores it encrypted with ENCRYPTION_KEY; the database keeps only ENC: ciphertext and the API never returns the plaintext. To rotate it, delete and recreate the Secret — the old Token stops working immediately.', + 'Token is scoped to the log namespace with small blast radius; stored encrypted server-side. Rotate by deleting the Secret and recreating.', contextLabel: 'Context (optional)', contextPlaceholder: 'Leave blank to use the default context', submitAdd: 'Add Cluster', diff --git a/frontend/src/i18n/locales/en/collectors.ts b/frontend/src/i18n/locales/en/collectors.ts index d69654a..d6cf312 100644 --- a/frontend/src/i18n/locales/en/collectors.ts +++ b/frontend/src/i18n/locales/en/collectors.ts @@ -77,7 +77,7 @@ const collectors = { logPath: 'Log Path', logPathHostHint: 'Supports glob wildcards (e.g. /var/log/*.log /var/log/syslog); separate multiple paths with commas', logPathSidecarHint: - 'Enter the file path your app writes to, e.g. /var/log/app/*.log. Existing log volumes are shared automatically; without one, a shared volume is created only in the dedicated logs directory. Regular paths such as /tmp/test.log are not mounted automatically.', + 'Paths the app writes to, e.g. /var/log/app/*.log; only existing log volumes or the dedicated logs directory are shared.', labels: 'Custom Labels (JSON, optional)', labelsHint: 'When valid JSON, attached as labels to every log line for filtering in Loki/Grafana; leave empty to skip', submit: 'Create & Deploy', @@ -103,11 +103,11 @@ const collectors = { nsNoMatch: 'No matches', nsLoadFailed: 'Failed to load namespaces', nsHint: - 'Check "All namespaces" to collect cluster-wide; otherwise only the selected namespaces are collected. Generates the kubernetes_sd_configs.namespaces allowlist for Promtail.', + '"All namespaces" collects cluster-wide; otherwise only the checked ones.', appLabelSource: 'app label source', appSourcePlaceholder: 'Empty = default (controller-name fallback chain)', appSourceHint: - 'By default falls back through app.kubernetes.io/name → app → controller name → pod name. If your cluster has its own convention label (e.g. k8s.kuboard.cn/name), enter it here and it takes the highest priority; the rest still serve as fallback.', + 'Falls back through app.kubernetes.io/name → app → controller name → pod name; a cluster-specific label (e.g. k8s.kuboard.cn/name) set here takes priority.', appSourcePreset: { default: 'Default: app.kubernetes.io/name → app → controller name → pod name', controllerName: 'controller_name (ReplicaSet name of the Pod, hash stripped = Deployment name)', @@ -118,8 +118,8 @@ const collectors = { appInclude: 'Only collect these apps (optional)', appIncludePlaceholder: 'Empty = no limit; comma-separated, e.g. cex-spot-api, cex-asset-api', appIncludeHint: - 'When set, Promtail keeps only logs whose app label matches these values (action: keep) — the most direct way to control log volume. Values must match the app resolved by the "app label source" above.', - logPathHint: 'The Sidecar requires the app container\'s shared log volume or the dedicated logs directory. Click "Deploy" after saving to apply the new configuration.', + 'Keeps only logs whose app matches these values (action: keep) — the most direct way to cap log volume.', + logPathHint: 'Sidecar needs a shared log volume or the dedicated logs directory; click Deploy after saving.', save: 'Save', saving: 'Saving…', validation: { diff --git a/frontend/src/i18n/locales/en/common.ts b/frontend/src/i18n/locales/en/common.ts index 226e40f..6f3f7d7 100644 --- a/frontend/src/i18n/locales/en/common.ts +++ b/frontend/src/i18n/locales/en/common.ts @@ -106,6 +106,14 @@ export default { apply: 'Apply', recent: 'Recently used', }, + ui: { + close: 'Close', + prevPage: 'Previous', + nextPage: 'Next', + goPrevPage: 'Go to previous page', + goNextPage: 'Go to next page', + morePages: 'More pages', + }, password: { hide: 'Hide password', show: 'Show password', diff --git a/frontend/src/i18n/locales/en/deployments.ts b/frontend/src/i18n/locales/en/deployments.ts index d46cffc..3e058f8 100644 --- a/frontend/src/i18n/locales/en/deployments.ts +++ b/frontend/src/i18n/locales/en/deployments.ts @@ -10,7 +10,7 @@ export default { deleteDockerDesc: 'Delete "{{name}}"? The Loki container on the host will be removed as well. This cannot be undone.', deleteRecordDesc: - 'Delete "{{name}}"? Deleting the record does not uninstall the Loki resources already deployed on the cluster/host. This cannot be undone.', + 'Deleting "{{name}}" does not uninstall deployed Loki resources; this cannot be undone.', errLoadRefs: 'Failed to load cluster/host/storage data', errDeploy: 'Deployment failed', errStop: 'Failed to stop', @@ -36,7 +36,7 @@ export default { ready: 'Loki ready', notReady: 'Loki not ready', runningHint: - 'Tip: link Loki under "Log Query → Data Sources" to start querying; for cross-cluster/public access, configure an Ingress domain for Loki first.', + 'Link the Loki source under "Log Query → Data Sources" to query logs.', configure: 'Configure', deploy: 'Deploy', deploying: 'Deploying...', @@ -103,7 +103,7 @@ export default { loading: 'Loading…', templateNote: 'Generated by the deployment template', yamlLabel: 'Retention Config YAML', - yamlHint: 'Edit limits_config.retention_period, e.g. 90d or 2160h. Only this setting is accepted here.', + yamlHint: 'Edit retention_period, e.g. 90d / 2160h; only this setting is accepted.', applyHintRunning: 'Save only updates the deployment record; Save & Apply redeploys Loki and queries may be briefly interrupted.', applyHintIdle: 'Takes effect on the next deployment.', diff --git a/frontend/src/i18n/locales/en/hosts.ts b/frontend/src/i18n/locales/en/hosts.ts index 41d231c..9969633 100644 --- a/frontend/src/i18n/locales/en/hosts.ts +++ b/frontend/src/i18n/locales/en/hosts.ts @@ -24,7 +24,7 @@ export default { }, delete: { warning: - 'Delete "{{name}}"? Warning: {{dep}} deployments and {{col}} collector policies still reference this host; they will stop working once it is deleted.', + 'Delete "{{name}}"? {{dep}} deployment(s) and {{col}} collector policy(ies) still reference it.', }, toast: { deleteFailed: 'Failed to delete host', diff --git a/frontend/src/i18n/locales/en/logs.ts b/frontend/src/i18n/locales/en/logs.ts index 06aed87..9f3352e 100644 --- a/frontend/src/i18n/locales/en/logs.ts +++ b/frontend/src/i18n/locales/en/logs.ts @@ -8,7 +8,7 @@ export default { query: 'Loki Query', access: 'Query Access', sources: 'Data Sources', - descQuery: 'Search by log stream and time range; drag the timeline to narrow the range, and click a log line to view its context.', + descQuery: 'Search by stream selector and time range; drag the timeline or open line context.', descAccess: 'Reuse log scopes with policy groups and attach multiple users at once.', descSources: 'Configure Loki connections and authentication.', }, @@ -20,7 +20,7 @@ export default { sourceAria: 'Data source', sourceEmptyOption: 'No data sources available', limitLabel: 'Per page', - limitHint: 'Up to 1000 entries per page; paging keeps only the current page. For wide ranges, check the timeline first, then drag to select a peak window.', + limitHint: 'Up to 1000 entries per page; paging keeps only the current page.', rangeInvalid: 'Select a valid start and end time', queryFailed: 'Query failed', }, @@ -28,14 +28,28 @@ export default { loadFailed: 'Failed to load data sources', missingTitle: 'No Loki data source available', missingBody: - 'Link a Loki data source first under "Data Sources": for in-cluster Loki, pick "Cluster Service" (Namespace / Service / port, no Ingress needed); for cross-cluster or public queries, expose Loki via an Ingress domain first, then add it with "Direct URL". Queries are unavailable until a data source is linked.', + 'Link a Loki data source first under "Data Sources": use "Cluster Service" in-cluster, or "Direct URL" via an Ingress domain.', goConfigure: 'Go to data sources', sectionAria: 'Data source management', heading: 'Loki Data Sources', - hint: 'Tip: when Loki runs in the cluster, add a "Cluster Service" data source directly (no Ingress needed); for cross-cluster / public queries, expose Loki via an Ingress domain first, then link it with "Direct URL". Without a linked data source, the query page cannot be used.', + hint: 'In-cluster Loki works with "Cluster Service"; cross-cluster / public access needs an Ingress domain plus "Direct URL".', managedCluster: 'Cluster {{name}}', managedBadge: 'Platform deployed · managed with the deployment', - add: '+ Add manually', + countLabel: '{{count}} total', + create: 'New source', + createTitle: 'New data source', + editTitle: 'Edit data source', + edit: 'Edit', + cancel: 'Cancel', + testing: 'Testing…', + deleteTitle: 'Delete data source', + deleteDesc: 'Deleting "{{name}}" breaks queries and access policies based on it. Delete it?', + deleteAria: 'Delete {{name}}', + defaultBadge: 'Default', + emptyTitle: 'No data sources yet', + emptyDesc: 'Add a "Cluster Service" or "Direct URL" source, then query logs on the Loki Query tab.', + typeLabel: 'Type', + namePlaceholder: 'prod-loki', nameLabel: 'Name', targetUrl: 'Direct URL', targetClusterSvc: 'Cluster Service', @@ -62,17 +76,6 @@ export default { saveFailed: 'Failed to save', remove: 'Delete', }, - range: { - lastMinutes: 'Last {{count}} minutes', - lastHours: 'Last {{count}} hours', - lastHours_one: 'Last {{count}} hour', - custom: 'Custom time', - title: 'Select time range', - start: 'Start time', - end: 'End time', - apply: 'Apply', - recent: 'Recently used', - }, editor: { aria: 'LogQL query', valuesOf: 'Values of {{label}}', @@ -85,7 +88,7 @@ export default { templates: { aria: 'Quick query templates', title: 'Quick LogQL builder', - hint: 'Pick a namespace, then an app, then a pod; app candidates come only from the selected namespace.', + hint: 'Pick a namespace to narrow down; selecting an app / pod runs the query.', namespace: 'Namespace', app: 'App', appWithKey: 'App ({{key}})', @@ -95,12 +98,10 @@ export default { pickNsFirst: 'Pick a namespace first to narrow down', noPod: 'No pods under this app', optional: 'Optional', - scopeLogs: 'Query this scope', errorLogs: 'Errors (error)', warnLogs: 'Warnings (warn)', - podLogs: 'Specific pod logs', ingressLogs: 'Ingress access logs', - scopeHint: 'Pick a namespace, then an app or pod — or choose values in the Label browser', + scopeHint: 'Selecting an app / pod runs the query — or pick values in the Label browser', }, labels: { browser: 'Label Browser', @@ -115,11 +116,11 @@ export default { loading: 'Loading…', noValues: 'No matching values', useAsValue: 'Use "{{value}}" as value', - hint: 'Select namespace first to query all replicas in that Namespace; then app / service_name to narrow to a service (workloads without an app label can rely on namespace alone). Candidates come from logs written to Loki within the current time range; the authorized scope is still enforced server-side.', + hint: 'Pick namespace, then app / service_name to narrow down; candidates come from logs in the current range.', }, volume: { statsLoading: 'Counting logs across the whole time range; you can keep browsing the log page.', - statsError: 'Log volume statistics timed out or failed; the log page still works. Narrow the time range and query again.', + statsError: 'Volume stats failed; narrow the range and retry.', histogramLoading: 'Counting…', histogramEmpty: 'No log volume data', timelineAria: 'Log volume timeline', @@ -136,16 +137,10 @@ export default { summary_one: 'Page {{page}} · {{count}} entry', summaryMore: 'Page {{page}} · {{count}} entries · more available', summaryMore_one: 'Page {{page}} · {{count}} entry · more available', - viewAria: 'Log display mode', - viewRaw: 'Raw', - viewTable: 'Table', - viewLine: 'Line', - viewStream: 'Stream', - viewPie: 'Pie', loadingPage: 'Loading log page', loadingQuery: 'Querying Loki', loadingWait: 'Waiting for {{seconds}}s. Wide-range queries may take a while.', - pagingHint: 'Logs are paged newest to oldest; charts show the current page, and the top timeline covers the whole query range.', + pagingHint: 'Logs page newest-first; the top timeline covers the whole query range.', domainAll: 'All domains', domainFilter: 'Domain filter', domainPlaceholder: 'Type or pick a domain', @@ -165,42 +160,6 @@ export default { idleHint: 'You can also build queries quickly in the Label browser', retry: 'Query again', }, - views: { - noDomain: 'No domain recorded', - otherDomain: 'Other domains', - unknown: 'Unknown', - untagged: 'Untagged', - other: 'Other', - tableLoaded: '{{count}} log entries loaded on this page.', - tableEmpty: 'No logs to display', - thTime: 'Time', - thNsPod: 'namespace / pod', - thLevel: 'Level', - thLog: 'Log', - lineByLoaded: 'Based on currently loaded logs; load more for additional results.', - lineByRange: 'Log volume aggregated over the query time range, from Loki.', - lineGroupAria: 'Line chart grouping', - lineByDomain: 'By domain', - lineByTotal: 'Total', - lineEmpty: 'No time-series data for this query', - lineSeriesTotal: 'Log count', - streamParsed: 'Showing {{count}} parsed entries', - streamByLevel: 'Showing {{count}} loaded entries by level', - streamSuffix: ', based on currently loaded results.', - streamGroupAria: 'Stream chart grouping', - streamByStatus: 'By HTTP status class', - streamEmpty: 'No log streams to display', - pieByAccess: 'Grouping {{count}} parsed entries by access fields', - pieByLabel: 'Grouping {{count}} loaded entries by label', - pieGroupAria: 'Pie chart grouping', - pieDomain: 'Domain (request Host)', - pieStatus: 'HTTP status', - pieMethod: 'Method', - piePath: 'Path', - pieUpstream: 'Upstream service', - pieLevel: 'Level', - pieEmpty: 'No label distribution to display', - }, ingress: { modesAria: 'Ingress statistics mode', modeTraffic: 'Endpoints PV / UV', @@ -208,7 +167,7 @@ export default { modeSlow: 'Top 10 slow requests', modeDetail: 'Request details', summary: - 'Based on the {{loaded}} entries loaded on this page, of which {{parsed}} are ingress-nginx access logs. Statistics are recomputed after paging; UV is deduplicated by non-empty client IP.', + 'Based on {{loaded}} loaded entries', statPv: 'PV', statUv: 'UV (IP)', statErrors: '4xx / 5xx', @@ -219,9 +178,9 @@ export default { error4xx: '4xx only', slowThreshold: 'Slow request threshold', seconds: '{{count}}s', - slowHint: 'Sorted by request_time from the access log; 408 / 499 / 504 are only timeout hints and do not by themselves prove a backend timeout.', + slowHint: 'Sorted by request_time; 408 / 499 / 504 are timeout hints.', slowMissing: '{{count}} entries lack a valid duration.', - trafficHint: 'Success rate = 2xx / 3xx requests ÷ PV; query strings are stripped from paths.', + trafficHint: 'Success rate = 2xx / 3xx ÷ PV', thHost: 'Request Host', thPath: 'Path', thSuccess: 'Success rate', @@ -257,7 +216,7 @@ export default { loadFailed: 'Failed to load context', }, access: { - labelsUnlimited: 'Unlimited · all namespaces', + labelsUnlimited: 'Unrestricted · all namespaces', loadFailed: 'Failed to load query policies', validate: 'Fill in the policy group name, choose a data source, and select "Anyone" or at least one user', saveFailed: 'Failed to save policy group', @@ -309,19 +268,4 @@ export default { loading: 'Loading…', noMatch: 'No matches', }, - raw: { - searching: 'Querying…', - count: '{{count}} logs', - count_one: '{{count}} log', - dedupSuffix: ' (deduplicated from {{count}})', - dedup: 'Dedup', - empty: 'No matching logs', - original: 'Raw log', - labelsTitle: 'Labels (click to filter)', - filterValue: 'Filter this value', - excludeValue: 'Exclude this value', - fields: 'Extracted fields', - loadMore: 'Load more', - loadingMore: 'Loading…', - }, } diff --git a/frontend/src/i18n/locales/en/settings.ts b/frontend/src/i18n/locales/en/settings.ts index f389e2d..91b6acf 100644 --- a/frontend/src/i18n/locales/en/settings.ts +++ b/frontend/src/i18n/locales/en/settings.ts @@ -45,9 +45,9 @@ export default { cardTitle: 'Alert Channels — Feishu (Lark)', create: 'New channel', // Empty state is assembled as "prefix + link + suffix" (link points to the Alerts page) - emptyBefore: 'No channels yet — create one per alert group (e.g. "App alerts", "Nginx alerts"), then link rules to it on the ', + emptyBefore: 'No channels yet — create one per alert group, ', emptyLink: 'Alerts', - emptyAfter: ' page', + emptyAfter: 'then link rules to it on the alerts page.', webhookConfigured: 'Webhook configured', webhookMissing: 'Not configured', secretConfigured: ' · Signing secret configured', @@ -82,7 +82,7 @@ export default { headerHint: 'Enables LDAP sign-in and bulk user import', errLoad: 'Failed to load LDAP configuration', disabledHint: - 'LDAP is disabled — turn it on and fill in the directory details (server / Bind DN / Base DN). After saving, "Import from LDAP" appears on the Users page.', + 'LDAP disabled — enable it and fill in the directory info to show "Import from LDAP" on the Users page.', hostLabel: 'Server address *', portLabel: 'Port', ldapsLabel: 'LDAPS', @@ -133,7 +133,7 @@ export default { timezone: 'Timezone', timezoneAria: 'Theme schedule timezone', browser: 'Follow browser (local)', - autoHint: 'Light from {{start}}:00 to {{end}}:00 in the selected timezone, dark otherwise; the sun/moon toggle overrides anytime.', + autoHint: 'Light from {{start}}:00–{{end}}:00 in the chosen timezone, dark otherwise; the top bar toggle always works.', }, prefsCard: { title: 'Appearance & Language', diff --git a/frontend/src/i18n/locales/en/storage.ts b/frontend/src/i18n/locales/en/storage.ts index b871b65..5055277 100644 --- a/frontend/src/i18n/locales/en/storage.ts +++ b/frontend/src/i18n/locales/en/storage.ts @@ -10,7 +10,7 @@ export default { deleteWarning_one: 'Delete "{{name}}"? Warning: {{count}} deployment still uses this storage; its object storage config will stop working.', deleteWarning_other: - 'Delete "{{name}}"? Warning: {{count}} deployments still use this storage; their object storage configs will stop working.', + 'Delete "{{name}}"? {{count}} deployment(s) still use it; their object storage will break.', errDelete: 'Failed to delete the storage config', errSetDefault: 'Failed to set the default storage', }, diff --git a/frontend/src/i18n/locales/en/users.ts b/frontend/src/i18n/locales/en/users.ts index 418308b..e2fa855 100644 --- a/frontend/src/i18n/locales/en/users.ts +++ b/frontend/src/i18n/locales/en/users.ts @@ -80,7 +80,7 @@ export default { clear: 'Clear selection', selectAll: 'Select all', confirmTitle: 'Confirm Bulk Deletion', - confirmDesc: 'Delete {{count}} selected users? Protected accounts (your own / the last admin) will be skipped. This cannot be undone.', + confirmDesc: 'Delete {{count}} selected users? Protected accounts are skipped; this cannot be undone.', skippedWarn: '{{count}} skipped: {{reasons}}', failed: 'Bulk delete failed', }, diff --git a/frontend/src/i18n/locales/en/wizard.ts b/frontend/src/i18n/locales/en/wizard.ts index 8f41a2f..7b189dc 100644 --- a/frontend/src/i18n/locales/en/wizard.ts +++ b/frontend/src/i18n/locales/en/wizard.ts @@ -32,7 +32,7 @@ const wizard = { ready: 'Ready', connected: 'Connected: {{names}}', nameSep: ', ', - k8sHint: 'Connect a cluster via kubeconfig or ServiceAccount token; connectivity is verified automatically on creation', + k8sHint: 'Connect via kubeconfig or Token; connectivity is verified on create.', addCluster: 'Add Cluster', hostHint: 'Connect a host via SSH key or password (used for deployment and later collector installation)', addHost: 'Add Host', @@ -44,7 +44,7 @@ const wizard = { manage: 'Manage Storage', add: 'Add Storage', configure: 'Configure Storage', - hint: 'Object storage (MinIO / RustFS / S3 / OSS) is recommended for more reliable replication and retention. Use "Test Connection" when adding to verify credentials and auto-create missing buckets; skip to use local disk.', + hint: 'Object storage (MinIO / RustFS / S3 / OSS) recommended; skip to use local disk.', }, deploy: { meta: '{{mode}} · Namespace {{namespace}} · {{endpoint}}', diff --git a/frontend/src/i18n/locales/zh/alerts.ts b/frontend/src/i18n/locales/zh/alerts.ts index c621ed5..53763cc 100644 --- a/frontend/src/i18n/locales/zh/alerts.ts +++ b/frontend/src/i18n/locales/zh/alerts.ts @@ -34,7 +34,7 @@ export default { sent: '已发送', cooldown: '冷却跳过', failed: '失败', - hits_other: '{{count}} 条命中', + hits: '{{count}} 条命中', }, tpl: { nsError: { @@ -65,7 +65,7 @@ export default { sourcePlaceholder: '选择数据源', query: 'LogQL 查询 *', queryHint: - '常用:{app="xxx"} |= "FATAL" / {namespace=~".+"} |~ "(?i)error" / {app="x"} |= "OutOfMemoryError"', + '常用:{app="xxx"} |= "FATAL"', channels: '通知通道(可多选,扇出到多个告警群)', noChannels: '请先在「系统设置 → 告警通知」创建通道', channelDisabled: '(禁用)', diff --git a/frontend/src/i18n/locales/zh/analysis.ts b/frontend/src/i18n/locales/zh/analysis.ts index 61c644f..d823d70 100644 --- a/frontend/src/i18n/locales/zh/analysis.ts +++ b/frontend/src/i18n/locales/zh/analysis.ts @@ -1,7 +1,6 @@ // 日志分析页(admin)— 经 i18n/domains.ts 注册,页面入口 import '@/i18n/domains' export default { title: '日志分析', - subtitle: '按应用与命名空间统计日志量、错误量与趋势,定位日志大户', adminOnly: { title: '仅管理员可访问', desc: '日志分析面向管理员提供全量日志统计,请联系管理员开通。', @@ -24,7 +23,6 @@ export default { }, trend: { title: '日志量趋势', - subtitle: '总量 / 错误 / 告警 按时间分桶', seriesTotal: '总量', seriesError: '错误', seriesWarn: '告警', @@ -32,25 +30,21 @@ export default { }, stream: { title: '应用日志量流图', - subtitle: '日志量 Top {{count}} 应用随时间的堆叠分布', empty: '没有应用维度的数据', }, pie: { title: '命名空间分布', - subtitle: '按命名空间统计的日志量占比', empty: '没有命名空间维度的数据', other: '其他', }, daily: { fixedBadge: '固定近 7 天', title: '应用每日日志量', - subtitle: '固定统计近 7 个自然日的 Top 应用分布,不受上方时间范围影响', empty: '近 7 天没有应用维度的数据', }, table: { title: '应用日志量排行', - subtitle: '按日志量排序的 Top 10 应用,点击「查看日志」跳转查询页', rank: '#', app: '应用', appWithKey: '应用({{key}})', @@ -64,4 +58,13 @@ export default { untagged: '(未打标签)', empty: '没有应用维度的数据', }, -} \ No newline at end of file + ingress: { + title: 'Ingress 访问分析', + noIngress: '当前数据源未发现 ingress-nginx / ingress 命名空间的访问日志', + empty: '该时间范围内没有可解析的 ingress 访问日志', + loading: '加载 ingress 访问日志中…', + notDetected: '未检测到 ingress-nginx', + toggleAria: '展开 / 折叠 Ingress 访问分析', + loadFailed: '加载 ingress 访问日志失败', + }, +} diff --git a/frontend/src/i18n/locales/zh/clusters.ts b/frontend/src/i18n/locales/zh/clusters.ts index cdd08d3..8dea7e7 100644 --- a/frontend/src/i18n/locales/zh/clusters.ts +++ b/frontend/src/i18n/locales/zh/clusters.ts @@ -51,13 +51,13 @@ export default { rbacScope: '只授予 log 命名空间权限,Token 泄露也不会影响其他命名空间;不要使用 cluster-admin。', rbacScriptIntro: - '一次性创建 log 命名空间、ServiceAccount、命名空间级 Role 和长期 Token(在集群里执行一次,Token 粘贴到下方输入框):', + '在集群执行一次,创建命名空间 / SA / Role / 长期 Token:', rbacScriptComment1: '# 1) 一次创建:命名空间 + SA + 命名空间级 Role + 长期 Token', rbacScriptComment2: '# 2) 取出长期 Token(不自动过期),粘贴到 Token 输入框', rbacNamespaceNote: - '上面的 Namespace 由管理员创建,平台 Token 本身没有任何集群级权限。只有确实需要平台自建命名空间时,才额外授予 namespaces: create/get 的 ClusterRole;注意 namespace 的 create 无法按名称限制,所以预建 log 更安全。', + 'Token 无集群级权限;仅当需要平台自建命名空间时才额外授予 namespaces: create/get。', rbacTokenNote: - 'Token 长期有效,但只绑定 log 命名空间的 Role(仅能操作该命名空间的 Pod / 工作负载),泄露影响面仅限于 log;服务端会转成 kubeconfig 并用 ENCRYPTION_KEY 加密存储,数据库里只有 ENC: 密文,接口不返回明文。轮换时删除该 Secret 重建即可,旧 Token 立即失效。', + 'Token 仅限 log 命名空间,泄露影响面小;服务端加密存储。轮换:删除该 Secret 重建。', contextLabel: '上下文(可选)', contextPlaceholder: '留空则使用默认上下文', submitAdd: '添加集群', diff --git a/frontend/src/i18n/locales/zh/collectors.ts b/frontend/src/i18n/locales/zh/collectors.ts index 6717e77..82dd982 100644 --- a/frontend/src/i18n/locales/zh/collectors.ts +++ b/frontend/src/i18n/locales/zh/collectors.ts @@ -80,7 +80,7 @@ const collectors = { logPath: '采集日志路径', logPathHostHint: '支持 glob 通配(如 /var/log/*.log /var/log/syslog);多个路径用逗号分隔', logPathSidecarHint: - '填写业务写入的文件路径,如 /var/log/app/*.log。已有日志卷会自动共享;无日志卷时仅在专用 logs 目录创建共享卷。/tmp/test.log 等普通目录不会自动挂载。', + '填业务写入的路径,如 /var/log/app/*.log;仅共享已有日志卷或专用 logs 目录。', labels: '自定义标签(JSON,可选)', labelsHint: '合法 JSON 时将附加到每条日志的标签,便于在 Loki/Grafana 中筛选;留空则不加', submit: '创建并部署', @@ -107,11 +107,11 @@ const collectors = { nsLoadFailed: '加载命名空间失败', // nsHint/appSourceHint/appIncludeHint 含行内 ,渲染处用 Trans 组件 nsHint: - '勾「所有命名空间」= 采集全集群;否则只采集勾选的命名空间。生成 Promtail 的 kubernetes_sd_configs.namespaces 白名单。', + '勾「所有命名空间」= 采集全集群;否则只采勾选项。', appLabelSource: 'app 标签取值来源', appSourcePlaceholder: '留空 = 默认(controller 名回退链)', appSourceHint: - '默认按 app.kubernetes.io/name → app → controller 名 → pod 名 四级回退。若集群有自己的私有规范标签(如 k8s.kuboard.cn/name),填在这里会作为最高优先级,其余仍兜底。', + '默认按 app.kubernetes.io/name → app → controller 名 → pod 名 回退;集群自定义标签(如 k8s.kuboard.cn/name)填此处优先。', appSourcePreset: { default: '默认:app.kubernetes.io/name → app → controller 名 → pod 名', controllerName: 'controller_name(Pod 的 ReplicaSet 名,去 hash = Deployment 名)', @@ -122,8 +122,8 @@ const collectors = { appInclude: '只采集这些 app(可选)', appIncludePlaceholder: '留空 = 不限;多个用逗号分隔,如 cex-spot-api, cex-asset-api', appIncludeHint: - '填了之后 Promtail 只保留 app 命中这些值的日志(action: keep),是控制日志量最直接的手段。值需与上面「app 标签取值来源」解析出的 app 一致。', - logPathHint: 'Sidecar 需使用业务容器的共享日志卷,或专用 logs 目录。保存后点击「部署」应用新配置。', + '只保留 app 命中这些值的日志(action: keep),是控制日志量最直接的手段。', + logPathHint: 'Sidecar 需共享日志卷或专用 logs 目录;保存后点「部署」生效。', save: '保存', saving: '保存中…', validation: { diff --git a/frontend/src/i18n/locales/zh/common.ts b/frontend/src/i18n/locales/zh/common.ts index 756ddcd..298bf98 100644 --- a/frontend/src/i18n/locales/zh/common.ts +++ b/frontend/src/i18n/locales/zh/common.ts @@ -105,6 +105,14 @@ export default { apply: '应用时间', recent: '最近使用', }, + ui: { + close: '关闭', + prevPage: '上一页', + nextPage: '下一页', + goPrevPage: '前往上一页', + goNextPage: '前往下一页', + morePages: '更多页码', + }, password: { hide: '隐藏密码', show: '显示密码', diff --git a/frontend/src/i18n/locales/zh/deployments.ts b/frontend/src/i18n/locales/zh/deployments.ts index 4b056c2..d8b9876 100644 --- a/frontend/src/i18n/locales/zh/deployments.ts +++ b/frontend/src/i18n/locales/zh/deployments.ts @@ -9,7 +9,7 @@ export default { // 删除确认:host-docker 连容器一起删,其余模式仅删记录不卸载资源 deleteDockerDesc: '确定要删除「{{name}}」吗?将同时移除主机上的 Loki 容器,此操作不可恢复。', deleteRecordDesc: - '确定要删除「{{name}}」吗?删除记录不会自动卸载集群/主机上已部署的 Loki 资源,此操作不可恢复。', + '删除「{{name}}」不会卸载已部署的 Loki 资源,此操作不可恢复。', errLoadRefs: '加载集群/主机/存储数据失败', errDeploy: '部署失败', errStop: '停止失败', @@ -35,7 +35,7 @@ export default { ready: 'Loki 就绪', notReady: 'Loki 未就绪', runningHint: - '提示:到「日志查询 → 数据源」关联 Loki 后即可查询;跨集群/公网访问需先为 Loki 配置 Ingress 域名。', + '到「日志查询 → 数据源」关联 Loki 后即可查询。', configure: '配置', deploy: '部署', deploying: '部署中...', @@ -101,7 +101,7 @@ export default { loading: '读取中…', templateNote: '由部署模板生成', yamlLabel: '保留配置 YAML', - yamlHint: '编辑 limits_config.retention_period,例如 90d 或 2160h。此处只接受这一项配置。', + yamlHint: '编辑 retention_period,如 90d / 2160h;仅支持这一项。', applyHintRunning: '仅保存会更新部署记录;保存并应用会重新部署 Loki,查询可能短暂中断。', applyHintIdle: '保存后在下次部署时生效。', cancel: '取消', diff --git a/frontend/src/i18n/locales/zh/hosts.ts b/frontend/src/i18n/locales/zh/hosts.ts index 0b71ec1..c550f3c 100644 --- a/frontend/src/i18n/locales/zh/hosts.ts +++ b/frontend/src/i18n/locales/zh/hosts.ts @@ -24,7 +24,7 @@ export default { }, delete: { warning: - '确定要删除「{{name}}」吗?警告:仍有 {{dep}} 个部署、{{col}} 个采集策略引用该主机,删除后它们将无法工作。', + '确定删除「{{name}}」吗?仍有 {{dep}} 个部署、{{col}} 个采集策略在引用。', }, toast: { deleteFailed: '删除主机失败', diff --git a/frontend/src/i18n/locales/zh/logs.ts b/frontend/src/i18n/locales/zh/logs.ts index 62978bc..de12649 100644 --- a/frontend/src/i18n/locales/zh/logs.ts +++ b/frontend/src/i18n/locales/zh/logs.ts @@ -7,7 +7,7 @@ export default { query: 'Loki 查询', access: '查询授权', sources: '数据源管理', - descQuery: '按日志流和时间范围检索;拖动时间线缩小范围,点日志查看前后文。', + descQuery: '按日志流与时间范围检索,可拖时间线、看前后文。', descAccess: '用策略组复用日志范围,并一次关联多个用户。', descSources: '配置 Loki 连接与认证方式。', }, @@ -19,7 +19,7 @@ export default { sourceAria: '数据源', sourceEmptyOption: '暂无可用数据源', limitLabel: '每页条数', - limitHint: '每页最多 1000 条;翻页只保留当前页。大范围先看时间线,再拖选高峰时段。', + limitHint: '每页最多 1000 条,翻页仅保留当前页。', rangeInvalid: '请选择有效的起止时间', queryFailed: '查询失败', }, @@ -27,14 +27,28 @@ export default { loadFailed: '加载数据源失败', missingTitle: '还没有可用的 Loki 数据源', missingBody: - '请先到「数据源管理」关联 Loki:集群内可直接选「集群服务」(命名空间 / Service / 端口,无需 Ingress);跨集群或公网查询,请先给 Loki 配置 Ingress 域名,再用「URL 直连」填写该域名。未关联数据源无法查询。', + '请先到「数据源管理」关联 Loki:集群内选「集群服务」,跨集群 / 公网用「URL 直连」。', goConfigure: '去配置数据源', sectionAria: '数据源管理', heading: 'Loki 数据源', - hint: '提示:Loki 部署在集群内时,可直接添加「集群服务」数据源(无需 Ingress);跨集群 / 公网查询需先为 Loki 配置 Ingress 域名,再用「URL 直连」关联。未关联数据源时,查询页无法使用。', + hint: '集群内 Loki 选「集群服务」即可;跨集群 / 公网需先配 Ingress 域名,再用「URL 直连」。', managedCluster: '集群 {{name}}', managedBadge: '平台部署 · 随部署管理', - add: '+ 手动添加', + countLabel: '共 {{count}} 个', + create: '新建数据源', + createTitle: '新建数据源', + editTitle: '编辑数据源', + edit: '编辑', + cancel: '取消', + testing: '测试中…', + deleteTitle: '删除数据源', + deleteDesc: '删除「{{name}}」后,基于它的查询与授权策略将失效,确定删除吗?', + deleteAria: '删除 {{name}}', + defaultBadge: '默认', + emptyTitle: '还没有数据源', + emptyDesc: '新建「集群服务」或「URL 直连」数据源后,即可在查询页检索日志。', + typeLabel: '类型', + namePlaceholder: 'prod-loki', nameLabel: '名称', targetUrl: 'URL 直连', targetClusterSvc: '集群服务', @@ -61,16 +75,6 @@ export default { saveFailed: '保存失败', remove: '删除', }, - range: { - lastMinutes: '最近 {{count}} 分钟', - lastHours: '最近 {{count}} 小时', - custom: '自定义时间', - title: '选择时间范围', - start: '开始时间', - end: '结束时间', - apply: '应用时间', - recent: '最近使用', - }, editor: { aria: 'LogQL 查询语句', valuesOf: '{{label}} 的值', @@ -83,7 +87,7 @@ export default { templates: { aria: '快捷查询模板', title: '快捷生成 LogQL', - hint: '先选 namespace,再选 app,最后选副本 pod;app 候选只来自所选命名空间。', + hint: '先选 namespace 缩小范围;选中 app / pod 自动查询。', namespace: '命名空间', app: 'App', appWithKey: 'App({{key}})', @@ -93,12 +97,10 @@ export default { pickNsFirst: '先选命名空间可缩小范围', noPod: '该 app 下无副本', optional: '可选', - scopeLogs: '查询该范围日志', errorLogs: '错误 error', warnLogs: '告警 warn', - podLogs: '指定副本日志', ingressLogs: 'Ingress 访问日志', - scopeHint: '先选 namespace,再选 app 或 pod,也可在 Label 浏览器选值', + scopeHint: '选 app / pod 即自动查询;也可在 Label 浏览器选值', }, labels: { browser: 'Label 浏览器', @@ -113,11 +115,11 @@ export default { loading: '加载中…', noValues: '没有匹配值', useAsValue: '使用“{{value}}”作为值', - hint: '先选 namespace 可查该命名空间全部副本;再选 app / service_name 可缩小到某个服务(无 app 标签的工作负载用 namespace 即可)。候选来自当前时间范围内已写入 Loki 的日志;授权范围仍由服务端限制。', + hint: '先选 namespace,再选 app / service_name 逐步缩小;候选为当前时间范围内的日志。', }, volume: { statsLoading: '正在统计整个时间范围的日志量,日志页可继续浏览。', - statsError: '日志量统计超时或失败;日志页仍可使用。可缩小时间范围后重新查询。', + statsError: '日志量统计失败;可缩小时间范围后重试。', histogramLoading: '统计中…', histogramEmpty: '暂无日志量数据', timelineAria: '日志量时间线', @@ -131,16 +133,10 @@ export default { title: '日志结果', summary: '第 {{page}} 页 · 本页 {{count}} 条', summaryMore: '第 {{page}} 页 · 本页 {{count}} 条 · 还有更多', - viewAria: '日志展示方式', - viewRaw: '原始日志', - viewTable: '表格', - viewLine: '折线图', - viewStream: '流图', - viewPie: '饼图', loadingPage: '正在加载日志页', loadingQuery: '正在查询 Loki', loadingWait: '已等待 {{seconds}} 秒。大范围查询可能需要较长时间。', - pagingHint: '日志按时间从新到旧翻页;图表展示当前页,顶部时间线展示整个查询范围。', + pagingHint: '日志按时间从新到旧翻页;顶部时间线为整个查询范围。', domainAll: '全部域名', domainFilter: '域名筛选', domainPlaceholder: '输入或选择域名', @@ -160,42 +156,6 @@ export default { idleHint: '也可以使用 Label 浏览器快速构建查询', retry: '重新查询', }, - views: { - noDomain: '未记录域名', - otherDomain: '其他域名', - unknown: '未知', - untagged: '未标记', - other: '其他', - tableLoaded: '当前页已加载 {{count}} 条日志。', - tableEmpty: '暂无可展示的日志', - thTime: '时间', - thNsPod: 'namespace / pod', - thLevel: '级别', - thLog: '日志', - lineByLoaded: '按当前已加载日志统计;更多结果请继续加载。', - lineByRange: '按查询时间范围统计的日志量,来自 Loki 时间聚合。', - lineGroupAria: '折线图分组字段', - lineByDomain: '按域名', - lineByTotal: '总量', - lineEmpty: '当前查询没有可用的时间统计', - lineSeriesTotal: '日志条数', - streamParsed: '展示 {{count}} 条已解析日志', - streamByLevel: '按级别展示当前已加载的 {{count}} 条日志', - streamSuffix: ',基于当前已加载结果。', - streamGroupAria: '流图分组字段', - streamByStatus: '按 HTTP 状态段', - streamEmpty: '暂无可展示的日志流', - pieByAccess: '按访问字段统计已解析的 {{count}} 条日志', - pieByLabel: '按标签统计当前已加载的 {{count}} 条日志', - pieGroupAria: '饼图分组字段', - pieDomain: '域名(请求 Host)', - pieStatus: 'HTTP 状态码', - pieMethod: '请求方法', - piePath: '请求路径', - pieUpstream: '上游服务', - pieLevel: '级别', - pieEmpty: '暂无可展示的标签分布', - }, ingress: { modesAria: 'Ingress 统计方式', modeTraffic: '接口 PV / UV', @@ -203,7 +163,7 @@ export default { modeSlow: '慢请求 Top 10', modeDetail: '请求明细', summary: - '仅统计当前页已加载的 {{loaded}} 条日志,其中 {{parsed}} 条为 ingress-nginx 访问日志。翻页后重新统计;UV 按非空客户端 IP 去重。', + '基于已加载的 {{loaded}} 条日志统计', statPv: 'PV', statUv: 'UV(IP)', statErrors: '4xx / 5xx', @@ -214,9 +174,9 @@ export default { error4xx: '只看 4xx', slowThreshold: '慢请求阈值', seconds: '{{count}} 秒', - slowHint: '依据 access log 的 request_time 排序;408 / 499 / 504 仅作超时线索,不能单凭状态码断定后端超时。', + slowHint: '按 request_time 排序;408 / 499 / 504 为超时线索。', slowMissing: '{{count}} 条缺少有效耗时。', - trafficHint: '访问成功率按 2xx / 3xx 请求数 ÷ PV 计算;路径已去掉查询参数。', + trafficHint: '成功率 = 2xx / 3xx ÷ PV', thHost: '请求 Host', thPath: '接口路径', thSuccess: '成功率', @@ -301,18 +261,4 @@ export default { loading: '加载中…', noMatch: '无匹配值', }, - raw: { - searching: '查询中…', - count: '{{count}} 条', - dedupSuffix: '(去重后,原 {{count}} 条)', - dedup: '去重', - empty: '没有匹配的日志', - original: '日志原文', - labelsTitle: '标签(点击过滤)', - filterValue: '过滤此值', - excludeValue: '排除此值', - fields: '提取字段', - loadMore: '加载更多', - loadingMore: '加载中…', - }, } diff --git a/frontend/src/i18n/locales/zh/settings.ts b/frontend/src/i18n/locales/zh/settings.ts index f571738..39ff24b 100644 --- a/frontend/src/i18n/locales/zh/settings.ts +++ b/frontend/src/i18n/locales/zh/settings.ts @@ -45,9 +45,9 @@ export default { cardTitle: '告警通知通道(飞书/Lark)', create: '新建通道', // 空态文案按「前段 + 链接 + 后段」拼接(链接指向日志告警页) - emptyBefore: '尚无通知通道 —— 每个告警群创建一个通道(如「应用告警群」「Nginx 告警群」),在', + emptyBefore: '尚无通知通道 —— 每个告警群建一个通道,', emptyLink: '日志告警', - emptyAfter: '页把规则关联到对应通道', + emptyAfter: '在告警规则页把规则关联到对应通道。', webhookConfigured: 'Webhook 已配置', webhookMissing: '未配置', secretConfigured: ' · 签名已配置', @@ -82,7 +82,7 @@ export default { headerHint: '启用后支持 LDAP 登录与批量导入', errLoad: '加载 LDAP 配置失败', disabledHint: - 'LDAP 未启用 —— 打开开关填写目录服务信息(服务器 / Bind DN / Base DN),保存后用户管理页将出现「从 LDAP 导入」', + 'LDAP 未启用 —— 开启并填写目录服务信息后,用户管理页将出现「从 LDAP 导入」。', hostLabel: '服务器地址 *', portLabel: '端口', ldapsLabel: 'LDAPS', @@ -133,7 +133,7 @@ export default { timezone: '判定时区', timezoneAria: '主题定时切换时区', browser: '跟随浏览器(本地)', - autoHint: '按所选时区,{{start}}:00–{{end}}:00 使用浅色,其余时段深色;顶栏太阳/月亮可随时手动覆盖。', + autoHint: '按所选时区 {{start}}:00–{{end}}:00 浅色,其余深色;顶栏可随时手动切换。', }, prefsCard: { title: '外观与语言', diff --git a/frontend/src/i18n/locales/zh/storage.ts b/frontend/src/i18n/locales/zh/storage.ts index 4be44b8..2c61d6f 100644 --- a/frontend/src/i18n/locales/zh/storage.ts +++ b/frontend/src/i18n/locales/zh/storage.ts @@ -8,7 +8,7 @@ export default { emptyDescription: '添加第一个 Loki 存储后端', // 删除确认文案(有部署引用时的警告;count 走中文无复数) deleteWarning: - '确定要删除「{{name}}」吗?警告:仍有 {{count}} 个部署使用该存储,删除后这些部署的对象存储配置将失效。', + '确定删除「{{name}}」吗?仍有 {{count}} 个部署在使用,删除后其对象存储将失效。', errDelete: '删除存储配置失败', errSetDefault: '设置默认存储失败', }, diff --git a/frontend/src/i18n/locales/zh/users.ts b/frontend/src/i18n/locales/zh/users.ts index c3c69c2..b91f9ae 100644 --- a/frontend/src/i18n/locales/zh/users.ts +++ b/frontend/src/i18n/locales/zh/users.ts @@ -67,7 +67,7 @@ export default { noEmail: '无邮箱', empty: '目录下没有用户', importing: '导入中...', - importCount_other: '导入 {{count}} 个用户', + importCount: '导入 {{count}} 个用户', importFailed: '导入失败', loadFailed: '加载 LDAP 用户失败', }, @@ -78,7 +78,7 @@ export default { clear: '清除选择', selectAll: '全选', confirmTitle: '确认批量删除', - confirmDesc: '确定要删除选中的 {{count}} 个用户吗?受保护的账号(自己/最后一个管理员)将被跳过,此操作不可恢复。', + confirmDesc: '删除选中的 {{count}} 个用户?受保护账号会跳过,操作不可恢复。', skippedWarn: '{{count}} 项被跳过:{{reasons}}', failed: '批量删除失败', }, diff --git a/frontend/src/i18n/locales/zh/wizard.ts b/frontend/src/i18n/locales/zh/wizard.ts index 78cbcc2..1e578ca 100644 --- a/frontend/src/i18n/locales/zh/wizard.ts +++ b/frontend/src/i18n/locales/zh/wizard.ts @@ -33,7 +33,7 @@ const wizard = { ready: '已就绪', connected: '已接入:{{names}}', nameSep: '、', - k8sHint: '通过 kubeconfig 或 ServiceAccount Token 接入集群,创建后自动验证连通性', + k8sHint: 'kubeconfig 或 Token 接入,创建后自动验证连通性。', addCluster: '添加集群', hostHint: 'SSH 密钥或密码接入主机(用于部署与后续采集器安装)', addHost: '添加主机', @@ -45,7 +45,7 @@ const wizard = { manage: '管理存储', add: '添加存储', configure: '配置存储', - hint: '推荐对象存储(MinIO / RustFS / S3 / OSS):多副本与数据保留更可靠。添加时可用「测试连接」验证凭证,并自动创建不存在的桶;跳过则使用本地磁盘。', + hint: '推荐对象存储(MinIO / RustFS / S3 / OSS),更可靠;跳过则使用本地磁盘。', }, deploy: { meta: '{{mode}} · 命名空间 {{namespace}} · {{endpoint}}', diff --git a/frontend/src/pages/alerts/index.tsx b/frontend/src/pages/alerts/index.tsx index be738ee..26ac0cf 100644 --- a/frontend/src/pages/alerts/index.tsx +++ b/frontend/src/pages/alerts/index.tsx @@ -2,6 +2,7 @@ import { memo, useCallback, useEffect, useState } from 'react' import { Link } from 'react-router' import { BellRing, Plus, Play, Pencil, Trash2 } from 'lucide-react' import { Button } from '@/components/ui/button' +import { Checkbox } from '@/components/ui/checkbox' import { Input } from '@/components/ui/input' import { Switch } from '@/components/ui/switch' import { Card, CardContent } from '@/components/ui/card' @@ -380,14 +381,12 @@ const AlertsPage = memo(function AlertsPage() { const checked = field.value.includes(String(ch.id)) return (