Skip to content

Commit 1a42efe

Browse files
authored
fix: localize all remaining hardcoded user-facing strings (#7)
Replace hardcoded English strings with useLocalize() calls across Sidebar, PermissionsUnavailable, ConfigRow, ArrayObjectField, ImportYamlDialog, ScopeSelector, and KeyValueField. Adds 12 new translation keys to en/translation.json and reuses existing keys where available (com_auth_title, com_config_entry_n, com_ui_true, com_ui_false, com_scope_create_error).
1 parent 5242d43 commit 1a42efe

File tree

8 files changed

+44
-26
lines changed

8 files changed

+44
-26
lines changed

src/components/Sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ export function Sidebar({ user, collapsed, onToggle }: t.SidebarProps) {
8484
>
8585
<div className="flex h-14 shrink-0 items-center px-2">
8686
<div className="flex items-center gap-2.5 overflow-hidden px-1.5">
87-
<img src={libreChatLogo} alt="LibreChat" className="h-6 w-6 shrink-0" />
87+
<img src={libreChatLogo} alt={localize('com_a11y_logo_alt')} className="h-6 w-6 shrink-0" />
8888
<span className="truncate text-sm font-semibold text-(--cui-color-text-default)">
89-
Admin Panel
89+
{localize('com_auth_title')}
9090
</span>
9191
</div>
9292
</div>

src/components/configuration/ConfigRow.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ export function ConfigRow({
5353
) : null;
5454

5555
const pendingResetHint = isPendingReset ? (
56-
<span className="text-[11px] font-medium text-(--cui-color-accent-danger)">Pending reset</span>
56+
<span className="text-[11px] font-medium text-(--cui-color-accent-danger)">
57+
{localize('com_config_pending_reset')}
58+
</span>
5759
) : null;
5860

5961
const configuredDot =

src/components/configuration/ImportYamlDialog.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,9 @@ export function ImportYamlDialog({
410410
))}
411411
{validationErrors.length > 10 && (
412412
<li className="py-0.5 opacity-70">
413-
...and {validationErrors.length - 10} more
413+
{localize('com_config_validation_more', {
414+
count: String(validationErrors.length - 10),
415+
})}
414416
</li>
415417
)}
416418
</ul>

src/components/configuration/ScopeSelector.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,10 @@ export function ScopeSelector({
123123
resetState();
124124
} catch (err) {
125125
setCreating(false);
126-
onError?.(err instanceof Error ? err.message : 'Failed to create scope');
126+
onError?.(err instanceof Error ? err.message : localize('com_scope_create_error'));
127127
}
128128
},
129-
[creating, queryClient, resetState, onError],
129+
[creating, queryClient, resetState, onError, localize],
130130
);
131131

132132
const handleCreateForGroup = useCallback(
@@ -146,10 +146,10 @@ export function ScopeSelector({
146146
resetState();
147147
} catch (err) {
148148
setCreating(false);
149-
onError?.(err instanceof Error ? err.message : 'Failed to create scope');
149+
onError?.(err instanceof Error ? err.message : localize('com_scope_create_error'));
150150
}
151151
},
152-
[creating, queryClient, resetState, onError],
152+
[creating, queryClient, resetState, onError, localize],
153153
);
154154

155155
const handleDelete = useCallback(async () => {
@@ -174,9 +174,9 @@ export function ScopeSelector({
174174
setDeleting(false);
175175
} catch (err) {
176176
setDeleting(false);
177-
onError?.(err instanceof Error ? err.message : 'Failed to delete scope');
177+
onError?.(err instanceof Error ? err.message : localize('com_scope_delete_error'));
178178
}
179-
}, [deleteTarget, deleting, queryClient, currentSelection, onSelect, onError]);
179+
}, [deleteTarget, deleting, queryClient, currentSelection, onSelect, onError, localize]);
180180

181181
const roleScopes = useMemo(
182182
() => scopes.filter((s) => s.principalType === PrincipalType.ROLE),

src/components/configuration/fields/ArrayObjectField.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { ObjectEntryCard } from './ObjectEntryCard';
44
import { AddItemButton } from '@/components/shared';
55
import { useLocalize } from '@/hooks';
66

7-
function getEntryLabel(item: t.ConfigValue, index: number): string {
7+
function getEntryLabel(item: t.ConfigValue): string | null {
88
if (item && typeof item === 'object' && !Array.isArray(item)) {
99
const obj = item as Record<string, t.ConfigValue>;
1010
if (typeof obj.name === 'string' && obj.name) return obj.name;
1111
if (typeof obj.label === 'string' && obj.label) return obj.label;
1212
if (typeof obj.group === 'string' && obj.group) return obj.group;
1313
}
14-
return `Entry ${index + 1}`;
14+
return null;
1515
}
1616

1717
export function ArrayObjectField({
@@ -107,7 +107,7 @@ export function ArrayObjectField({
107107
<ObjectEntryCard
108108
key={keys[index] ?? index}
109109
id={entryIdPrefix ? `${entryIdPrefix}-${index}` : undefined}
110-
entryKey={getEntryLabel(item, index)}
110+
entryKey={getEntryLabel(item) ?? localize('com_config_entry_n', { n: String(index + 1) })}
111111
fields={fields}
112112
value={item}
113113
onValueChange={(v) => handleEntryChange(index, v)}

src/components/configuration/fields/KeyValueField.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { AddItemButton, TrashButton } from '@/components/shared';
66
import { useLocalize } from '@/hooks';
77

88
const DEFAULT_TYPES: t.KVValueType[] = ['string', 'number', 'boolean'];
9-
const TYPE_LABELS: Record<t.KVValueType, string> = {
10-
string: 'abc',
11-
number: '123',
12-
boolean: 'T/F',
13-
json: '{ }',
9+
const TYPE_LABEL_KEYS: Record<t.KVValueType, string> = {
10+
string: 'com_kv_type_string',
11+
number: 'com_kv_type_number',
12+
boolean: 'com_kv_type_boolean',
13+
json: 'com_kv_type_json',
1414
};
1515

1616
function LocalInput({
@@ -171,8 +171,8 @@ export function KeyValueField({
171171
disabled={disabled}
172172
aria-label={valueLabel}
173173
>
174-
<Select.Item value="true">true</Select.Item>
175-
<Select.Item value="false">false</Select.Item>
174+
<Select.Item value="true">{localize('com_ui_true')}</Select.Item>
175+
<Select.Item value="false">{localize('com_ui_false')}</Select.Item>
176176
</Select>
177177
</div>
178178
) : (
@@ -195,7 +195,7 @@ export function KeyValueField({
195195
>
196196
{availableTypes.map((vt) => (
197197
<Select.Item key={vt} value={vt}>
198-
{TYPE_LABELS[vt]}
198+
{localize(TYPE_LABEL_KEYS[vt])}
199199
</Select.Item>
200200
))}
201201
</Select>
@@ -231,7 +231,7 @@ export function KeyValueField({
231231
>
232232
{availableTypes.map((vt) => (
233233
<Select.Item key={vt} value={vt}>
234-
{TYPE_LABELS[vt]}
234+
{localize(TYPE_LABEL_KEYS[vt])}
235235
</Select.Item>
236236
))}
237237
</Select>

src/components/shared/PermissionsUnavailable.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
import { useLocalize } from '@/hooks';
2+
13
export function PermissionsUnavailable() {
4+
const localize = useLocalize();
25
return (
36
<div role="alert" className="flex flex-1 flex-col items-center justify-center gap-4 p-6">
47
<h2 className="text-xl font-semibold text-(--cui-color-title-default)">
5-
Could not verify permissions
8+
{localize('com_perm_unavailable_title')}
69
</h2>
710
<p className="max-w-md text-center text-sm text-(--cui-color-text-muted)">
8-
The permissions service is temporarily unavailable. Please reload the page to try again.
11+
{localize('com_perm_unavailable_desc')}
912
</p>
1013
<button
1114
type="button"
1215
onClick={() => window.location.reload()}
1316
className="mt-2 cursor-pointer rounded-lg border border-(--cui-color-stroke-default) bg-transparent px-4 py-2 text-sm font-medium text-(--cui-color-text-default) transition-colors hover:bg-(--cui-color-background-hover)"
1417
>
15-
Reload
18+
{localize('com_ui_reload')}
1619
</button>
1720
</div>
1821
);

src/locales/en/translation.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,5 +1016,16 @@
10161016
"com_grants_assign_hint": "Roles and groups can be edited directly from the table above.",
10171017
"com_grants_assign_no_users": "No eligible users found",
10181018
"com_error_load_groups": "Failed to load groups. Please try again.",
1019-
"com_error_load_members": "Failed to load members. Please try again."
1019+
"com_error_load_members": "Failed to load members. Please try again.",
1020+
"com_perm_unavailable_title": "Could not verify permissions",
1021+
"com_perm_unavailable_desc": "The permissions service is temporarily unavailable. Please reload the page to try again.",
1022+
"com_ui_reload": "Reload",
1023+
"com_config_pending_reset": "Pending reset",
1024+
"com_config_validation_more": "...and {{count}} more",
1025+
"com_scope_delete_error": "Failed to delete configuration",
1026+
"com_kv_type_string": "abc",
1027+
"com_kv_type_number": "123",
1028+
"com_kv_type_boolean": "T/F",
1029+
"com_kv_type_json": "{ }",
1030+
"com_a11y_logo_alt": "LibreChat logo"
10201031
}

0 commit comments

Comments
 (0)