Merge PR #505: локализовать дефолт экрана подписки на канал

Захардкоженный английский дефолт бэкенда из 403 channel_subscription_required
перебивал переведённую blocking.channel.defaultMessage. Хелпер
customChannelMessage() отбрасывает известный дефолт и пустые значения,
кастомный текст проходит как есть. Закрывает #311. Спасибо @kewldan.
This commit is contained in:
Fringg
2026-07-24 14:08:57 +03:00
3 changed files with 55 additions and 1 deletions

View File

@@ -4,6 +4,7 @@ import { useBlockingStore } from '../../store/blocking';
import { apiClient, isChannelSubscriptionError } from '../../api/client'; import { apiClient, isChannelSubscriptionError } from '../../api/client';
import { usePlatform } from '../../platform'; import { usePlatform } from '../../platform';
import { useFocusTrap } from '../../hooks/useFocusTrap'; import { useFocusTrap } from '../../hooks/useFocusTrap';
import { customChannelMessage } from '../../utils/channelBlockingMessage';
import { TelegramIcon, ClockIcon, CheckIcon, RestartIcon } from '@/components/icons'; import { TelegramIcon, ClockIcon, CheckIcon, RestartIcon } from '@/components/icons';
import { Button } from '@/components/primitives'; import { Button } from '@/components/primitives';
import BlockingShell from './BlockingShell'; import BlockingShell from './BlockingShell';
@@ -108,7 +109,9 @@ export default function ChannelSubscriptionScreen() {
ariaLive="polite" ariaLive="polite"
icon={<TelegramIcon className="h-9 w-9" />} icon={<TelegramIcon className="h-9 w-9" />}
title={t('blocking.channel.title')} title={t('blocking.channel.title')}
description={channelInfo?.message || t('blocking.channel.defaultMessage')} description={
customChannelMessage(channelInfo?.message) ?? t('blocking.channel.defaultMessage')
}
footer={t('blocking.channel.hint')} footer={t('blocking.channel.hint')}
actions={ actions={
<Button <Button

View File

@@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest';
import { customChannelMessage } from './channelBlockingMessage';
describe('customChannelMessage', () => {
it('returns null for empty values', () => {
expect(customChannelMessage(undefined)).toBeNull();
expect(customChannelMessage(null)).toBeNull();
expect(customChannelMessage('')).toBeNull();
expect(customChannelMessage(' ')).toBeNull();
});
it('returns null for the hardcoded backend default message', () => {
expect(
customChannelMessage('Please subscribe to the required channels to continue'),
).toBeNull();
});
it('ignores surrounding whitespace when matching the backend default', () => {
expect(
customChannelMessage(' Please subscribe to the required channels to continue '),
).toBeNull();
});
it('passes through a custom message', () => {
expect(customChannelMessage('Подпишитесь на наш канал @example')).toBe(
'Подпишитесь на наш канал @example',
);
});
it('trims a custom message', () => {
expect(customChannelMessage(' custom ')).toBe('custom');
});
});

View File

@@ -0,0 +1,18 @@
/**
* The backend hardcodes this English message in the 403
* `channel_subscription_required` payload (app/cabinet/dependencies.py). It is
* not admin-configurable and not localized, so showing it verbatim leaks
* English into every locale. Treat it as "no custom message" and let the UI
* fall back to its own i18n string.
*/
const BACKEND_DEFAULT_MESSAGES = new Set(['Please subscribe to the required channels to continue']);
/**
* Returns the backend-provided channel-subscription message only when it is a
* real custom message; `null` for empty values and known backend defaults.
*/
export function customChannelMessage(message: string | null | undefined): string | null {
const trimmed = message?.trim();
if (!trimmed || BACKEND_DEFAULT_MESSAGES.has(trimmed)) return null;
return trimmed;
}