Skip to content

Commit 9ba8521

Browse files
dedyf5mudler
andauthored
feat(react-ui): localize models and fix 'Import' typo (#10341)
* feat(react-ui): localize SearchableSelect component Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> * feat(react-ui): localize ModelSelector component Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> * fix(react-ui): dynamically localize back navigation caption to match page title Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> * feat(react-ui): localize back navigation state on Models page Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> * feat(react-ui): localize ModelEditor page Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> * fix(react-ui): fix Indonesian typo 'Import' to 'Impor' in importModel locale Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> --------- Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com> Co-authored-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
1 parent 51c2319 commit 9ba8521

13 files changed

Lines changed: 136 additions & 38 deletions

File tree

core/http/react-ui/e2e/model-editor-back-nav.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ test.describe('Model Editor — Back navigation', () => {
4444
await mockEditorEndpoints(page)
4545
})
4646

47-
test('Back returns to Manage with a "Back to Manage" caption', async ({ page }) => {
47+
test('Back returns to Manage with a "Back to System" caption', async ({ page }) => {
4848
await page.goto('/app/manage')
4949
await expect(page.locator('.table')).toBeVisible({ timeout: 10_000 })
5050

@@ -55,7 +55,7 @@ test.describe('Model Editor — Back navigation', () => {
5555
await page.getByRole('menuitem', { name: 'Edit configuration' }).click()
5656

5757
await expect(page).toHaveURL(/\/app\/model-editor\//)
58-
const back = page.getByRole('button', { name: /Back to Manage/ })
58+
const back = page.getByRole('button', { name: /Back to System/ })
5959
await expect(back).toBeVisible({ timeout: 10_000 })
6060

6161
await back.click()
@@ -89,6 +89,6 @@ test.describe('Model Editor — Back navigation', () => {
8989

9090
test('falls back to "Back to Manage" on a direct visit with no origin state', async ({ page }) => {
9191
await page.goto('/app/model-editor/mock-model')
92-
await expect(page.getByRole('button', { name: /Back to Manage/ })).toBeVisible({ timeout: 10_000 })
92+
await expect(page.getByRole('button', { name: /Back to System/ })).toBeVisible({ timeout: 10_000 })
9393
})
9494
})

core/http/react-ui/public/locales/en/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
"type": "Type",
8787
"value": "Value",
8888
"search": "Search...",
89-
"selectPlaceholder": "Select an option..."
89+
"selectPlaceholder": "Select an option...",
90+
"noMatch": "No matches"
9091
},
9192
"time": {
9293
"now": "now",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"title": {
3+
"add": "Add Model",
4+
"edit": "Model Editor"
5+
},
6+
"subtitle": {
7+
"chooseModelType": "Choose a model type to get started",
8+
"newModel": "New model"
9+
},
10+
"actions": {
11+
"backTo": "Back to {{page}}",
12+
"system": "System",
13+
"templates": "Templates",
14+
"createModel": "Create Model",
15+
"saveChanges": "Save Changes",
16+
"saving": "Saving...",
17+
"saved": "Saved",
18+
"switchWarning": "Save or discard changes before switching tabs.",
19+
"discardAndSwitch": "Discard & Switch"
20+
},
21+
"tabs": {
22+
"interactive": "Interactive",
23+
"yaml": "YAML",
24+
"yamlDescription": "Edit the YAML directly. The model name must be set in the YAML for create to work."
25+
},
26+
"forms": {
27+
"modelName": {
28+
"label": "Model Name",
29+
"placeholder": "my-model-name",
30+
"hint": "Use letters, numbers, hyphens, underscores, and dots only."
31+
},
32+
"empty": {
33+
"nav": "Use the search bar above to add fields",
34+
"title": "No fields configured",
35+
"text": "Use the search bar above to find and add configuration fields."
36+
}
37+
}
38+
}

core/http/react-ui/public/locales/en/models.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"title": "Install Models",
33
"subtitle": "Browse and install AI models from the gallery",
4+
"models": "Models",
45
"stats": {
56
"available": "Available",
67
"installed": "Installed"
@@ -89,5 +90,11 @@
8990
"loadFailed": "Failed to load models: {{message}}",
9091
"installFailed": "Failed to install: {{message}}",
9192
"deleteFailed": "Failed to delete: {{message}}"
93+
},
94+
"selector": {
95+
"loading": "Loading models...",
96+
"selectModel": "Select model...",
97+
"searchPlaceholder": "Search models...",
98+
"noModels": "No models available"
9299
}
93100
}

core/http/react-ui/public/locales/id/common.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
"type": "Tipe",
8787
"value": "Nilai",
8888
"search": "Cari...",
89-
"selectPlaceholder": "Pilih opsi..."
89+
"selectPlaceholder": "Pilih opsi...",
90+
"noMatch": "Tidak ada yang cocok"
9091
},
9192
"time": {
9293
"now": "baru saja",
@@ -106,4 +107,4 @@
106107
"gigabytes": "GB",
107108
"terabytes": "TB"
108109
}
109-
}
110+
}

core/http/react-ui/public/locales/id/importModel.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"title": "Impor Model Baru",
33
"subtitle": {
4-
"simple": "Import model dari URI — deteksi otomatis memilih backend.",
4+
"simple": "Impor model dari URI — deteksi otomatis memilih backend.",
55
"powerYaml": "Tulis konfigurasi YAML lengkap untuk model.",
66
"powerPrefs": "Preferensi impor tingkat lanjut."
77
},
@@ -139,4 +139,4 @@
139139
"local": "File konfigurasi YAML lokal"
140140
}
141141
}
142-
}
142+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"title": {
3+
"add": "Tambah Model",
4+
"edit": "Editor Model"
5+
},
6+
"subtitle": {
7+
"chooseModelType": "Pilih tipe model untuk memulai",
8+
"newModel": "Model baru"
9+
},
10+
"actions": {
11+
"backTo": "Kembali ke {{page}}",
12+
"system": "Sistem",
13+
"templates": "Templat",
14+
"createModel": "Buat Model",
15+
"saveChanges": "Simpan Perubahan",
16+
"saving": "Menyimpan...",
17+
"saved": "Tersimpan",
18+
"switchWarning": "Simpan atau buang perubahan sebelum beralih tab.",
19+
"discardAndSwitch": "Buang & Beralih"
20+
},
21+
"tabs": {
22+
"interactive": "Interaktif",
23+
"yaml": "YAML",
24+
"yamlDescription": "Edit YAML secara langsung. Nama model harus diatur di YAML agar pembuatan berhasil."
25+
},
26+
"forms": {
27+
"modelName": {
28+
"label": "Nama Model",
29+
"placeholder": "nama-model-saya",
30+
"hint": "Gunakan huruf, angka, tanda hubung, garis bawah, dan titik saja."
31+
},
32+
"empty": {
33+
"nav": "Gunakan kolom pencarian di atas untuk menambahkan field",
34+
"title": "Tidak ada field yang dikonfigurasi",
35+
"text": "Gunakan kolom pencarian di atas untuk menemukan dan menambahkan field konfigurasi."
36+
}
37+
}
38+
}

core/http/react-ui/public/locales/id/models.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"title": "Instal Model",
33
"subtitle": "Telusuri dan instal model AI dari galeri",
4+
"models": "Model",
45
"stats": {
56
"available": "Tersedia",
67
"installed": "Terinstal"
@@ -89,5 +90,11 @@
8990
"loadFailed": "Gagal memuat model: {{message}}",
9091
"installFailed": "Gagal menginstal: {{message}}",
9192
"deleteFailed": "Gagal menghapus: {{message}}"
93+
},
94+
"selector": {
95+
"loading": "Memuat model...",
96+
"selectModel": "Pilih model...",
97+
"searchPlaceholder": "Cari model...",
98+
"noModels": "Model tidak tersedia"
9299
}
93-
}
100+
}

core/http/react-ui/src/components/ModelSelector.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { useEffect, useMemo } from 'react'
22
import { useModels } from '../hooks/useModels'
33
import SearchableSelect from './SearchableSelect'
4+
import { useTranslation } from 'react-i18next'
45

56
export default function ModelSelector({
67
value, onChange, capability, className = '',
78
options: externalOptions, loading: externalLoading,
89
disabled: externalDisabled, searchPlaceholder, style,
910
}) {
11+
const { t } = useTranslation('models')
1012
// Skip capability fetch when external options are provided (capability will be undefined)
1113
const { models: hookModels, loading: hookLoading } = useModels(externalOptions ? undefined : capability)
1214

@@ -28,8 +30,8 @@ export default function ModelSelector({
2830
value={value || ''}
2931
onChange={onChange}
3032
options={modelNames}
31-
placeholder={isLoading ? 'Loading models...' : (modelNames.length === 0 ? 'No models available' : 'Select model...')}
32-
searchPlaceholder={searchPlaceholder || 'Search models...'}
33+
placeholder={isLoading ? t('selector.loading') : (modelNames.length === 0 ? t('selector.noModels') : t('selector.selectModel'))}
34+
searchPlaceholder={searchPlaceholder || t('selector.searchPlaceholder')}
3335
disabled={isDisabled}
3436
className={className}
3537
style={style}

core/http/react-ui/src/components/SearchableSelect.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { useState, useEffect, useRef, useMemo } from 'react'
2+
import { useTranslation } from 'react-i18next'
23

34
export default function SearchableSelect({
45
value, onChange, options, placeholder = 'Select...',
56
allOption, searchPlaceholder = 'Search...',
67
disabled = false, style, className = '',
78
}) {
9+
const { t } = useTranslation('common')
810
const [open, setOpen] = useState(false)
911
const [query, setQuery] = useState('')
1012
const [focusIndex, setFocusIndex] = useState(-1)
@@ -226,7 +228,7 @@ export default function SearchableSelect({
226228
})}
227229
{filtered.length === 0 && !allOption && (
228230
<div style={{ padding: '6px 10px', fontSize: '0.8125rem', color: 'var(--color-text-muted)', fontStyle: 'italic' }}>
229-
No matches
231+
{t('forms.noMatch')}
230232
</div>
231233
)}
232234
</div>

0 commit comments

Comments
 (0)