🌐 refactor: Interpolate Localization Keys (#10650)

* fix: replace string concatenation of localization keys with interpolations and add keys for unlocalized string literals

* chore: update test for new localization key

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Dustin Healy 2025-11-25 12:19:49 -08:00 committed by Danny Avila
parent 39cecc97bd
commit b6dcefc53a
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
28 changed files with 130 additions and 85 deletions

View file

@ -154,9 +154,9 @@ const MarketplaceAdminSettings = () => {
</Button>
</OGDialogTrigger>
<OGDialogContent className="w-11/12 max-w-md border-border-light bg-surface-primary text-text-primary">
<OGDialogTitle>{`${localize('com_ui_admin_settings')} - ${localize(
'com_ui_marketplace',
)}`}</OGDialogTitle>
<OGDialogTitle>
{localize('com_ui_admin_settings_section', { section: localize('com_ui_marketplace') })}
</OGDialogTitle>
<div className="p-2">
{/* Role selection dropdown */}
<div className="flex items-center gap-2">

View file

@ -21,7 +21,7 @@ function MCPSelectContent() {
const renderSelectedValues = useCallback(
(values: string[], placeholder?: string) => {
if (values.length === 0) {
return placeholder || localize('com_ui_select') + '...';
return placeholder || localize('com_ui_select_placeholder');
}
if (values.length === 1) {
return values[0];

View file

@ -145,7 +145,7 @@ const EditPresetDialog = ({
<OGDialog open={presetModalVisible} onOpenChange={handleOpenChange}>
<OGDialogContent className="h-[100dvh] max-h-[100dvh] w-full max-w-full overflow-y-auto bg-white dark:border-gray-700 dark:bg-gray-850 dark:text-gray-300 md:h-auto md:max-h-[90vh] md:max-w-[75vw] md:rounded-lg lg:max-w-[950px]">
<OGDialogTitle>
{`${localize('com_ui_edit')} ${localize('com_endpoint_preset')} - ${preset?.title}`}
{localize('com_ui_edit_preset_title', { title: preset?.title })}
</OGDialogTitle>
<div className="flex w-full flex-col gap-2 px-1 pb-4 md:gap-4">

View file

@ -63,7 +63,7 @@ const PresetItems: FC<{
<button
type="button"
className="mr-1 flex h-[32px] cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-red-700 focus:ring-ring dark:bg-transparent dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-red-700"
aria-label={localize('com_ui_clear') + ' ' + localize('com_ui_all')}
aria-label={localize('com_ui_clear_all')}
>
<svg
width="24"
@ -76,12 +76,12 @@ const PresetItems: FC<{
>
<path d="M9.293 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.707A1 1 0 0 0 13.707 4L10 .293A1 1 0 0 0 9.293 0M9.5 3.5v-2l3 3h-2a1 1 0 0 1-1-1M6.854 7.146 8 8.293l1.146-1.147a.5.5 0 1 1 .708.708L8.707 9l1.147 1.146a.5.5 0 0 1-.708.708L8 9.707l-1.146 1.147a.5.5 0 0 1-.708-.708L7.293 9 6.146 7.854a.5.5 0 1 1 .708-.708"></path>
</svg>
{localize('com_ui_clear')} {localize('com_ui_all')}
{localize('com_ui_clear_all')}
</button>
</DialogTrigger>
<DialogTemplate
showCloseButton={false}
title={`${localize('com_ui_clear')} ${localize('com_endpoint_presets')}`}
title={localize('com_ui_clear_presets')}
className="max-w-[450px]"
main={
<>

View file

@ -137,7 +137,9 @@ export default function Conversation({ conversation, retainView, toggleNav }: Co
)}
role="button"
tabIndex={renaming ? -1 : 0}
aria-label={`${title || localize('com_ui_untitled')} conversation`}
aria-label={localize('com_ui_conversation_label', {
title: title || localize('com_ui_untitled'),
})}
onClick={(e) => {
if (renaming) {
return;

View file

@ -1,4 +1,5 @@
import React, { useCallback } from 'react';
import { Trans } from 'react-i18next';
import { QueryKeys } from 'librechat-data-provider';
import { useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom';
@ -80,7 +81,7 @@ export function DeleteConversationDialog({
return (
<OGDialogContent
title={localize('com_ui_delete_confirm') + ' ' + title}
title={localize('com_ui_delete_confirm', { title })}
className="w-11/12 max-w-md"
showCloseButton={false}
>
@ -88,7 +89,11 @@ export function DeleteConversationDialog({
<OGDialogTitle>{localize('com_ui_delete_conversation')}</OGDialogTitle>
</OGDialogHeader>
<div className="w-full truncate">
{localize('com_ui_delete_confirm')} <strong>{title}</strong> ?
<Trans
i18nKey="com_ui_delete_confirm_strong"
values={{ item: title }}
components={{ strong: <strong /> }}
/>
</div>
<div className="flex justify-end gap-4 pt-4">
<Button aria-label="cancel" variant="outline" onClick={() => setShowDeleteDialog(false)}>

View file

@ -137,7 +137,7 @@ const SearchBar = forwardRef((props: SearchBarProps, ref: React.Ref<HTMLDivEleme
/>
<button
type="button"
aria-label={`${localize('com_ui_clear')} ${localize('com_ui_search')}`}
aria-label={localize('com_ui_clear_search')}
className={cn(
'absolute right-[7px] flex h-5 w-5 items-center justify-center rounded-full border-none bg-transparent p-0 transition-opacity duration-200',
showClearIcon ? 'opacity-100' : 'opacity-0',

View file

@ -19,12 +19,12 @@ const ChatDirection = () => {
</div>
<Button
variant="outline"
aria-label={`${localize('com_nav_chat_direction')}: ${localize('com_ui_x_selected', {
0:
aria-label={localize('com_nav_chat_direction_selected', {
direction:
direction === 'LTR'
? localize('chat_direction_left_to_right')
: localize('chat_direction_right_to_left'),
})}`}
})}
onClick={toggleChatDirection}
data-testid="chatDirection"
>

View file

@ -251,7 +251,9 @@ export default function SharedLinks() {
onClick={() => {
window.open(`/c/${row.original.conversationId}`, '_blank');
}}
aria-label={`${localize('com_ui_view_source')} - ${row.original.title || localize('com_ui_untitled')}`}
aria-label={localize('com_ui_view_source', {
title: row.original.title || localize('com_ui_untitled'),
})}
>
<MessageSquare className="size-4" aria-hidden="true" />
</Button>
@ -262,7 +264,9 @@ export default function SharedLinks() {
setDeleteRow(row.original);
setIsDeleteOpen(true);
}}
aria-label={`${localize('com_ui_delete')} - ${row.original.title || localize('com_ui_untitled')}`}
aria-label={localize('com_ui_delete_shared_link', {
title: row.original.title || localize('com_ui_untitled'),
})}
>
<TrashIcon className="size-4" aria-hidden="true" />
</Button>

View file

@ -1,4 +1,5 @@
import { useState, useCallback, useMemo, useEffect } from 'react';
import { Trans } from 'react-i18next';
import debounce from 'lodash/debounce';
import { useRecoilValue } from 'recoil';
import { TrashIcon, ArchiveRestore, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
@ -287,12 +288,18 @@ export default function ArchivedChatsTable({
<OGDialog open={isDeleteOpen} onOpenChange={onOpenChange}>
<OGDialogContent
title={localize('com_ui_delete_confirm') + ' ' + (deleteConversation?.title ?? '')}
title={localize('com_ui_delete_confirm', {
title: deleteConversation?.title ?? localize('com_ui_untitled'),
})}
className="w-11/12 max-w-md"
>
<OGDialogHeader>
<OGDialogTitle>
{localize('com_ui_delete_confirm')} <strong>{deleteConversation?.title}</strong>
<Trans
i18nKey="com_ui_delete_confirm_strong"
values={{ title: deleteConversation?.title }}
components={{ strong: <strong /> }}
/>
</OGDialogTitle>
</OGDialogHeader>
<div className="flex justify-end gap-4 pt-4">

View file

@ -156,7 +156,7 @@ const AdminSettings = () => {
</OGDialogTrigger>
<OGDialogContent className="max-w-lg border-border-light bg-surface-primary text-text-primary lg:w-1/4">
<OGDialogTitle>
{`${localize('com_ui_admin_settings')} - ${localize('com_ui_prompts')}`}
{localize('com_ui_admin_settings_section', { section: localize('com_ui_prompts') })}
</OGDialogTitle>
<div className="p-2">
{/* Role selection dropdown */}

View file

@ -72,11 +72,7 @@ function ChatGroupItem({
<button
id={`prompt-actions-${group._id}`}
type="button"
aria-label={
localize('com_ui_sr_actions_menu', { 0: group.name }) +
' ' +
localize('com_ui_prompt')
}
aria-label={localize('com_ui_sr_actions_menu', { name: group.name })}
onClick={(e) => {
e.stopPropagation();
}}

View file

@ -114,7 +114,7 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
variant="ghost"
onClick={(e) => e.stopPropagation()}
className="h-8 w-8 p-0 hover:bg-surface-hover"
aria-label={localize('com_ui_rename_prompt') + ' ' + group.name}
aria-label={localize('com_ui_rename_prompt_name', { name: group.name })}
>
<Pen className="icon-sm text-text-primary" aria-hidden="true" />
</Button>
@ -130,7 +130,7 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
value={nameInputValue}
onChange={(e) => setNameInputValue(e.target.value)}
className="w-full"
aria-label={localize('com_ui_rename_prompt') + ' ' + group.name}
aria-label={localize('com_ui_rename_prompt_name', { name: group.name })}
/>
</div>
</div>
@ -153,7 +153,7 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
variant="ghost"
className="h-8 w-8 p-0 hover:bg-surface-hover"
onClick={(e) => e.stopPropagation()}
aria-label={localize('com_ui_delete_prompt') + ' ' + group.name}
aria-label={localize('com_ui_delete_prompt_name', { name: group.name })}
>
<TrashIcon className="icon-sm text-text-primary" aria-hidden="true" />
</Button>

View file

@ -42,10 +42,7 @@ export default function VariablesDropdown({
};
return (
<div
className={className}
title={`${localize('com_ui_add')} ${localize('com_ui_special_variables')}`}
>
<div className={className} title={localize('com_ui_add_special_variables')}>
<DropdownPopup
portal={true}
mountByState={true}
@ -56,7 +53,7 @@ export default function VariablesDropdown({
trigger={
<Menu.MenuButton
id="variables-menu-button"
aria-label={`${localize('com_ui_add')} ${localize('com_ui_special_variables')}`}
aria-label={localize('com_ui_add_special_variables')}
className="flex h-8 items-center gap-1 rounded-md border border-border-medium bg-surface-secondary px-2 py-0 text-sm text-text-primary transition-colors duration-200 hover:bg-surface-tertiary"
>
<PlusCircle className="mr-1 h-3 w-3 text-text-secondary" aria-hidden={true} />

View file

@ -166,9 +166,9 @@ const PeoplePickerAdminSettings = () => {
</Button>
</OGDialogTrigger>
<OGDialogContent className="w-full border-border-light bg-surface-primary text-text-primary lg:w-1/4">
<OGDialogTitle>{`${localize('com_ui_admin_settings')} - ${localize(
'com_ui_people_picker',
)}`}</OGDialogTitle>
<OGDialogTitle>
{localize('com_ui_admin_settings_section', { section: localize('com_ui_people_picker') })}
</OGDialogTitle>
<div className="p-2">
{/* Role selection dropdown */}
<div className="flex items-center gap-2">

View file

@ -160,9 +160,9 @@ const AdminSettings = () => {
</Button>
</OGDialogTrigger>
<OGDialogContent className="border-border-light bg-surface-primary text-text-primary lg:w-1/4">
<OGDialogTitle>{`${localize('com_ui_admin_settings')} - ${localize(
'com_ui_agents',
)}`}</OGDialogTitle>
<OGDialogTitle>
{localize('com_ui_admin_settings_section', { section: localize('com_ui_agents') })}
</OGDialogTitle>
<div className="p-2">
{/* Role selection dropdown */}
<div className="flex items-center gap-2">

View file

@ -315,9 +315,18 @@ export default function AgentConfig() {
{/* Agent Tools & Actions */}
<div className="mb-4">
<label className={labelClass}>
{`${toolsEnabled === true ? localize('com_ui_tools') : ''}
${toolsEnabled === true && actionsEnabled === true ? ' + ' : ''}
${actionsEnabled === true ? localize('com_assistants_actions') : ''}`}
{(() => {
if (toolsEnabled === true && actionsEnabled === true) {
return localize('com_ui_tools_and_actions');
}
if (toolsEnabled === true) {
return localize('com_ui_tools');
}
if (actionsEnabled === true) {
return localize('com_assistants_actions');
}
return '';
})()}
</label>
<div>
<div className="mb-1">

View file

@ -335,7 +335,8 @@ describe('AgentPanel - Update Agent Toast Messages', () => {
await waitFor(() => {
expect(mockShowToast).toHaveBeenCalledWith({
message: 'com_assistants_update_success Test Agent',
message: 'com_assistants_update_success_name',
status: undefined,
});
});
});
@ -355,7 +356,8 @@ describe('AgentPanel - Update Agent Toast Messages', () => {
await waitFor(() => {
expect(mockShowToast).toHaveBeenCalledWith({
message: 'com_assistants_update_success com_ui_agent',
message: 'com_assistants_update_success_name',
status: undefined,
});
});
});
@ -375,7 +377,8 @@ describe('AgentPanel - Update Agent Toast Messages', () => {
await waitFor(() => {
expect(mockShowToast).toHaveBeenCalledWith({
message: 'com_assistants_update_success Test Agent',
message: 'com_assistants_update_success_name',
status: undefined,
});
});
});

View file

@ -46,7 +46,7 @@ function getUpdateToastMessage(
if (noVersionChange) {
return localize('com_ui_no_changes');
}
return `${localize('com_assistants_update_success')} ${name ?? localize('com_ui_agent')}`;
return localize('com_assistants_update_success_name', { name: name ?? localize('com_ui_agent') });
}
/**
@ -504,20 +504,10 @@ export default function AgentPanel() {
setCurrentAgentId(undefined);
}}
disabled={agentQuery.isInitialLoading}
aria-label={
localize('com_ui_create') +
' ' +
localize('com_ui_new') +
' ' +
localize('com_ui_agent')
}
aria-label={localize('com_ui_create_new_agent')}
>
<Plus className="mr-1 h-4 w-4" aria-hidden="true" />
{localize('com_ui_create') +
' ' +
localize('com_ui_new') +
' ' +
localize('com_ui_agent')}
{localize('com_ui_create_new_agent')}
</Button>
<Button
variant="submit"
@ -526,7 +516,7 @@ export default function AgentPanel() {
e.preventDefault();
handleSelectAgent();
}}
aria-label={localize('com_ui_select') + ' ' + localize('com_ui_agent')}
aria-label={localize('com_ui_select_agent')}
>
{localize('com_ui_select')}
</Button>

View file

@ -181,7 +181,7 @@ export default function AgentSelect({
};
}, [selectedAgentId, agents, onSelect]);
const createAgent = localize('com_ui_create') + ' ' + localize('com_ui_agent');
const createAgent = localize('com_ui_create_new_agent');
return (
<Controller

View file

@ -87,7 +87,7 @@ export default function DeleteButton({
<Button
size="sm"
variant="outline"
aria-label={localize('com_ui_delete') + ' ' + localize('com_ui_agent')}
aria-label={localize('com_ui_delete_agent')}
type="button"
>
<div className="flex w-full items-center justify-center gap-2 text-red-500">
@ -96,7 +96,7 @@ export default function DeleteButton({
</Button>
</OGDialogTrigger>
<OGDialogTemplate
title={localize('com_ui_delete') + ' ' + localize('com_ui_agent')}
title={localize('com_ui_delete_agent')}
className="max-w-[450px]"
main={
<>

View file

@ -36,7 +36,7 @@ export default function DuplicateAgent({ agent_id }: { agent_id: string }) {
<Button
size="sm"
variant="outline"
aria-label={localize('com_ui_duplicate') + ' ' + localize('com_ui_agent')}
aria-label={localize('com_ui_duplicate_agent')}
type="button"
onClick={handleDuplicate}
>

View file

@ -263,7 +263,7 @@ export default function AssistantSelect({
};
}, [selectedAssistant, query.data, onSelect]);
const createAssistant = localize('com_ui_create') + ' ' + localize('com_ui_assistant');
const createAssistant = localize('com_ui_create_assistant');
return (
<SelectDropDown
value={!value ? createAssistant : value}

View file

@ -100,7 +100,7 @@ export default function ContextButton({
</button>
</DialogTrigger>
<DialogTemplate
title={localize('com_ui_delete') + ' ' + localize('com_ui_assistant')}
title={localize('com_ui_delete_assistant')}
className="max-w-[450px]"
main={
<>

View file

@ -194,7 +194,7 @@ function MCPPanelContent() {
variant="outline"
className="flex-1 justify-start dark:hover:bg-gray-700"
onClick={() => handleServerClickToEdit(server.serverName)}
aria-label={localize('com_ui_edit') + ' ' + server.serverName}
aria-label={localize('com_ui_edit_server', { serverName: server.serverName })}
>
<div className="flex items-center gap-2">
<span>{server.serverName}</span>

View file

@ -149,9 +149,9 @@ const AdminSettings = () => {
</Button>
</OGDialogTrigger>
<OGDialogContent className="border-border-light bg-surface-primary text-text-primary lg:w-1/4">
<OGDialogTitle>{`${localize('com_ui_admin_settings')} - ${localize(
'com_ui_memories',
)}`}</OGDialogTitle>
<OGDialogTitle>
{localize('com_ui_admin_settings_section', { section: localize('com_ui_memories') })}
</OGDialogTitle>
<div className="p-2">
{/* Role selection dropdown */}
<div className="flex items-center gap-2">

View file

@ -99,6 +99,7 @@
"com_agents_start_chat": "Start Chat",
"com_agents_top_picks": "Top Picks",
"com_agents_update_error": "There was an error updating your agent.",
"com_agents_avatar_upload_error": "Failed to upload agent avatar",
"com_assistants_action_attempt": "Assistant wants to talk to {{0}}",
"com_assistants_actions": "Actions",
"com_assistants_actions_disabled": "You need to create an assistant before adding actions.",
@ -144,6 +145,7 @@
"com_assistants_update_actions_success": "Successfully created or updated Action",
"com_assistants_update_error": "There was an error updating your assistant.",
"com_assistants_update_success": "Successfully updated",
"com_assistants_update_success_name": "Successfully updated {{name}}",
"com_auth_already_have_account": "Already have an account?",
"com_auth_apple_login": "Sign in with Apple",
"com_auth_back_to_login": "Back to Login",
@ -441,6 +443,7 @@
"com_nav_chat_commands": "Chat Commands",
"com_nav_chat_commands_info": "These commands are activated by typing specific characters at the beginning of your message. Each command is triggered by its designated prefix. You can disable them if you frequently use these characters to start messages.",
"com_nav_chat_direction": "Chat direction",
"com_nav_chat_direction_selected": "Chat direction: {{direction}}",
"com_nav_clear_all_chats": "Clear all chats",
"com_nav_clear_cache_confirm_message": "Are you sure you want to clear the cache?",
"com_nav_clear_conversation": "Clear conversations",
@ -639,10 +642,13 @@
"com_ui_add_mcp_server": "Add MCP Server",
"com_ui_add_model_preset": "Add a model or preset for an additional response",
"com_ui_add_multi_conversation": "Add multi-conversation",
"com_ui_add_special_variables": "Add Special Variables",
"com_ui_adding_details": "Adding details",
"com_ui_additional_details": "Additional Details",
"com_ui_admin": "Admin",
"com_ui_admin_access_warning": "Disabling Admin access to this feature may cause unexpected UI issues requiring refresh. If saved, the only way to revert is via the interface setting in librechat.yaml config which affects all roles.",
"com_ui_admin_settings": "Admin Settings",
"com_ui_admin_settings_section": "Admin Settings - {{section}}",
"com_ui_advanced": "Advanced",
"com_ui_advanced_settings": "Advanced Settings",
"com_ui_agent": "Agent",
@ -773,8 +779,12 @@
"com_ui_change_version": "Change Version",
"com_ui_chat": "Chat",
"com_ui_chat_history": "Chat History",
"com_ui_check_internet": "Check your internet connection",
"com_ui_clear": "Clear",
"com_ui_clear_all": "Clear all",
"com_ui_clear_browser_cache": "Clear your browser cache",
"com_ui_clear_presets": "Clear Presets",
"com_ui_clear_search": "Clear search",
"com_ui_click_to_close": "Click to close",
"com_ui_client_id": "Client ID",
"com_ui_client_secret": "Client Secret",
@ -799,10 +809,13 @@
"com_ui_continue_oauth": "Continue with OAuth",
"com_ui_control_bar": "Control bar",
"com_ui_controls": "Controls",
"com_ui_contact_admin_if_issue_persists": "Contact the Admin if the issue persists",
"com_ui_conversation_label": "{{title}} conversation",
"com_ui_convo_delete_error": "Failed to delete conversation",
"com_ui_convo_delete_success": "Conversation successfully deleted",
"com_ui_copied": "Copied!",
"com_ui_copied_to_clipboard": "Copied to clipboard",
"com_ui_copy": "Copy",
"com_ui_copy_code": "Copy code",
"com_ui_copy_link": "Copy link",
"com_ui_copy_stack_trace": "Copy stack trace",
@ -810,8 +823,10 @@
"com_ui_copy_to_clipboard": "Copy to clipboard",
"com_ui_copy_url_to_clipboard": "Copy URL to clipboard",
"com_ui_create": "Create",
"com_ui_create_assistant": "Create Assistant",
"com_ui_create_link": "Create link",
"com_ui_create_memory": "Create Memory",
"com_ui_create_new_agent": "Create New Agent",
"com_ui_create_prompt": "Create Prompt",
"com_ui_creating_image": "Creating image. May take a moment",
"com_ui_current": "Current",
@ -842,9 +857,12 @@
"com_ui_delete": "Delete",
"com_ui_delete_action": "Delete Action",
"com_ui_delete_action_confirm": "Are you sure you want to delete this action?",
"com_ui_delete_agent": "Delete Agent",
"com_ui_delete_agent_confirm": "Are you sure you want to delete this agent?",
"com_ui_delete_assistant": "Delete Assistant",
"com_ui_delete_assistant_confirm": "Are you sure you want to delete this Assistant? This cannot be undone.",
"com_ui_delete_confirm": "This will delete",
"com_ui_delete_confirm": "This will delete {{title}}",
"com_ui_delete_confirm_strong": "This will delete <strong>{{title}}</strong>",
"com_ui_delete_confirm_prompt_version_var": "This will delete the selected version for \"{{0}}.\" If no other versions exist, the prompt will be deleted.",
"com_ui_delete_conversation": "Delete chat?",
"com_ui_delete_mcp": "Delete MCP",
@ -854,7 +872,8 @@
"com_ui_delete_memory": "Delete Memory",
"com_ui_delete_not_allowed": "Delete operation is not allowed",
"com_ui_delete_prompt": "Delete Prompt?",
"com_ui_delete_shared_link": "Delete shared link?",
"com_ui_delete_shared_link": "Delete Shared Link - {{title}}",
"com_ui_delete_prompt_name": "Delete Prompt - {{name}}",
"com_ui_delete_success": "Successfully deleted",
"com_ui_delete_tool": "Delete Tool",
"com_ui_delete_tool_confirm": "Are you sure you want to delete this tool?",
@ -877,6 +896,7 @@
"com_ui_dropdown_variables": "Dropdown variables:",
"com_ui_dropdown_variables_info": "Create custom dropdown menus for your prompts: `{{variable_name:option1|option2|option3}}`",
"com_ui_duplicate": "Duplicate",
"com_ui_duplicate_agent": "Duplicate Agent",
"com_ui_duplication_error": "There was an error duplicating the conversation",
"com_ui_duplication_processing": "Duplicating conversation...",
"com_ui_duplication_success": "Successfully duplicated conversation",
@ -884,6 +904,8 @@
"com_ui_edit_editing_image": "Editing image",
"com_ui_edit_mcp_server": "Edit MCP Server",
"com_ui_edit_memory": "Edit Memory",
"com_ui_edit_preset_title": "Edit Preset - {{title}}",
"com_ui_edit_server": "Edit {{serverName}}",
"com_ui_edit_prompt_page": "Edit Prompt Page",
"com_ui_editable_message": "Editable Message",
"com_ui_editor_instructions": "Drag the image to reposition • Use zoom slider or buttons to adjust size",
@ -897,7 +919,10 @@
"com_ui_enter_value": "Enter value",
"com_ui_error": "Error",
"com_ui_error_connection": "Error connecting to server, try refreshing the page.",
"com_ui_error_message_prefix": "Error Message:",
"com_ui_error_save_admin_settings": "There was an error saving your admin settings.",
"com_ui_error_try_following_prefix": "Please try one of the following",
"com_ui_error_unexpected": "Oops! Something Unexpected Occurred",
"com_ui_error_updating_preferences": "Error updating preferences",
"com_ui_everyone_permission_level": "Everyone's permission level",
"com_ui_examples": "Examples",
@ -1135,6 +1160,7 @@
"com_ui_rename_conversation": "Rename Conversation",
"com_ui_rename_failed": "Failed to rename conversation",
"com_ui_rename_prompt": "Rename Prompt",
"com_ui_rename_prompt_name": "Rename Prompt - {{name}}",
"com_ui_requires_auth": "Requires Authentication",
"com_ui_reset": "Reset",
"com_ui_reset_adjustments": "Reset adjustments",
@ -1186,11 +1212,13 @@
"com_ui_seconds": "seconds",
"com_ui_secret_key": "Secret Key",
"com_ui_select": "Select",
"com_ui_select_agent": "Select Agent",
"com_ui_select_all": "Select All",
"com_ui_select_file": "Select a file",
"com_ui_select_model": "Select a model",
"com_ui_select_options": "Select options...",
"com_ui_select_or_create_prompt": "Select or Create a Prompt",
"com_ui_select_placeholder": "Select...",
"com_ui_select_provider": "Select a provider",
"com_ui_select_provider_first": "Select a provider first",
"com_ui_select_region": "Select a region",
@ -1228,7 +1256,9 @@
"com_ui_special_variables": "Special variables:",
"com_ui_special_variables_more_info": "You can select special variables from the dropdown: `{{current_date}}` (today's date and day of week), `{{current_datetime}}` (local date and time), `{{utc_iso_datetime}}` (UTC ISO datetime), and `{{current_user}}` (your account name).",
"com_ui_speech_while_submitting": "Can't submit speech while a response is being generated",
"com_ui_sr_actions_menu": "Open actions menu for \"{{0}}\"",
"com_ui_sr_actions_menu": "Open actions menu for \"{{0}}\" prompt",
"com_ui_stack_trace": "Stack Trace",
"com_ui_status_prefix": "Status:",
"com_ui_stop": "Stop",
"com_ui_storage": "Storage",
"com_ui_submit": "Submit",
@ -1254,6 +1284,7 @@
"com_ui_tool_info": "Tool Information",
"com_ui_tool_more_info": "More information about this tool",
"com_ui_tools": "Tools",
"com_ui_tools_and_actions": "Tools and Actions",
"com_ui_transferred_to": "Transferred to",
"com_ui_travel": "Travel",
"com_ui_trust_app": "I trust this application",
@ -1301,7 +1332,7 @@
"com_ui_version_var": "Version {{0}}",
"com_ui_versions": "Versions",
"com_ui_view_memory": "View Memory",
"com_ui_view_source": "View source chat",
"com_ui_view_source": "View source chat - {{title}}",
"com_ui_web_search": "Web Search",
"com_ui_web_search_cohere_key": "Enter Cohere API Key",
"com_ui_web_search_firecrawl_url": "Firecrawl API URL (optional)",

View file

@ -1,4 +1,3 @@
/* eslint-disable i18next/no-literal-string */
import { Button } from '@librechat/client';
import { useRouteError } from 'react-router-dom';
import { useLocalize } from '~/hooks';
@ -132,12 +131,12 @@ export default function RouteErrorBoundary() {
>
<div className="bg-surface-primary/60 mx-4 w-11/12 max-w-4xl rounded-2xl border border-border-light p-8 shadow-2xl backdrop-blur-xl">
<h2 className="mb-6 text-center text-3xl font-medium tracking-tight text-text-primary">
Oops! Something Unexpected Occurred
{localize('com_ui_error_unexpected')}
</h2>
{/* Error Message */}
<div className="mb-4 rounded-xl border border-red-500/20 bg-red-500/5 p-4 text-sm text-gray-600 dark:text-gray-200">
<h3 className="mb-2 font-medium">Error Message:</h3>
<h3 className="mb-2 font-medium">{localize('com_ui_error_message_prefix')}</h3>
<pre className="whitespace-pre-wrap text-sm font-light leading-relaxed text-text-primary">
{errorDetails.message}
</pre>
@ -147,7 +146,7 @@ export default function RouteErrorBoundary() {
{(typeof errorDetails.status === 'number' ||
typeof errorDetails.statusText === 'string') && (
<div className="mb-4 rounded-xl border border-yellow-500/20 bg-yellow-500/5 p-4 text-sm text-text-primary">
<h3 className="mb-2 font-medium">Status:</h3>
<h3 className="mb-2 font-medium">{localize('com_ui_status_prefix')}:</h3>
<p className="text-text-primary">
{typeof errorDetails.status === 'number' && `${errorDetails.status} `}
{typeof errorDetails.statusText === 'string' && errorDetails.statusText}
@ -159,7 +158,7 @@ export default function RouteErrorBoundary() {
{errorDetails.stack != null && errorDetails.stack.trim() !== '' && (
<details className="group mb-4 rounded-xl border border-border-light p-4">
<summary className="mb-2 flex cursor-pointer items-center justify-between text-sm font-medium text-text-primary">
<span>Stack Trace</span>
<span>{localize('com_ui_stack_trace')}</span>
<div className="flex items-center">
<Button
variant="outline"
@ -168,7 +167,7 @@ export default function RouteErrorBoundary() {
className="ml-2 px-2 py-1 text-xs"
aria-label={localize('com_ui_copy_stack_trace')}
>
Copy
{localize('com_ui_copy')}
</Button>
</div>
</summary>
@ -191,7 +190,7 @@ export default function RouteErrorBoundary() {
{errorDetails.data != null && (
<details className="group mb-4 rounded-xl border border-border-light p-4">
<summary className="mb-2 flex cursor-pointer items-center justify-between text-sm font-medium text-text-primary">
<span>Additional Details</span>
<span>{localize('com_ui_additional_details')}</span>
<span className="transition-transform group-open:rotate-90">{'>'}</span>
</summary>
<pre className="whitespace-pre-wrap text-xs font-light leading-relaxed text-text-primary">
@ -201,12 +200,14 @@ export default function RouteErrorBoundary() {
)}
<div className="mt-6 flex flex-col gap-4">
<p className="text-sm font-light text-text-secondary">Please try one of the following:</p>
<p className="text-sm font-light text-text-secondary">
{localize('com_ui_error_try_following_prefix')}:
</p>
<ul className="list-inside list-disc text-sm text-text-secondary">
<li>Refresh the page</li>
<li>Clear your browser cache</li>
<li>Check your internet connection</li>
<li>Contact the Admin if the issue persists</li>
<li>{localize('com_ui_refresh_page')}</li>
<li>{localize('com_ui_clear_browser_cache')}</li>
<li>{localize('com_ui_check_internet')}</li>
<li>{localize('com_ui_contact_admin_if_issue_persists')}</li>
</ul>
<div className="mt-4 flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
<Button