itube-specs 0.0.766 → 0.0.767

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { useFormatTimeAgo } from '../use-format-time-ago';
3
+
4
+ describe('useFormatTimeAgo', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ vi.setSystemTime(new Date('2025-06-15T12:00:00Z'));
8
+ });
9
+
10
+ afterEach(() => {
11
+ vi.useRealTimers();
12
+ });
13
+
14
+ const now = () => Math.floor(Date.now() / 1000);
15
+
16
+ it('сегодня', () => {
17
+ expect(useFormatTimeAgo(now(), 'en')).toBe('today');
18
+ });
19
+
20
+ it('вчера', () => {
21
+ expect(useFormatTimeAgo(now() - 86400, 'en')).toBe('yesterday');
22
+ });
23
+
24
+ it('5 дней назад', () => {
25
+ expect(useFormatTimeAgo(now() - 5 * 86400, 'en')).toBe('5 days ago');
26
+ });
27
+
28
+ it('3 месяца назад', () => {
29
+ expect(useFormatTimeAgo(now() - 90 * 86400, 'en')).toBe('3 months ago');
30
+ });
31
+
32
+ it('2 года назад', () => {
33
+ expect(useFormatTimeAgo(now() - 730 * 86400, 'en')).toBe('2 years ago');
34
+ });
35
+
36
+ it('локализация (ru)', () => {
37
+ expect(useFormatTimeAgo(now() - 5 * 86400, 'ru')).toBe('5 дней назад');
38
+ });
39
+ });
@@ -8,14 +8,14 @@ describe('useFilterChipsItems', () => {
8
8
  it('пустой массив без filter_ в query', () => {
9
9
  const filters = ref([]);
10
10
  const route = { query: {} };
11
- const { chipsItems } = useFilterChipsItems(filters, route, t);
11
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
12
12
  expect(chipsItems.value).toEqual([]);
13
13
  });
14
14
 
15
15
  it('игнорирует не-filter параметры', () => {
16
16
  const filters = ref([]);
17
17
  const route = { query: { page: '1', sort: 'popular' } };
18
- const { chipsItems } = useFilterChipsItems(filters, route, t);
18
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
19
19
  expect(chipsItems.value).toEqual([]);
20
20
  });
21
21
 
@@ -29,7 +29,7 @@ describe('useFilterChipsItems', () => {
29
29
  ],
30
30
  }]);
31
31
  const route = { query: { filter_hair_color: 'blonde' } };
32
- const { chipsItems } = useFilterChipsItems(filters, route, t);
32
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
33
33
  expect(chipsItems.value).toHaveLength(1);
34
34
  expect(chipsItems.value[0].title).toBe('Hair color: Blonde');
35
35
  expect(chipsItems.value[0].value).toEqual(['filter_hair_color']);
@@ -45,7 +45,7 @@ describe('useFilterChipsItems', () => {
45
45
  ],
46
46
  }]);
47
47
  const route = { query: { filter_age_from: '18', filter_age_to: '30' } };
48
- const { chipsItems } = useFilterChipsItems(filters, route, t);
48
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
49
49
  expect(chipsItems.value).toHaveLength(1);
50
50
  expect(chipsItems.value[0].title).toBe('Age: 18 - 30');
51
51
  expect(chipsItems.value[0].value).toEqual(['filter_age_from', 'filter_age_to']);
@@ -61,7 +61,21 @@ describe('useFilterChipsItems', () => {
61
61
  ],
62
62
  }]);
63
63
  const route = { query: { filter_weight: '65' } };
64
- const { chipsItems } = useFilterChipsItems(filters, route, t);
64
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
65
65
  expect(chipsItems.value[0].title).toBe('Weight: 65');
66
66
  });
67
+
68
+ it('месяц → локализованное название', () => {
69
+ const filters = ref([{
70
+ name: 'birth_month',
71
+ title: 'Birth month',
72
+ options: [
73
+ { name: '1', title: '1' },
74
+ { name: '12', title: '12' },
75
+ ],
76
+ }]);
77
+ const route = { query: { filter_birth_month: '6' } };
78
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
79
+ expect(chipsItems.value[0].title).toBe('Birth month: June');
80
+ });
67
81
  });
@@ -1,26 +1,18 @@
1
1
  /**
2
- * Форматирует Unix timestamp (в секундах) в относительную строку: «сегодня», «вчера», «3 дня назад» и т.д.
3
- * @param t - функция перевода (i18n)
2
+ * Форматирует Unix timestamp (в секундах) в относительную строку в локали пользователя
3
+ * («сегодня», «вчера», «5 дней назад») через Intl.RelativeTimeFormat.
4
4
  * @param time - Unix timestamp в секундах
5
+ * @param locale - активная локаль (код или BCP-47)
5
6
  */
6
- export const useFormatTimeAgo = (t, time: number) => {
7
- const pastDate = new Date(time * 1000);
8
- const now = new Date();
7
+ export const useFormatTimeAgo = (time: number, locale: string): string => {
8
+ const diffInDays = Math.floor((Date.now() - time * 1000) / 86_400_000);
9
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
9
10
 
10
- const diffInMs = Number(now) - Number(pastDate);
11
- const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
12
-
13
- if (diffInDays === 0) {
14
- return t('date.today');
15
- } else if (diffInDays === 1) {
16
- return t('date.day_ago');
17
- } else if (diffInDays < 30) {
18
- return `${diffInDays} ${t('date.days_ago')}`;
19
- } else if (diffInDays < 365) {
20
- const diffInMonths = Math.floor(diffInDays / 30);
21
- return `${diffInMonths} ${t('date.month')}${diffInMonths > 1 ? 's' : ''}`;
22
- } else {
23
- const diffInYears = Math.floor(diffInDays / 365);
24
- return `${diffInYears} ${t('date.year')}${diffInYears > 1 ? 's' : ''}`;
11
+ if (diffInDays < 30) {
12
+ return rtf.format(-diffInDays, 'day');
13
+ }
14
+ if (diffInDays < 365) {
15
+ return rtf.format(-Math.floor(diffInDays / 30), 'month');
25
16
  }
26
- }
17
+ return rtf.format(-Math.floor(diffInDays / 365), 'year');
18
+ };
@@ -6,7 +6,8 @@ import type { Ref } from 'vue';
6
6
  export function useFilterChipsItems(
7
7
  filters: Ref<IModelFilter[]>,
8
8
  route: { query: LocationQuery },
9
- t: (key: string) => string
9
+ t: (key: string) => string,
10
+ locale: Ref<string>
10
11
  ) {
11
12
  const chipsItems = computed<IChipsItem[]>(() => {
12
13
  const queryItems = Object.keys(route.query)
@@ -60,7 +61,7 @@ export function useFilterChipsItems(
60
61
 
61
62
  if (item.includes('month')) {
62
63
  return getMonth(
63
- t,
64
+ locale.value,
64
65
  Number(value ?? defaultValue?.name) - 1
65
66
  );
66
67
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "itube-specs",
3
3
  "type": "module",
4
- "version": "0.0.766",
4
+ "version": "0.0.767",
5
5
  "main": "./nuxt.config.ts",
6
6
  "types": "./types/index.d.ts",
7
7
  "scripts": {
package/runtime/index.ts CHANGED
@@ -8,7 +8,6 @@ export * from './constants/report-forms-subjects';
8
8
  export * from './constants/playlist-step';
9
9
  export * from './constants/thumb-size';
10
10
  export * from './utils/format-date';
11
- export * from './utils/format-number';
12
11
  export * from './utils/format-time';
13
12
  export * from './utils/get-multiple-query';
14
13
  export * from './utils/get-duration';
@@ -35,7 +34,6 @@ export * from './utils/cleaners/clean-video-data';
35
34
  export * from './utils/cleaners/clean-video-card';
36
35
  export * from './utils/cleaners/clean-model-info';
37
36
  export * from './utils/video-data-add-model-icon';
38
- export * from './utils/format-time-ago';
39
37
  export * from './utils/converters/convert-categories-to-footer';
40
38
  export * from './utils/converters/convert-categories-to-chips';
41
39
  export * from './utils/converters/convert-model-card-to-chips';
@@ -1,30 +1,32 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { getMonth } from '../get-month';
3
3
 
4
- const t = (key: string) => key;
5
-
6
4
  describe('getMonth', () => {
7
5
  it('январь = 0', () => {
8
- expect(getMonth(t, 0)).toBe('january');
6
+ expect(getMonth('en', 0)).toBe('January');
9
7
  });
10
8
 
11
9
  it('декабрь = 11', () => {
12
- expect(getMonth(t, 11)).toBe('december');
10
+ expect(getMonth('en', 11)).toBe('December');
13
11
  });
14
12
 
15
13
  it('июнь = 5', () => {
16
- expect(getMonth(t, 5)).toBe('june');
14
+ expect(getMonth('en', 5)).toBe('June');
17
15
  });
18
16
 
19
17
  it('undefined → декабрь (дефолт)', () => {
20
- expect(getMonth(t)).toBe('december');
18
+ expect(getMonth('en')).toBe('December');
21
19
  });
22
20
 
23
21
  it('отрицательное число → декабрь', () => {
24
- expect(getMonth(t, -1)).toBe('december');
22
+ expect(getMonth('en', -1)).toBe('December');
25
23
  });
26
24
 
27
25
  it('число >= 12 → декабрь', () => {
28
- expect(getMonth(t, 12)).toBe('december');
26
+ expect(getMonth('en', 12)).toBe('December');
27
+ });
28
+
29
+ it('локализация (ru)', () => {
30
+ expect(getMonth('ru', 5)).toBe('июнь');
29
31
  });
30
32
  });
@@ -1,24 +1,9 @@
1
1
  /**
2
- * Возвращает локализованное название месяца по его номеру (0-based).
3
- * @param t - функция перевода (i18n)
4
- * @param number - номер месяца от 0 (январь) до 11 (декабрь)
2
+ * Возвращает локализованное название месяца по его номеру (0-based) через Intl.DateTimeFormat.
3
+ * @param locale - локаль (код или BCP-47)
4
+ * @param number - номер месяца от 0 (январь) до 11 (декабрь); вне диапазона → декабрь
5
5
  */
6
- export function getMonth(t: (key: string) => string, number?: number) {
7
- const months = [
8
- 'january',
9
- 'february',
10
- 'march',
11
- 'april',
12
- 'may',
13
- 'june',
14
- 'july',
15
- 'august',
16
- 'september',
17
- 'october',
18
- 'november',
19
- 'december'
20
- ];
6
+ export function getMonth(locale: string, number?: number): string {
21
7
  const index = typeof number === 'number' && number >= 0 && number < 12 ? number : 11;
22
- const key = months[index];
23
- return t(key) || key;
8
+ return new Intl.DateTimeFormat(locale, { month: 'long' }).format(new Date(2000, index, 1));
24
9
  }
@@ -1,24 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { formatNumber } from '../format-number';
3
-
4
- describe('formatNumber', () => {
5
- it('тысячи', () => {
6
- expect(formatNumber(10400)).toBe('10.4K');
7
- });
8
-
9
- it('миллионы', () => {
10
- expect(formatNumber(1500000)).toBe('1.5M');
11
- });
12
-
13
- it('маленькое число без сокращения', () => {
14
- expect(formatNumber(999)).toBe('999');
15
- });
16
-
17
- it('ноль', () => {
18
- expect(formatNumber(0)).toBe('0');
19
- });
20
-
21
- it('ровно тысяча', () => {
22
- expect(formatNumber(1000)).toBe('1K');
23
- });
24
- });
@@ -1,49 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { formatTimeAgo } from '../format-time-ago';
3
-
4
- const t = (key: string) => key;
5
-
6
- describe('formatTimeAgo', () => {
7
- beforeEach(() => {
8
- vi.useFakeTimers();
9
- vi.setSystemTime(new Date('2025-06-15T12:00:00Z'));
10
- });
11
-
12
- afterEach(() => {
13
- vi.useRealTimers();
14
- });
15
-
16
- const now = () => Math.floor(Date.now() / 1000);
17
-
18
- it('сегодня', () => {
19
- expect(formatTimeAgo(now(), t)).toBe('today');
20
- });
21
-
22
- it('1 день назад', () => {
23
- expect(formatTimeAgo(now() - 86400, t)).toBe('day_ago');
24
- });
25
-
26
- it('5 дней назад', () => {
27
- expect(formatTimeAgo(now() - 5 * 86400, t)).toBe('5 days_ago');
28
- });
29
-
30
- it('29 дней назад', () => {
31
- expect(formatTimeAgo(now() - 29 * 86400, t)).toBe('29 days_ago');
32
- });
33
-
34
- it('1 месяц', () => {
35
- expect(formatTimeAgo(now() - 31 * 86400, t)).toBe('1 month ago');
36
- });
37
-
38
- it('3 месяца — plural', () => {
39
- expect(formatTimeAgo(now() - 90 * 86400, t)).toBe('3 months ago');
40
- });
41
-
42
- it('1 год', () => {
43
- expect(formatTimeAgo(now() - 366 * 86400, t)).toBe('1 year ago');
44
- });
45
-
46
- it('2 года — plural', () => {
47
- expect(formatTimeAgo(now() - 730 * 86400, t)).toBe('2 years ago');
48
- });
49
- });
@@ -1,12 +0,0 @@
1
- /**
2
- * Форматирует число в короткий формат: 10400 → 10.4K.
3
- * @param number - исходное число
4
- */
5
-
6
- export function formatNumber (number: number) {
7
- return number.toLocaleString('en-US', {
8
- maximumFractionDigits: 2,
9
- notation: 'compact',
10
- compactDisplay: 'short'
11
- });
12
- };
@@ -1,26 +0,0 @@
1
- /**
2
- * Форматирует Unix timestamp (в секундах) в относительную строку: «сегодня», «вчера», «3 дня назад» и т.д.
3
- * @param date - Unix timestamp в секундах
4
- * @param t - функция перевода (i18n)
5
- */
6
- export function formatTimeAgo(date: number, t) {
7
- const pastDate = new Date(date * 1000);
8
- const now = new Date();
9
-
10
- const diffInMs = Number(now) - Number(pastDate);
11
- const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
12
-
13
- if (diffInDays === 0) {
14
- return t('today');
15
- } else if (diffInDays === 1) {
16
- return t('day_ago');
17
- } else if (diffInDays < 30) {
18
- return `${diffInDays} ${t('days_ago')}`;
19
- } else if (diffInDays < 365) {
20
- const diffInMonths = Math.floor(diffInDays / 30);
21
- return `${diffInMonths} ${t('month')}${diffInMonths > 1 ? 's' : ''} ${t('ago')}`;
22
- } else {
23
- const diffInYears = Math.floor(diffInDays / 365);
24
- return `${diffInYears} ${t('year')}${diffInYears > 1 ? 's' : ''} ${t('ago')}`;
25
- }
26
- }