diff --git a/frontend/src/features/account/api.ts b/frontend/src/features/account/api.ts index a3f9d47..48dbc04 100644 --- a/frontend/src/features/account/api.ts +++ b/frontend/src/features/account/api.ts @@ -1,4 +1,5 @@ import { api } from '../../shared/api/client' +import type { TFunction } from 'i18next' export type DeviceLink = { id: string; linked_at: string; last_seen: string | null } @@ -6,9 +7,9 @@ const ALLOWED_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']) const MAX_BYTES = 5 * 1024 * 1024 /** Client-side avatar checks so a bad file never reaches the server. */ -export function checkAvatarFile(file: File): string | undefined { - if (!ALLOWED_TYPES.has(file.type)) return 'Подойдёт PNG, JPG или WebP.' - if (file.size > MAX_BYTES) return 'Файл больше 5 МБ.' +export function checkAvatarFile(file: File, t: TFunction<'account'>): string | undefined { + if (!ALLOWED_TYPES.has(file.type)) return t('validation.avatarType') + if (file.size > MAX_BYTES) return t('validation.avatarTooBig') return undefined } diff --git a/frontend/src/features/account/components/AvatarUpload.tsx b/frontend/src/features/account/components/AvatarUpload.tsx index 7b9e51a..5094641 100644 --- a/frontend/src/features/account/components/AvatarUpload.tsx +++ b/frontend/src/features/account/components/AvatarUpload.tsx @@ -19,7 +19,7 @@ export function AvatarUpload() { const file = e.target.files?.[0] e.target.value = '' if (!file) return - const problem = checkAvatarFile(file) + const problem = checkAvatarFile(file, t) if (problem) { setError(problem) return diff --git a/frontend/src/features/account/i18n/en.ts b/frontend/src/features/account/i18n/en.ts index 76249f1..284e422 100644 --- a/frontend/src/features/account/i18n/en.ts +++ b/frontend/src/features/account/i18n/en.ts @@ -9,6 +9,10 @@ export const accountEn = { avatar: { change: 'Change avatar', }, + validation: { + avatarType: 'PNG, JPG or WebP please.', + avatarTooBig: 'The file is over 5 MB.', + }, devices: { heading: 'Linked games', linkedAt: 'Linked {{date}}', diff --git a/frontend/src/features/account/i18n/ru.ts b/frontend/src/features/account/i18n/ru.ts index 05e775a..a11d2bb 100644 --- a/frontend/src/features/account/i18n/ru.ts +++ b/frontend/src/features/account/i18n/ru.ts @@ -6,6 +6,10 @@ export const accountRu = { avatar: { change: 'Сменить аватар', }, + validation: { + avatarType: 'Подойдёт PNG, JPG или WebP.', + avatarTooBig: 'Файл больше 5 МБ.', + }, devices: { heading: 'Привязанные игры', linkedAt: 'Привязано {{date}}', diff --git a/frontend/src/features/account/tests/account.test.tsx b/frontend/src/features/account/tests/account.test.tsx index c8e88de..4b95e02 100644 --- a/frontend/src/features/account/tests/account.test.tsx +++ b/frontend/src/features/account/tests/account.test.tsx @@ -1,5 +1,6 @@ import { screen, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' +import { getI18n } from 'react-i18next' import { expect, test } from 'vitest' import { checkAvatarFile } from '../api' import { DeviceList } from '../components/DeviceList' @@ -7,10 +8,11 @@ import { renderApp } from '../../../test/render' import { json, mockFetch } from '../../../test/fetch' test('avatar file checks', () => { - expect(checkAvatarFile(new File(['x'], 'a.gif', { type: 'image/gif' }))).toBe('Подойдёт PNG, JPG или WebP.') + const t = getI18n().getFixedT('ru', 'account') + expect(checkAvatarFile(new File(['x'], 'a.gif', { type: 'image/gif' }), t)).toBe('Подойдёт PNG, JPG или WebP.') const big = new File([new Uint8Array(5 * 1024 * 1024 + 1)], 'a.png', { type: 'image/png' }) - expect(checkAvatarFile(big)).toBe('Файл больше 5 МБ.') - expect(checkAvatarFile(new File(['x'], 'a.png', { type: 'image/png' }))).toBeUndefined() + expect(checkAvatarFile(big, t)).toBe('Файл больше 5 МБ.') + expect(checkAvatarFile(new File(['x'], 'a.png', { type: 'image/png' }), t)).toBeUndefined() }) test('revoking a device asks for confirmation first', async () => {