Add i18n foundation and translations (#1027)
* feat: add i18n foundation * feat: localize sessions sidebar * Localize multirun/scheduled tasks and fix dialog dropdown interactions * localize git sidebar surface and add zh-CN keys * feat(ui): localize context panel, diff/plan views, and context sidebar content * fix(config): resolve user config home via fs/home before embedded home * localize header/chat UI and complete model/worktree panel strings * localize worktree + github issue/pr dialog flows * localize settings sections and split settings i18n dictionaries * localize additional settings sections and sidebars * localize more settings pages and dialogs * fix settings select trigger localization * localize tunnel settings ui surface * localize additional settings sections * localize keyboard shortcuts labels in settings * localize terminal and utility dialogs surfaces * feat(i18n): localize remaining UI strings * Add Ukrainian locale * Add Spanish locale * Add Brazilian Portuguese locale * Polish locale translations
This commit is contained in:
committed by
GitHub
parent
87db2ea210
commit
7d7285655d
@@ -10,6 +10,7 @@ import { isIMECompositionEvent } from '@/lib/ime';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useModelLists } from '@/hooks/useModelLists';
|
||||
import type { ModelMetadata } from '@/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
/** Chip height class - shared between chips and add button */
|
||||
const CHIP_HEIGHT_CLASS = 'h-7';
|
||||
@@ -110,11 +111,12 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
onRemove,
|
||||
onUpdate,
|
||||
minModels,
|
||||
addButtonLabel = 'Add model',
|
||||
addButtonLabel,
|
||||
showChips = true,
|
||||
maxModels,
|
||||
addButtonClassName,
|
||||
}) => {
|
||||
const { t } = useI18n();
|
||||
const providers = useConfigStore((state) => state.providers);
|
||||
const modelsMetadata = useConfigStore((state) => state.modelsMetadata);
|
||||
const { favoriteModelsList, recentModelsList } = useModelLists();
|
||||
@@ -334,7 +336,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<RiAddLine className="h-3.5 w-3.5 mr-1" />
|
||||
{addButtonLabel}
|
||||
{addButtonLabel ?? t('multirun.modelMultiSelect.actions.addModel')}
|
||||
</Button>
|
||||
|
||||
{isOpen && (() => {
|
||||
@@ -414,7 +416,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
<Input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
placeholder="Search models"
|
||||
placeholder={t('multirun.modelMultiSelect.search.placeholder')}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
@@ -431,7 +433,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
<div className="p-1">
|
||||
{!hasResults && (
|
||||
<div className="px-2 py-4 text-center typography-meta text-muted-foreground">
|
||||
No models found
|
||||
{t('multirun.modelMultiSelect.search.noResults')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -440,7 +442,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
<>
|
||||
<div className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider sticky top-0 z-10 -mx-1 flex items-center gap-2 border-b border-border/30 px-3 py-1.5 [background:linear-gradient(var(--surface-elevated),var(--surface-elevated)),linear-gradient(var(--surface-background),var(--surface-background))]">
|
||||
<RiStarFill className="h-4 w-4 text-primary" />
|
||||
Favorites
|
||||
{t('multirun.modelMultiSelect.sections.favorites')}
|
||||
</div>
|
||||
{filteredFavorites.map(({ model, providerID, modelID }) => {
|
||||
const idx = currentFlatIndex++;
|
||||
@@ -455,7 +457,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
{filteredFavorites.length > 0 && <div className="h-px bg-border/40 my-1" />}
|
||||
<div className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider sticky top-0 z-10 -mx-1 flex items-center gap-2 border-b border-border/30 px-3 py-1.5 [background:linear-gradient(var(--surface-elevated),var(--surface-elevated)),linear-gradient(var(--surface-background),var(--surface-background))]">
|
||||
<RiTimeLine className="h-4 w-4" />
|
||||
Recent
|
||||
{t('multirun.modelMultiSelect.sections.recent')}
|
||||
</div>
|
||||
{filteredRecents.map(({ model, providerID, modelID }) => {
|
||||
const idx = currentFlatIndex++;
|
||||
@@ -491,7 +493,7 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
|
||||
{/* Keyboard hints footer */}
|
||||
<div className="px-3 pt-1 pb-1.5 border-t border-border/40 typography-micro text-muted-foreground">
|
||||
↑↓ navigate • Enter select • Esc close
|
||||
{t('multirun.modelMultiSelect.keyboard.hint')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -544,11 +546,11 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
variantValue === DEFAULT_VARIANT_VALUE ? 'text-muted-foreground' : 'text-[color:var(--status-info)]'
|
||||
)}
|
||||
/>
|
||||
<SelectValue placeholder="Thinking" />
|
||||
<SelectValue placeholder={t('multirun.modelMultiSelect.variant.placeholder')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent fitContent>
|
||||
<SelectItem value={DEFAULT_VARIANT_VALUE} className="pr-2 [&>span:first-child]:hidden">
|
||||
Default
|
||||
{t('multirun.modelMultiSelect.variant.default')}
|
||||
</SelectItem>
|
||||
{variantKeys.map((variant) => (
|
||||
<SelectItem key={variant} value={variant} className="pr-2 [&>span:first-child]:hidden">
|
||||
@@ -568,7 +570,9 @@ export const ModelMultiSelect: React.FC<ModelMultiSelectProps> = ({
|
||||
{/* Validation hint */}
|
||||
{minModels !== undefined && selectedModels.length < minModels && (
|
||||
<p className="typography-micro text-muted-foreground">
|
||||
Select from {minModels} {maxModels !== undefined ? `to ${maxModels} models` : ''}.
|
||||
{maxModels !== undefined
|
||||
? t('multirun.modelMultiSelect.validation.minToMax', { min: minModels, max: maxModels })
|
||||
: t('multirun.modelMultiSelect.validation.minOnly', { min: minModels })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user