From d81ed71e1dfad5a32e7129040ff7addbbd2108d3 Mon Sep 17 00:00:00 2001 From: smanylov Date: Thu, 8 Jan 2026 17:21:09 +0700 Subject: [PATCH] add declension of words --- src/app/components/StackedBarChart.tsx | 2 +- src/app/components/tanstakTable.tsx | 2 +- src/app/lib/pluralize.ts | 10 +++++ src/app/styles/pages-styles.scss | 4 ++ src/app/ui/dashboard/protection-overview.tsx | 39 ++++++++++++++++--- .../ui/marking-page/protection-summary.tsx | 34 ++++++++++++---- .../ui/my-content/my-content-info-block.tsx | 14 +++++-- .../ui/my-content/my-content-pie-chart.tsx | 2 +- src/i18n/messages/en.json | 15 +++++-- src/i18n/messages/ru.json | 21 ++++++---- 10 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 src/app/lib/pluralize.ts diff --git a/src/app/components/StackedBarChart.tsx b/src/app/components/StackedBarChart.tsx index 65283c9..2da6902 100644 --- a/src/app/components/StackedBarChart.tsx +++ b/src/app/components/StackedBarChart.tsx @@ -198,7 +198,7 @@ export const StackedBarChart = ({ data }: Files) => {
- {t('check')}: {totalChecks} + {t('checks')}: {totalChecks}
  • - {t('images')} + {t('images-few')}
  • {t('videos')} diff --git a/src/app/lib/pluralize.ts b/src/app/lib/pluralize.ts new file mode 100644 index 0000000..05fa1be --- /dev/null +++ b/src/app/lib/pluralize.ts @@ -0,0 +1,10 @@ +export const pluralize = (number: number, one: string, few: string, many: string) => { + const n = Math.abs(number) % 100; + const n1 = n % 10; + + if (n > 10 && n < 20) return many; // 11-19 + if (n1 > 1 && n1 < 5) return few; // 2-4 (кроме 12-14) + if (n1 === 1) return one; // 1 (кроме 11) + + return many; // 0, 5-9, 10-20, 25-30 и т.д. +}; \ No newline at end of file diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index 08b36d3..18f9eee 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -96,6 +96,10 @@ .protection-stat-label { text-align: start; + + &::first-letter { + text-transform: uppercase; + } } } diff --git a/src/app/ui/dashboard/protection-overview.tsx b/src/app/ui/dashboard/protection-overview.tsx index 74213d0..6ae0c98 100644 --- a/src/app/ui/dashboard/protection-overview.tsx +++ b/src/app/ui/dashboard/protection-overview.tsx @@ -8,6 +8,7 @@ import { useClickOutside } from '@/app/hooks/useClickOutside'; import { useQuery } from '@tanstack/react-query'; import { getUserFilesInfo } from '@/app/actions/action'; import { convertBytes } from '@/app/lib/convertBytes'; +import { pluralize } from '@/app/lib/pluralize'; type FilesInfo = { totalSize: number; @@ -86,7 +87,7 @@ export default function ProtectionOverview() { useEffect(() => { setFilesCount([ { - name: 'images', + name: 'images-few', value: filesInfo?.imageSize ? filesInfo?.imageSize : 0, color: '#f08c00' }, @@ -103,6 +104,24 @@ export default function ProtectionOverview() { ]) }, [filesInfo]); + const pluralizeFiles = (number: number) => { + const translate = [t('file'), t('files-few'), t('files')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const pluralizeCheks = (number: number) => { + const translate = [t('check'), t('checks-few'), t('checks')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const pluralizeViolations = (number: number) => { + const translate = [t('violation'), t('violations-few'), t('violations')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const CHECKS = 6; + const VIOLATIONS = 2; + return (

    {t('protecting-your-content')}

    @@ -116,13 +135,21 @@ export default function ProtectionOverview() {
    {filesInfo?.totalCount ? filesInfo?.totalCount : 0}
    -
    {t('total-files')}
    +
    + {pluralizeFiles(filesInfo?.totalCount || 0)} +
    -
    0
    -
    {t('check')}
    -
    0
    -
    {t('violations')}
    +
    + {CHECKS} +
    +
    + {pluralizeCheks(CHECKS || 0)} +
    +
    {VIOLATIONS}
    +
    + {pluralizeViolations(VIOLATIONS)} +
    diff --git a/src/app/ui/marking-page/protection-summary.tsx b/src/app/ui/marking-page/protection-summary.tsx index 951abfe..45f822d 100644 --- a/src/app/ui/marking-page/protection-summary.tsx +++ b/src/app/ui/marking-page/protection-summary.tsx @@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import { getUserFilesData } from '@/app/actions/fileEntity'; import { getUserFilesInfo } from '@/app/actions/action'; import { convertBytes } from '@/app/lib/convertBytes'; +import { pluralize } from '@/app/lib/pluralize'; type FilesInfo = { totalSize: number; @@ -69,6 +70,23 @@ export default function ProtectionSummary({ fileType }: { fileType: string }) { } }); + const pluralizeFilesName = (number: number) => { + const translate = [t(`${fileType}`), t(`${fileType}s-few`), t(`${fileType}s`)]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const pluralizeCheks = (number: number) => { + const translate = [t('check'), t('checks-few'), t('checks')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const pluralizeViolations = (number: number) => { + const translate = [t('violation'), t('violations-few'), t('violations')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + + const CHECKS = 6; + const VIOLATIONS = 2; return (
    @@ -78,16 +96,18 @@ export default function ProtectionSummary({ fileType }: { fileType: string }) {
    {filesInfo?.totalCount ? filesInfo?.totalCount : 0}
    - {t('total-files')} -
    - ({t(`${fileType}s`)}) + {pluralizeFilesName(filesInfo?.totalCount || 0)}
    -
    0
    -
    {t('check')}
    -
    0
    -
    {t('violations')}
    +
    {CHECKS}
    +
    + {pluralizeCheks(CHECKS)} +
    +
    {VIOLATIONS}
    +
    + {pluralizeViolations(VIOLATIONS)} +
    {filesInfo?.totalSize ? convertBytes(filesInfo?.totalSize) : 0} / 0
    diff --git a/src/app/ui/my-content/my-content-info-block.tsx b/src/app/ui/my-content/my-content-info-block.tsx index c3d5eb6..6605984 100644 --- a/src/app/ui/my-content/my-content-info-block.tsx +++ b/src/app/ui/my-content/my-content-info-block.tsx @@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import { getUserFilesInfo } from '@/app/actions/action'; import { useTranslations } from 'next-intl'; import { convertBytes } from '@/app/lib/convertBytes'; +import { pluralize } from '@/app/lib/pluralize'; export default function MyContentInfoBlock() { const t = useTranslations('Global'); @@ -67,6 +68,11 @@ export default function MyContentInfoBlock() { return result; }; + const pluralizeFiles = (number: number) => { + const translate = [t('file'), t('files-few'), t('files')]; + return pluralize(number, translate[0], translate[1], translate[2]); + }; + return (
    @@ -83,7 +89,7 @@ export default function MyContentInfoBlock() {
    - {filesInfo.images_quantity ? filesInfo.images_quantity : 0} {t('files')} + {filesInfo.images_quantity || 0} {pluralizeFiles(filesInfo.images_quantity || 0)} {filesInfo.images_size ? convertBytes(filesInfo.images_size) : 0} @@ -103,7 +109,7 @@ export default function MyContentInfoBlock() {
    - {filesInfo.videos_quantity ? filesInfo.videos_quantity : 0} {t('files')} + {filesInfo.videos_quantity || 0} {pluralizeFiles(filesInfo.videos_quantity || 0)} {filesInfo.videos_size ? convertBytes(filesInfo.videos_size) : 0} @@ -123,7 +129,7 @@ export default function MyContentInfoBlock() {
    - {filesInfo.audios_quantity ? filesInfo.audios_quantity : 0} {t('files')} + {filesInfo.audios_quantity || 0} {pluralizeFiles(filesInfo.audios_quantity || 0)} {filesInfo.audios_size ? convertBytes(filesInfo.audios_size) : 0} @@ -143,7 +149,7 @@ export default function MyContentInfoBlock() {
    - {filesInfo.documents_quantity ? filesInfo.documents_quantity : 0} {t('files')} + {filesInfo.documents_quantity || 0} {pluralizeFiles(filesInfo.documents_quantity || 0)} {filesInfo.documents_size ? convertBytes(filesInfo.documents_size) : 0} diff --git a/src/app/ui/my-content/my-content-pie-chart.tsx b/src/app/ui/my-content/my-content-pie-chart.tsx index bf0967d..d6ae2d7 100644 --- a/src/app/ui/my-content/my-content-pie-chart.tsx +++ b/src/app/ui/my-content/my-content-pie-chart.tsx @@ -81,7 +81,7 @@ export default function MyContentPieChart() { useEffect(() => { setFilesCount([ { - name: 'images', + name: 'images-few', value: filesInfo?.imageSize ? filesInfo?.imageSize : 0, color: '#f08c00' }, diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index eeb2f91..568c643 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -10,18 +10,26 @@ "your-content": "Your content", "file": "File", "files": "Files", + "files-few": "files", "content-type": "Content type", "quantity": "Quantity", "size": "Size", + "violation": "Violation", + "violations-few": "Violations", "violations": "Violations", + "check": "Check", "checks": "Checks", - "check": "Checks", + "checks-few": "Checks", "documents": "Documents", - "videos": "Videos", "video": "video", - "audios": "Audio", + "videos": "Videos", + "videos-few": "video", "audio": "audio", + "audios": "Audios", + "audios-few": "Audios", + "image": "image", "images": "Images", + "images-few": "Images", "main": "Main", "marking": "Marking", "reports": "Reports", @@ -128,7 +136,6 @@ "drag-the-file-here": "Drag the file here", "select-files-to-protect": "Select files to protect", "upload-for-protection": "Upload {fileType} for protection", - "image": "image", "file-has-no-extension": "File has no extension", "have-unsaved-changes": "You have unsaved changes. Are you sure you want to delete this page?", "image-size": "Image size", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index c27d442..5e2b027 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -10,18 +10,26 @@ "your-content": "Ваш контент", "file": "Файл", "files": "Файлов", + "files-few": "Файла", "content-type": "Тип контента", "quantity": "Количество", "size": "Размер", - "violations": "Нарушения", - "checks": "Проверки", - "check": "Проверок", + "violation": "Нарушениe", + "violations-few": "Нарушения", + "violations": "Нарушений", + "check": "Проверка", + "checks": "Проверок", + "checks-few": "Проверки", "documents": "Документы", - "videos": "Видео", "video": "видео", - "audios": "Аудио", + "videos": "Видео", + "videos-few": "Видео", "audio": "аудио", - "images": "Изображения", + "audios": "Аудио", + "audios-few": "Аудио", + "image": "изображение", + "images": "Изображений", + "images-few": "Изображения", "main": "Главная", "marking": "Маркировка", "reports": "Отчёты", @@ -128,7 +136,6 @@ "drag-the-file-here": "Перетащите файл сюда", "select-files-to-protect": "Выбрать файлы для защиты", "upload-for-protection": "Загрузить {fileType} для защиты", - "image": "изображение", "file-has-no-extension": "Файл не имеет расширения", "have-unsaved-changes": "У вас есть несохраненные изменения. Вы уверены, что хотите покинуть страницу?", "image-size": "Размер изображения",