feat(platform): add i18n support for agent config fields#943
Conversation
Add locale-based translations for agent fields (displayName, description, conversationStarters) with an i18n key in the agent JSON schema. Includes org-level default locale setting, locale-aware field resolution in chat, auto-translate via AI in the conversation starters editor, and locale tab UI for managing translations per language.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
📝 WalkthroughWalkthroughThis PR introduces internationalization support for agents, enabling per-locale overrides of agent metadata fields (displayName, description, conversationStarters). The changes extend the agent JSON schema to include an optional Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@services/platform/app/features/chat/hooks/use-effective-agent.ts`:
- Around line 44-50: The resolve function should normalize runtime locale tags
to their base language before calling resolveAgentLocale to avoid mismatches
with base-only agent.i18n keys; update the code in use-effective-agent.ts
(inside the resolve function where locale and defaultLocale are used) to derive
baseLocale (e.g., strip region subtags like "de-DE" -> "de") and
baseDefaultLocale and pass those normalized values into
resolveAgentLocale(agent, baseLocale, baseDefaultLocale) so that agents (from
agents and agent.i18n) match regardless of region-specific runtime tags.
In
`@services/platform/app/features/settings/organization/components/organization-settings.tsx`:
- Around line 90-94: The form currently seeds defaultLocale from
existingMetadata.defaultLocale without checking it is one of
SUPPORTED_AGENT_LOCALES, so validate that value before using it: when
initializing defaultLocale (the block referencing existingMetadata.defaultLocale
and DEFAULT_LOCALE) check membership in SUPPORTED_AGENT_LOCALES and fall back to
DEFAULT_LOCALE if not supported; also ensure any save/update path that persists
defaultLocale performs the same validation to avoid re-persisting unsupported
legacy locales (this will prevent resolveAgentLocale() from looking up missing
keys in the agent i18n record).
In
`@services/platform/app/routes/dashboard/`$id/agents/$agentId/conversation-starters.tsx:
- Around line 54-62: The duplicate parseDefaultLocale function (and its
DEFAULT_LOCALE constant) should be extracted into a single shared utility
module: move the logic into an exported function parseDefaultLocale and export
DEFAULT_LOCALE from a common utility, then replace the local implementations in
both conversation-starters.tsx and use-effective-agent.ts with imports from that
utility; ensure the exported function signature and behavior remain identical
and update imports where parseDefaultLocale and DEFAULT_LOCALE are referenced.
- Around line 234-267: The locale tab buttons rendered in the localeTabs map
(button with onClick calling setEditingLocale, key={locale}) lack an explicit
focus-visible style; update the className passed into cn for that button to
include keyboard-focus styles (e.g., focus:outline-none and focus-visible:ring /
focus-visible:ring-offset / focus-visible:ring-foreground with an accessible
ring color and offset) so the active/focus state is visible for keyboard users
while preserving the existing active/hover classes; ensure the focus rules apply
to the same button element that uses hasLocaleContent, tGlobal and t so keyboard
users can clearly see which tab is focused.
- Around line 89-94: getStarters is recreated on every render because it's
declared inside the component; memoize it with React.useMemo (depend on
editingLocale and config) or move the logic into a stable helper to avoid
redefinition. Specifically, wrap the logic that reads
config.conversationStarters and
config.i18n?.[editingLocale]?.conversationStarters in useMemo (dependencies:
[editingLocale, config]) so getStarters returns the memoized array and is not
recreated each render.
In `@services/platform/lib/shared/constants/agents.ts`:
- Line 5: The exported type AgentLocale is unused and causing a lint failure;
either remove the unused export or consume it where appropriate. Locate the
declaration "export type AgentLocale = (typeof
SUPPORTED_AGENT_LOCALES)[number];" and either (a) delete the export (or the
entire type line) if no code references it, or (b) replace the unused export
with an internal (non-exported) type or use AgentLocale in the code that defines
or types agent-related functions/variables (e.g., function signatures, params,
or constants referencing SUPPORTED_AGENT_LOCALES) so it is consumed; ensure the
change removes the unused-export lint error without altering runtime behavior.
In `@services/platform/lib/shared/schemas/agents.ts`:
- Line 25: Remove the unused exported type TranslatableFields to satisfy the
linter: delete the line exporting TranslatableFields (which references
translatableFieldsSchema) or, if the type will be needed internally only,
convert it to a non-exported type (e.g., change "export type TranslatableFields"
to "type TranslatableFields") so the symbol is no longer exported and the
unused-export lint error is resolved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5b6ac8bc-072a-410e-8277-61d818770689
⛔ Files ignored due to path filters (1)
services/platform/convex/_generated/api.d.tsis excluded by!**/_generated/**
📒 Files selected for processing (13)
examples/agents/chat-agent.jsonservices/platform/app/features/agents/hooks/mutations.tsservices/platform/app/features/chat/hooks/queries.tsservices/platform/app/features/chat/hooks/use-effective-agent.tsservices/platform/app/features/settings/organization/components/organization-settings.tsxservices/platform/app/routes/dashboard/$id/agents/$agentId/conversation-starters.tsxservices/platform/convex/agents/file_actions.tsservices/platform/convex/agents/file_utils.tsservices/platform/convex/agents/translate_fields.tsservices/platform/lib/shared/constants/agents.tsservices/platform/lib/shared/schemas/agents.tsservices/platform/lib/shared/utils/resolve-agent-locale.tsservices/platform/messages/en.json
| const locale = i18n.language; | ||
| const defaultLocale = parseDefaultLocale(organization?.metadata); | ||
|
|
||
| return useMemo(() => { | ||
| function resolve(agent: NonNullable<typeof agents>[number]) { | ||
| const resolved = resolveAgentLocale(agent, locale, defaultLocale); | ||
| return { |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🌐 Web query:
In i18next/react-i18next, can i18n.language/i18n.resolvedLanguagereturn region-specific tags likeen-USorde-DE, and what is the recommended way to handle base-language fallback (en, de)?
💡 Result:
Yes, i18n.language and i18n.resolvedLanguage can return region-specific tags like en-US or de-DE. i18n.language reflects the currently detected or set language code (e.g., from browser navigator or language detector), which can include region variants. i18n.resolvedLanguage (available since v21.0.0) represents the primary resolved language after applying configuration like supportedLngs and load options, suitable for use in language switchers. The recommended way to handle base-language fallback (e.g., en-US → en, de-DE → de) is through i18next configuration options: - Use load: 'all' (default): Loads and falls back from specific (en-US) to base (en) to fallbackLng. - Use supportedLngs: ['en', 'de'] with nonExplicitSupportedLngs: true: Treats variants like en-US as supported if base en is listed. - Use load: 'languageOnly': Strips to base language only (e.g., en), but may have bugs with supportedLngs as noted in recent issues. Provide translation resources for both specific (e.g., en-US/translation.json) and base (e.g., en/translation.json) languages. i18next automatically resolves keys by trying the most specific available match first. This applies identically in react-i18next, as it extends i18next. Access via useTranslation hook: const { i18n } = useTranslation; i18n.language or i18n.resolvedLanguage. Example configuration: i18next.init({ supportedLngs: ['en', 'de'], nonExplicitSupportedLngs: true, fallbackLng: 'en', load: 'all' // default });
Citations:
- 1: http://www.i18next.com/overview/api
- 2: https://www.i18next.com/overview/configuration-options
- 3: https://www.i18next.com/overview/api
- 4: supportedLngs does not correctly resolve regional language codes (e.g., 'en-US' to 'en') when load: 'languageOnly' is used i18next/i18next#2354
- 5: https://react.i18next.com/latest/using-with-hooks
🏁 Script executed:
# First, let's examine the file in question
cat -n services/platform/app/features/chat/hooks/use-effective-agent.ts | head -70Repository: tale-project/tale
Length of output: 3016
🏁 Script executed:
# Find where resolveAgentLocale is defined
fd -t f -e ts -e tsx | xargs grep -l "resolveAgentLocale" | head -10Repository: tale-project/tale
Length of output: 184
🏁 Script executed:
# Look for the agent structure and i18n property
fd -t f -e ts -e tsx | xargs grep -l "agent\.i18n" | head -10Repository: tale-project/tale
Length of output: 119
🏁 Script executed:
# Check i18next configuration
fd -t f -e ts -e tsx -e js | xargs grep -l "i18next.init\|i18n.init" | head -5Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Read the resolve-agent-locale implementation
cat -n services/platform/lib/shared/utils/resolve-agent-locale.tsRepository: tale-project/tale
Length of output: 1574
🏁 Script executed:
# Search for i18next configuration in common locations
find services/platform -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) | xargs grep -l "i18next\.init\|i18n\.init\|i18n\.use" | head -10Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Check what AgentI18nOverrides looks like
find services/platform -path "*/convex/agents/file_utils*" -type f | xargs catRepository: tale-project/tale
Length of output: 3705
🏁 Script executed:
# Search for how agent.i18n is populated or set
fd -t f -e ts -e tsx | xargs grep -A5 "i18n.*=" | grep -A5 "agent" | head -40Repository: tale-project/tale
Length of output: 2381
🏁 Script executed:
# Look for test files or examples of agent.i18n usage
fd -t f \( -name "*.test.ts" -o -name "*.spec.ts" -o -name "*.test.tsx" \) | xargs grep -l "resolveAgentLocale\|agent.*i18n" 2>/dev/nullRepository: tale-project/tale
Length of output: 293
🏁 Script executed:
# Search for i18next configuration files
find services/platform -type f \( -name "i18n.ts" -o -name "i18next.ts" -o -name "i18n.config.ts" -o -name "main.ts" \) | head -10Repository: tale-project/tale
Length of output: 132
🏁 Script executed:
# Look for where agent.i18n keys are actually populated/stored
rg "i18n\s*:\s*\{" -A 10 services/platform --type ts --type tsx | head -50Repository: tale-project/tale
Length of output: 88
🏁 Script executed:
# Check if there are any examples of agent JSON files with i18n keys
find . -name "*.json" -path "*/agents/*" -type f | head -5 | xargs cat 2>/dev/nullRepository: tale-project/tale
Length of output: 24069
🏁 Script executed:
# Read the i18next configuration file
cat -n services/platform/lib/i18n/i18n.tsRepository: tale-project/tale
Length of output: 906
🏁 Script executed:
# Check if there's any locale normalization already in place elsewhere
rg "split.*-|substring.*locale|\.language" services/platform/lib/i18n/ -A 3 -B 3Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Look for how agent.i18n keys are actually set/structured in real usage
rg "i18n.*\[" services/platform --type ts | head -20Repository: tale-project/tale
Length of output: 466
🏁 Script executed:
# Check how locale is being set/detected - look for language detector setup
rg "languageDetector|LanguageDetector|browser" services/platform/lib/i18n/ -A 3Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Check if there are other places where agent.i18n is accessed with different locale formats
rg "agent\.i18n\[|i18n\?\.\[" services/platform --type ts --type tsx -B 2 -A 2Repository: tale-project/tale
Length of output: 88
🏁 Script executed:
# Verify what locales are actually expected/configured in the app
rg "defaultLocale\|supportedLngs\|fallbackLng" services/platform/lib/i18n/ -A 1Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Check the i18n config file
cat -n services/platform/lib/i18n/config.tsRepository: tale-project/tale
Length of output: 111
🏁 Script executed:
# Look for language detector configuration
rg "detect" services/platform/lib/i18n -iRepository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Search more broadly for how agents are being accessed with i18n
rg "agent\.i18n" services/platform -B 3 -A 3Repository: tale-project/tale
Length of output: 662
🏁 Script executed:
# Check if there's any language detection or browser locale detection
find services/platform -type f -name "*.ts" -o -name "*.js" | xargs grep -l "navigator\.language\|languageDetector\|detect" 2>/dev/null | head -10Repository: tale-project/tale
Length of output: 660
🏁 Script executed:
# Check the entire i18n directory for any other configuration
ls -la services/platform/lib/i18n/Repository: tale-project/tale
Length of output: 466
🏁 Script executed:
# Verify if defaultLocale in parseDefaultLocale can be region-specific
rg "defaultLocale" services/platform --type ts -B 2 -A 2 | head -30Repository: tale-project/tale
Length of output: 1933
🏁 Script executed:
# Check if there are any tests for resolveAgentLocale that show expected behavior
find services/platform -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "resolveAgentLocale" 2>/dev/nullRepository: tale-project/tale
Length of output: 45
🏁 Script executed:
# Look at the resolve-locale utility which seems to handle locale resolution
cat -n services/platform/lib/utils/intl/resolve-locale.tsRepository: tale-project/tale
Length of output: 1144
🏁 Script executed:
# Check the i18n-provider and client setup
cat -n services/platform/lib/i18n/client.tsx
cat -n services/platform/lib/i18n/i18n-provider.tsxRepository: tale-project/tale
Length of output: 781
🏁 Script executed:
# Search for actual usages in conversation-starters component to see how locales are being passed
cat -n services/platform/app/routes/dashboard/'$id'/agents/'$agentId'/conversation-starters.tsx | head -100Repository: tale-project/tale
Length of output: 4087
🏁 Script executed:
# Check if there's a language detector or any code that sets language to region-specific values
rg "changeLanguage|setLanguage" services/platform --type ts -B 2 -A 2Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Check SUPPORTED_AGENT_LOCALES to see what locales are actually supported
rg "SUPPORTED_AGENT_LOCALES" services/platform -A 5Repository: tale-project/tale
Length of output: 3125
🏁 Script executed:
# Check if defaultLocale from organization metadata is validated to be base-only
rg "metadata.*locale\|defaultLocale" services/platform --type ts -B 2 -A 2 | head -40Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Check if there's any dynamic language changing or language detection
rg "i18n\.changeLanguage|i18n\.use.*detect|LanguageDetector" services/platformRepository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Look at use-locale hook to see if it handles language locale detection
cat -n services/platform/app/hooks/use-locale.tsRepository: tale-project/tale
Length of output: 2106
🏁 Script executed:
# Verify the complete flow - check if defaultLocale from metadata is validated
cat -n services/platform/lib/shared/constants/agents.ts | grep -A 10 "SUPPORTED_AGENT_LOCALES\|AgentLocale"Repository: tale-project/tale
Length of output: 204
🏁 Script executed:
# Check if there's any place where i18n.changeLanguage is called
rg "changeLanguage|\.language\s*=" services/platform --type tsRepository: tale-project/tale
Length of output: 165
🏁 Script executed:
# Verify that i18n instance is only used for translation keys, not language switching
rg "from.*react-i18next" services/platform --type ts -A 3 | head -50Repository: tale-project/tale
Length of output: 1328
🏁 Script executed:
# Check if there's any test showing what i18n.language actually returns
find services/platform -name "*.test.ts" | xargs grep -l "i18n.language" 2>/dev/nullRepository: tale-project/tale
Length of output: 45
🏁 Script executed:
# Check if there are any integration tests that show what i18n.language actually returns
find services/platform -name "*.integration.ts" -o -name "*.e2e.ts" | xargs grep -l "i18n\|language" 2>/dev/nullRepository: tale-project/tale
Length of output: 45
🏁 Script executed:
# Look at the actual test for use-effective-agent to see what's being tested
find services/platform -path "*use-effective-agent*test*" -type fRepository: tale-project/tale
Length of output: 140
🏁 Script executed:
# Check if there's any place where i18n configuration is modified or extended
rg "i18n\.init\|i18next\.init" services/platform --type ts -A 10Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Read the test file to understand expected behavior
cat -n services/platform/app/features/chat/hooks/__tests__/use-effective-agent.test.tsRepository: tale-project/tale
Length of output: 5784
🏁 Script executed:
# Check if there's any dynamic language switching in the codebase
rg "localStorage.*user-locale\|changeLanguage" services/platform --type tsRepository: tale-project/tale
Length of output: 43
🏁 Script executed:
# Final verification: Check if there's any recent change to i18n config or if language detector was added
git log --oneline -20 services/platform/lib/i18n/ 2>/dev/null || echo "No git history"Repository: tale-project/tale
Length of output: 133
Consider normalizing locale tags to base language for forward compatibility.
While the current i18next configuration only loads 'en' and would return i18n.language as a base tag, the resolveAgentLocale function does exact-key matching against agent.i18n. If the i18n setup is later extended to support region-specific variants (e.g., 'de-DE') or a language detector is enabled, runtime locale tags could become region-specific while stored overrides remain base-only (e.g., 'de'), causing fallback failures.
🔧 Suggested fix
const DEFAULT_CHAT_AGENT_NAME = 'chat-agent';
const DEFAULT_LOCALE = 'en';
+function normalizeLocaleTag(locale: string): string {
+ return locale.split('-')[0]?.toLowerCase() || DEFAULT_LOCALE;
+}
+
function parseDefaultLocale(metadata: unknown): string {
if (metadata && typeof metadata === 'object' && 'defaultLocale' in metadata) {
const value = (metadata as { defaultLocale: unknown }).defaultLocale; // oxlint-disable-line typescript/no-unsafe-type-assertion
if (typeof value === 'string') return value;
}
return DEFAULT_LOCALE;
}
@@
- const locale = i18n.language;
- const defaultLocale = parseDefaultLocale(organization?.metadata);
+ const locale = normalizeLocaleTag(i18n.language);
+ const defaultLocale = normalizeLocaleTag(
+ parseDefaultLocale(organization?.metadata),
+ );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@services/platform/app/features/chat/hooks/use-effective-agent.ts` around
lines 44 - 50, The resolve function should normalize runtime locale tags to
their base language before calling resolveAgentLocale to avoid mismatches with
base-only agent.i18n keys; update the code in use-effective-agent.ts (inside the
resolve function where locale and defaultLocale are used) to derive baseLocale
(e.g., strip region subtags like "de-DE" -> "de") and baseDefaultLocale and pass
those normalized values into resolveAgentLocale(agent, baseLocale,
baseDefaultLocale) so that agents (from agents and agent.i18n) match regardless
of region-specific runtime tags.
| defaultLocale: | ||
| typeof existingMetadata.defaultLocale === 'string' | ||
| ? existingMetadata.defaultLocale | ||
| : DEFAULT_LOCALE, | ||
| }, |
There was a problem hiding this comment.
Consider validating that stored defaultLocale is a supported value.
The form initializes defaultLocale from existingMetadata.defaultLocale if it's a string, but doesn't verify it's in SUPPORTED_AGENT_LOCALES. If legacy data contains an unsupported locale (e.g., 'fr'), it will be used and re-persisted.
This could cause unexpected behavior in resolveAgentLocale() which would attempt to look up i18n overrides for a locale that doesn't exist in the agent's i18n record.
🛡️ Proposed fix to validate against supported locales
+import type { AgentLocale } from '@/lib/shared/constants/agents';
+
+function isValidLocale(value: string): value is AgentLocale {
+ return (SUPPORTED_AGENT_LOCALES as readonly string[]).includes(value);
+}
+
const form = useForm<OrganizationFormData>({
mode: 'onChange',
defaultValues: {
name: organization?.name || '',
defaultLocale:
- typeof existingMetadata.defaultLocale === 'string'
+ typeof existingMetadata.defaultLocale === 'string' &&
+ isValidLocale(existingMetadata.defaultLocale)
? existingMetadata.defaultLocale
: DEFAULT_LOCALE,
},
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@services/platform/app/features/settings/organization/components/organization-settings.tsx`
around lines 90 - 94, The form currently seeds defaultLocale from
existingMetadata.defaultLocale without checking it is one of
SUPPORTED_AGENT_LOCALES, so validate that value before using it: when
initializing defaultLocale (the block referencing existingMetadata.defaultLocale
and DEFAULT_LOCALE) check membership in SUPPORTED_AGENT_LOCALES and fall back to
DEFAULT_LOCALE if not supported; also ensure any save/update path that persists
defaultLocale performs the same validation to avoid re-persisting unsupported
legacy locales (this will prevent resolveAgentLocale() from looking up missing
keys in the agent i18n record).
| const DEFAULT_LOCALE = 'en'; | ||
|
|
||
| function parseDefaultLocale(metadata: unknown): string { | ||
| if (metadata && typeof metadata === 'object' && 'defaultLocale' in metadata) { | ||
| const value = (metadata as { defaultLocale: unknown }).defaultLocale; // oxlint-disable-line typescript/no-unsafe-type-assertion | ||
| if (typeof value === 'string') return value; | ||
| } | ||
| return DEFAULT_LOCALE; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Duplicate parseDefaultLocale function.
This function is identical to the one in services/platform/app/features/chat/hooks/use-effective-agent.ts (see context snippet 2). Consider extracting to a shared utility to avoid duplication.
♻️ Suggested refactor
Extract to a shared utility, e.g., lib/shared/utils/parse-default-locale.ts:
const DEFAULT_LOCALE = 'en';
export function parseDefaultLocale(metadata: unknown): string {
if (metadata && typeof metadata === 'object' && 'defaultLocale' in metadata) {
const value = (metadata as { defaultLocale: unknown }).defaultLocale;
if (typeof value === 'string') return value;
}
return DEFAULT_LOCALE;
}Then import in both use-effective-agent.ts and conversation-starters.tsx.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@services/platform/app/routes/dashboard/`$id/agents/$agentId/conversation-starters.tsx
around lines 54 - 62, The duplicate parseDefaultLocale function (and its
DEFAULT_LOCALE constant) should be extracted into a single shared utility
module: move the logic into an exported function parseDefaultLocale and export
DEFAULT_LOCALE from a common utility, then replace the local implementations in
both conversation-starters.tsx and use-effective-agent.ts with imports from that
utility; ensure the exported function signature and behavior remain identical
and update imports where parseDefaultLocale and DEFAULT_LOCALE are referenced.
| function getStarters(): string[] { | ||
| if (editingLocale === null) { | ||
| return config.conversationStarters ?? []; | ||
| } | ||
| return config.i18n?.[editingLocale]?.conversationStarters ?? []; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
getStarters function is redefined on every render.
This closure is recreated on each render since it's a plain function inside the component. While it works correctly, consider memoizing or restructuring for clarity.
♻️ Suggested refactor using useMemo
- function getStarters(): string[] {
- if (editingLocale === null) {
- return config.conversationStarters ?? [];
- }
- return config.i18n?.[editingLocale]?.conversationStarters ?? [];
- }
+ const currentStarters = useMemo(() => {
+ if (editingLocale === null) {
+ return config.conversationStarters ?? [];
+ }
+ return config.i18n?.[editingLocale]?.conversationStarters ?? [];
+ }, [editingLocale, config.conversationStarters, config.i18n]);
- const [items, setItems] = useState<StarterItem[]>(() =>
- toItems(getStarters()),
- );
+ const [items, setItems] = useState<StarterItem[]>(() =>
+ toItems(config.conversationStarters ?? []),
+ );
- const startersKey = JSON.stringify(getStarters());
+ const startersKey = JSON.stringify(currentStarters);
useEffect(() => {
- setItems(toItems(getStarters()));
+ setItems(toItems(currentStarters));
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [startersKey, editingLocale]);
+ }, [startersKey]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@services/platform/app/routes/dashboard/`$id/agents/$agentId/conversation-starters.tsx
around lines 89 - 94, getStarters is recreated on every render because it's
declared inside the component; memoize it with React.useMemo (depend on
editingLocale and config) or move the logic into a stable helper to avoid
redefinition. Specifically, wrap the logic that reads
config.conversationStarters and
config.i18n?.[editingLocale]?.conversationStarters in useMemo (dependencies:
[editingLocale, config]) so getStarters returns the memoized array and is not
recreated each render.
| <div className="scrollbar-hide border-border flex items-center gap-4 overflow-x-auto border-b"> | ||
| {localeTabs.map(({ locale, isDefault }) => { | ||
| const active = isDefault | ||
| ? editingLocale === null | ||
| : editingLocale === locale; | ||
| return ( | ||
| <button | ||
| key={locale} | ||
| type="button" | ||
| onClick={() => setEditingLocale(isDefault ? null : locale)} | ||
| className={cn( | ||
| 'relative flex shrink-0 items-center gap-1.5 whitespace-nowrap pb-2 text-sm font-medium transition-colors', | ||
| active | ||
| ? 'text-foreground' | ||
| : 'text-muted-foreground hover:text-foreground', | ||
| )} | ||
| > | ||
| {tGlobal(`languages.${locale}`)} | ||
| {isDefault && ( | ||
| <span className="text-muted-foreground text-xs"> | ||
| ({t('agents.conversationStarters.default')}) | ||
| </span> | ||
| )} | ||
| {!isDefault && !hasLocaleContent(locale) && ( | ||
| <span className="bg-muted text-muted-foreground rounded px-1 py-0.5 text-[10px] leading-none"> | ||
| {t('agents.conversationStarters.untranslated')} | ||
| </span> | ||
| )} | ||
| {active && ( | ||
| <span className="bg-foreground absolute bottom-0 left-0 h-0.5 w-full" /> | ||
| )} | ||
| </button> | ||
| ); | ||
| })} |
There was a problem hiding this comment.
Tab buttons lack visible focus states for keyboard accessibility.
The locale tab buttons use custom styling but don't include explicit focus-visible styles. Per coding guidelines, interactive elements should have visible focus states.
♿ Proposed fix to add focus states
className={cn(
'relative flex shrink-0 items-center gap-1.5 whitespace-nowrap pb-2 text-sm font-medium transition-colors',
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
active
? 'text-foreground'
: 'text-muted-foreground hover:text-foreground',
)}As per coding guidelines: "Ensure all interactive elements are keyboard accessible and have visible focus states."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@services/platform/app/routes/dashboard/`$id/agents/$agentId/conversation-starters.tsx
around lines 234 - 267, The locale tab buttons rendered in the localeTabs map
(button with onClick calling setEditingLocale, key={locale}) lack an explicit
focus-visible style; update the className passed into cn for that button to
include keyboard-focus styles (e.g., focus:outline-none and focus-visible:ring /
focus-visible:ring-offset / focus-visible:ring-foreground with an accessible
ring color and offset) so the active/focus state is visible for keyboard users
while preserving the existing active/hover classes; ensure the focus rules apply
to the same button element that uses hasLocaleContent, tGlobal and t so keyboard
users can clearly see which tab is focused.
| export const MAX_CONVERSATION_STARTER_LENGTH = 200; | ||
|
|
||
| export const SUPPORTED_AGENT_LOCALES = ['en', 'de'] as const; | ||
| export type AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number]; |
There was a problem hiding this comment.
Remove or consume unused exported type AgentLocale (lint blocker).
Line 5 introduces an exported type that KNIP flags as unused, currently failing CI.
🔧 Minimal fix
export const SUPPORTED_AGENT_LOCALES = ['en', 'de'] as const;
-export type AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number];
+type AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export type AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number]; | |
| type AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number]; |
🧰 Tools
🪛 GitHub Actions: Lint
[error] 5-5: KNIP found unused exported type 'AgentLocale'.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@services/platform/lib/shared/constants/agents.ts` at line 5, The exported
type AgentLocale is unused and causing a lint failure; either remove the unused
export or consume it where appropriate. Locate the declaration "export type
AgentLocale = (typeof SUPPORTED_AGENT_LOCALES)[number];" and either (a) delete
the export (or the entire type line) if no code references it, or (b) replace
the unused export with an internal (non-exported) type or use AgentLocale in the
code that defines or types agent-related functions/variables (e.g., function
signatures, params, or constants referencing SUPPORTED_AGENT_LOCALES) so it is
consumed; ensure the change removes the unused-export lint error without
altering runtime behavior.
| conversationStarters: z.array(z.string().max(200)).max(4).optional(), | ||
| }); | ||
|
|
||
| export type TranslatableFields = z.infer<typeof translatableFieldsSchema>; |
There was a problem hiding this comment.
TranslatableFields export is currently unused and breaking lint.
Line 25 exports a type KNIP marks as unused; this is currently a failing check.
🔧 Minimal fix
-export type TranslatableFields = z.infer<typeof translatableFieldsSchema>;
+type TranslatableFields = z.infer<typeof translatableFieldsSchema>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export type TranslatableFields = z.infer<typeof translatableFieldsSchema>; | |
| type TranslatableFields = z.infer<typeof translatableFieldsSchema>; |
🧰 Tools
🪛 GitHub Actions: Lint
[error] 25-25: KNIP found unused exported type 'TranslatableFields'.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@services/platform/lib/shared/schemas/agents.ts` at line 25, Remove the unused
exported type TranslatableFields to satisfy the linter: delete the line
exporting TranslatableFields (which references translatableFieldsSchema) or, if
the type will be needed internally only, convert it to a non-exported type
(e.g., change "export type TranslatableFields" to "type TranslatableFields") so
the symbol is no longer exported and the unused-export lint error is resolved.
- Refactor translateFields to use generateObject with Zod schema and 3-retry loop instead of fragile generateText + JSON.parse - Make translateFields generic (Record<string, string | string[]>) so it can translate any combination of agent fields in one call - Extract duplicated parseDefaultLocale into shared utility - Fix race condition: disable locale tabs during auto-translation and guard against stale locale after async completion - Add console.error to catch block per project coding standards - Fix broken tests by adding mocks for useTranslation and useOrganization - Add i18n locale resolution test cases and resolveAgentLocale unit tests
Summary
i18nkey to the agent JSON schema for per-locale overrides ofdisplayName,description, andconversationStartersde) translations in the examplechat-agent.jsonTest plan
i18nfield and rejects malformed entriesSummary by CodeRabbit