fix: 修复新 UI 语言与文案显示问题 (#4876)

* chore(dev): add local setup state reset target

- add a reset-setup make target to clear setup records, root users, and related options.
- support both docker dev PostgreSQL and local SQLite development databases.
- restart the docker dev backend so setup status is recalculated after reset.

* fix(chat): prevent preset menu text overflow

- add truncation layout for chat preset names to keep long labels inside the sidebar menu.
- prevent loading and external-link icons from shrinking in constrained menu rows.

* fix(i18n): translate dashboard granularity options

- call t() for granularity option labels in dashboard system settings.
- keep localized text consistent between the select trigger and dropdown items.

* chore(dev): add backend dev service rebuild target

- add a dev-api-rebuild make target to rebuild and start the docker backend service.
- reuse DEV_COMPOSE_FILE and DEV_BACKEND_SERVICE variables to avoid repeated compose config literals.

* fix(i18n): align interface language option labels

- add shared interface language options to keep display names consistent.
- reuse the shared options in the header switcher and profile preferences.
- normalize language codes so zh-CN and zh_CN resolve to Simplified Chinese.

* fix(i18n): add missing frontend translation keys

- route channel key prompts, form validation messages, and channel fallback text through i18n.
- add missing translations across six locales for channels, rankings, billing, and logs.
- update i18n sync reports so literal t() keys are present in the base locale.
This commit is contained in:
QuentinHsu
2026-05-17 11:45:27 +08:00
committed by GitHub
parent 68830e6097
commit f69ceb6967
22 changed files with 527 additions and 257 deletions
+10 -11
View File
@@ -17,6 +17,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useCallback } from 'react'
import {
INTERFACE_LANGUAGE_OPTIONS,
normalizeInterfaceLanguage,
} from '@/i18n/languages'
import { Languages, Check } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useAuthStore } from '@/stores/auth-store'
@@ -30,18 +34,10 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
const languages = [
{ code: 'en', label: 'English' },
{ code: 'zh', label: '中文' },
{ code: 'fr', label: 'Français' },
{ code: 'ru', label: 'Русский' },
{ code: 'ja', label: '日本語' },
{ code: 'vi', label: 'Tiếng Việt' },
]
export function LanguageSwitcher() {
const { i18n, t } = useTranslation()
const user = useAuthStore((s) => s.auth.user)
const currentLanguage = normalizeInterfaceLanguage(i18n.language)
const handleChangeLanguage = useCallback(
async (code: string) => {
@@ -66,7 +62,7 @@ export function LanguageSwitcher() {
<span className='sr-only'>{t('Change language')}</span>
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
{languages.map((lang) => (
{INTERFACE_LANGUAGE_OPTIONS.map((lang) => (
<DropdownMenuItem
key={lang.code}
onClick={() => handleChangeLanguage(lang.code)}
@@ -74,7 +70,10 @@ export function LanguageSwitcher() {
{lang.label}
<Check
size={14}
className={cn('ms-auto', i18n.language !== lang.code && 'hidden')}
className={cn(
'ms-auto',
currentLanguage !== lang.code && 'hidden'
)}
/>
</DropdownMenuItem>
))}
@@ -79,7 +79,9 @@ function ChatMenuItem({
/>
}
>
<span>{preset.name}</span>
<span className='min-w-0 flex-1 truncate whitespace-nowrap'>
{preset.name}
</span>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
)
@@ -95,11 +97,13 @@ function ChatMenuItem({
isActive={false}
className='justify-between'
>
<span>{preset.name}</span>
<span className='min-w-0 flex-1 truncate whitespace-nowrap'>
{preset.name}
</span>
{loading ? (
<Loader2 className='h-4 w-4 animate-spin' />
<Loader2 className='h-4 w-4 shrink-0 animate-spin' />
) : (
<ExternalLink className='h-4 w-4' />
<ExternalLink className='h-4 w-4 shrink-0' />
)}
</SidebarMenuSubButton>
</SidebarMenuSubItem>
+5 -1
View File
@@ -27,6 +27,7 @@ import {
type FieldValues,
} from 'react-hook-form'
import { useRender } from '@base-ui/react/use-render'
import { useTranslation } from 'react-i18next'
import { cn } from '@/lib/utils'
import { Label } from '@/components/ui/label'
@@ -153,12 +154,15 @@ function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
const { error, formMessageId } = useFormField()
const { t } = useTranslation()
const body = error ? String(error?.message ?? '') : props.children
if (!body) {
return null
}
const translatedBody = typeof body === 'string' ? t(body) : body
return (
<p
data-slot='form-message'
@@ -166,7 +170,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
className={cn('text-destructive text-sm', className)}
{...props}
>
{body}
{translatedBody}
</p>
)
}