Add a Bun script to apply and normalize AGPL copyright headers across the default frontend source files. The script keeps headers idempotent, upgrades existing headers to the 2023-2026 QuantumNous range, and is exposed through `bun run copyright` for future maintenance.
85 lines
2.6 KiB
TypeScript
Vendored
85 lines
2.6 KiB
TypeScript
Vendored
/*
|
|
Copyright (C) 2023-2026 QuantumNous
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
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 { Languages, Check } from 'lucide-react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { useAuthStore } from '@/stores/auth-store'
|
|
import { api } from '@/lib/api'
|
|
import { cn } from '@/lib/utils'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
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 handleChangeLanguage = useCallback(
|
|
async (code: string) => {
|
|
await i18n.changeLanguage(code)
|
|
if (user) {
|
|
try {
|
|
await api.put('/api/user/self', { language: code })
|
|
} catch {
|
|
// Best-effort persistence; don't block the UI on failure
|
|
}
|
|
}
|
|
},
|
|
[i18n, user]
|
|
)
|
|
|
|
return (
|
|
<DropdownMenu modal={false}>
|
|
<DropdownMenuTrigger
|
|
render={<Button variant='ghost' size='icon' className='h-9 w-9' />}
|
|
>
|
|
<Languages className='size-[1.2rem]' />
|
|
<span className='sr-only'>{t('Change language')}</span>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align='end'>
|
|
{languages.map((lang) => (
|
|
<DropdownMenuItem
|
|
key={lang.code}
|
|
onClick={() => handleChangeLanguage(lang.code)}
|
|
>
|
|
{lang.label}
|
|
<Check
|
|
size={14}
|
|
className={cn('ms-auto', i18n.language !== lang.code && 'hidden')}
|
|
/>
|
|
</DropdownMenuItem>
|
|
))}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|