mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 16:02:24 +00:00
fix(i18n): localize the integration card status pills (#12208)
Every card under Settings -> Integrations built its status pill from bare literals inside the JSX call, so the pill stayed English beside a translated description. audit-localization-coverage.mjs inspects JSX attributes and object properties, not conditional expressions handed to a prop, so the gate stayed green while the strings shipped untranslated. The three token-configured cards share tokenProviderStatusLabel(); Gitea passes optional: true because it works read-only without a token, which is why its unconfigured state reads Optional setup rather than Not configured. Co-authored-by: Evgenii <kumiro@me.com>
This commit is contained in:
@@ -99,7 +99,14 @@ export function JiraIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
checking={checking}
|
||||
statusTone={connected ? 'connected' : 'attention'}
|
||||
statusLabel={connected ? 'Connected' : 'Not connected'}
|
||||
statusLabel={
|
||||
connected
|
||||
? translate('auto.components.settings.jira.integration.card.statusConnected', 'Connected')
|
||||
: translate(
|
||||
'auto.components.settings.jira.integration.card.statusNotConnected',
|
||||
'Not connected'
|
||||
)
|
||||
}
|
||||
actions={
|
||||
!checking ? (
|
||||
<Button
|
||||
|
||||
@@ -91,7 +91,17 @@ export function LinearIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
checking={checking}
|
||||
statusTone={connected ? 'connected' : 'attention'}
|
||||
statusLabel={connected ? 'Connected' : 'Not connected'}
|
||||
statusLabel={
|
||||
connected
|
||||
? translate(
|
||||
'auto.components.settings.task.tracker.integration.cards.statusConnected',
|
||||
'Connected'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.settings.task.tracker.integration.cards.statusNotConnected',
|
||||
'Not connected'
|
||||
)
|
||||
}
|
||||
actions={
|
||||
!checking ? (
|
||||
<Button
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
|
||||
import { usePreflightCardStatuses } from './source-control-preflight-card-status'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { tokenProviderStatusLabel } from './token-source-control-status'
|
||||
|
||||
export function BitbucketIntegrationCard(): React.JSX.Element {
|
||||
const { statuses, unavailable, refresh } = usePreflightCardStatuses('bitbucket')
|
||||
@@ -32,15 +33,7 @@ export function BitbucketIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
checking={status === 'checking'}
|
||||
statusTone={connected ? 'connected' : 'attention'}
|
||||
statusLabel={
|
||||
connected
|
||||
? 'Connected'
|
||||
: status === 'unavailable'
|
||||
? 'Unavailable'
|
||||
: status === 'not-configured'
|
||||
? 'Not configured'
|
||||
: 'Auth failed'
|
||||
}
|
||||
statusLabel={tokenProviderStatusLabel({ configured: connected, status })}
|
||||
>
|
||||
{status !== 'checking' && !connected ? (
|
||||
<IntegrationCardDetails>
|
||||
@@ -154,17 +147,11 @@ export function AzureDevOpsIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
checking={status === 'checking'}
|
||||
statusTone={configured ? 'connected' : 'attention'}
|
||||
statusLabel={
|
||||
configured
|
||||
? statuses.azureDevOpsAccount
|
||||
? 'Connected'
|
||||
: 'Configured'
|
||||
: status === 'unavailable'
|
||||
? 'Unavailable'
|
||||
: status === 'not-configured'
|
||||
? 'Not configured'
|
||||
: 'Auth failed'
|
||||
}
|
||||
statusLabel={tokenProviderStatusLabel({
|
||||
configured,
|
||||
hasAccount: Boolean(statuses.azureDevOpsAccount),
|
||||
status
|
||||
})}
|
||||
>
|
||||
{status !== 'checking' && !configured ? (
|
||||
<IntegrationCardDetails>
|
||||
@@ -283,17 +270,12 @@ export function GiteaIntegrationCard(): React.JSX.Element {
|
||||
}
|
||||
checking={status === 'checking'}
|
||||
statusTone={configured ? 'connected' : 'attention'}
|
||||
statusLabel={
|
||||
configured
|
||||
? statuses.giteaAccount
|
||||
? 'Connected'
|
||||
: 'Configured'
|
||||
: status === 'unavailable'
|
||||
? 'Unavailable'
|
||||
: status === 'not-configured'
|
||||
? 'Optional setup'
|
||||
: 'Auth failed'
|
||||
}
|
||||
statusLabel={tokenProviderStatusLabel({
|
||||
configured,
|
||||
hasAccount: Boolean(statuses.giteaAccount),
|
||||
status,
|
||||
optional: true
|
||||
})}
|
||||
>
|
||||
{status !== 'checking' && !configured ? (
|
||||
<IntegrationCardDetails>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import type { AzureDevOpsStatus, BitbucketStatus, GiteaStatus } from './integrations-pane-status'
|
||||
|
||||
/**
|
||||
* Card status as the pane computes it: the provider's own state, or
|
||||
* `'unavailable'` when the runtime cannot report on it at all.
|
||||
*/
|
||||
export type TokenProviderStatus = BitbucketStatus | AzureDevOpsStatus | GiteaStatus | 'unavailable'
|
||||
|
||||
const NS = 'auto.components.settings.token.source.control.integration.cards'
|
||||
|
||||
/**
|
||||
* Status copy for the token-configured source-control cards (Bitbucket, Azure
|
||||
* DevOps, Gitea). They share the same states, so the labels live in one place.
|
||||
*/
|
||||
export function tokenProviderStatusLabel(input: {
|
||||
/** Credentials present for the provider. */
|
||||
configured: boolean
|
||||
/** An account was resolved with those credentials. */
|
||||
hasAccount?: boolean
|
||||
status: TokenProviderStatus
|
||||
/** Gitea works read-only without a token, so its unconfigured state is softer. */
|
||||
optional?: boolean
|
||||
}): string {
|
||||
if (input.configured) {
|
||||
return input.hasAccount === false
|
||||
? translate(`${NS}.statusConfigured`, 'Configured')
|
||||
: translate(`${NS}.statusConnected`, 'Connected')
|
||||
}
|
||||
if (input.status === 'unavailable') {
|
||||
return translate(`${NS}.statusUnavailable`, 'Unavailable')
|
||||
}
|
||||
if (input.status === 'not-configured') {
|
||||
return input.optional
|
||||
? translate(`${NS}.statusOptionalSetup`, 'Optional setup')
|
||||
: translate(`${NS}.statusNotConfigured`, 'Not configured')
|
||||
}
|
||||
return translate(`${NS}.statusAuthFailed`, 'Auth failed')
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Integration cards build their status pill inside the JSX call, which the
|
||||
* coverage audit does not inspect — it looks at JSX attributes and object
|
||||
* properties, not conditional expressions passed to a prop. These assertions
|
||||
* pin the catalog contract so a card that goes back to a bare literal fails here.
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import en from './locales/en.json'
|
||||
|
||||
function lookup(key: string): string | undefined {
|
||||
const value = key
|
||||
.split('.')
|
||||
.reduce<unknown>(
|
||||
(node, part) =>
|
||||
node && typeof node === 'object' ? (node as Record<string, unknown>)[part] : undefined,
|
||||
en as unknown
|
||||
)
|
||||
return typeof value === 'string' ? value : undefined
|
||||
}
|
||||
|
||||
const REQUIRED_KEYS: Record<string, string> = {
|
||||
// Linear and other task trackers
|
||||
'auto.components.settings.task.tracker.integration.cards.statusConnected': 'Connected',
|
||||
'auto.components.settings.task.tracker.integration.cards.statusNotConnected': 'Not connected',
|
||||
// Jira
|
||||
'auto.components.settings.jira.integration.card.statusConnected': 'Connected',
|
||||
'auto.components.settings.jira.integration.card.statusNotConnected': 'Not connected',
|
||||
// Bitbucket, Azure DevOps, Gitea
|
||||
'auto.components.settings.token.source.control.integration.cards.statusConnected': 'Connected',
|
||||
'auto.components.settings.token.source.control.integration.cards.statusConfigured': 'Configured',
|
||||
'auto.components.settings.token.source.control.integration.cards.statusUnavailable':
|
||||
'Unavailable',
|
||||
'auto.components.settings.token.source.control.integration.cards.statusNotConfigured':
|
||||
'Not configured',
|
||||
'auto.components.settings.token.source.control.integration.cards.statusOptionalSetup':
|
||||
'Optional setup',
|
||||
'auto.components.settings.token.source.control.integration.cards.statusAuthFailed': 'Auth failed'
|
||||
}
|
||||
|
||||
describe('integration card status labels', () => {
|
||||
it.each(Object.entries(REQUIRED_KEYS))('keeps %s in the English catalog', (key, expected) => {
|
||||
expect(lookup(key)).toBe(expected)
|
||||
})
|
||||
})
|
||||
@@ -8489,7 +8489,9 @@
|
||||
"d5c5b47bb9": "update",
|
||||
"fb854902d9": "connecting",
|
||||
"9a9f8d4910": "Connect Jira Cloud to browse, create, and link issues.",
|
||||
"74f3063026": "{{value0}} site{{value1}} connected"
|
||||
"74f3063026": "{{value0}} site{{value1}} connected",
|
||||
"statusConnected": "Connected",
|
||||
"statusNotConnected": "Not connected"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9312,7 +9314,9 @@
|
||||
"disconnect_all": "Disconnect",
|
||||
"account_scope_prefix": "Account scope",
|
||||
"2d60ec7921": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are sent to the selected remote runtime and stored there with runtime-supported encryption.",
|
||||
"977e360b71": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are stored locally and encrypted when local runtime storage supports it."
|
||||
"977e360b71": "Connect a Jira Cloud site with an API token, or a self-hosted Jira with a personal access token or username and password. Credentials are stored locally and encrypted when local runtime storage supports it.",
|
||||
"statusConnected": "Connected",
|
||||
"statusNotConnected": "Not connected"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9353,7 +9357,13 @@
|
||||
"63a7f47392": "ORCA_BITBUCKET_EMAIL",
|
||||
"24ac1c69dc": "Bitbucket status is not available in this runtime yet.",
|
||||
"a924e8dcd1": "Pull requests and build statuses via Bitbucket Cloud API tokens.",
|
||||
"0fa5629dad": "Pull requests and build statuses"
|
||||
"0fa5629dad": "Pull requests and build statuses",
|
||||
"statusConnected": "Connected",
|
||||
"statusUnavailable": "Unavailable",
|
||||
"statusNotConfigured": "Not configured",
|
||||
"statusAuthFailed": "Auth failed",
|
||||
"statusConfigured": "Configured",
|
||||
"statusOptionalSetup": "Optional setup"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user