Stop the Telegram paragraph rendering with spaces in Japanese and Chinese (#2187)

`telegram-link.tsx` assembles that paragraph from five separate messages
with the anchor and the QR clause in the middle, joining them with a
hardcoded space. Japanese and Chinese do not put spaces between words, so
the assembled sentence carried them mid-clause: `通过 此链接 或扫描二维码
打开 Telegram,` separated a preposition from its object and an adverbial
phrase from its verb. The separator now comes from the locale and is
empty for `ja`, `zh` and `zh-tw`. Korean keeps its spaces, because Korean
uses them, as do Thai's phrase boundaries.

The locale is matched exactly as `loadLocale` matches it. Comparing case
insensitively would have been worse than the bug: `remark_config.locale`
is forwarded verbatim and `loadLocale` is case sensitive, so a
conventional `zh-TW` loads the English catalogue, and a lowercased
comparison would then join English words with nothing between them. The
test covers that case alongside `ja` and `en`, and fails if either the
comparison loosens or the separator stops depending on the locale.

Macedonian labelled the replies feed as comments. `subscribeByRSS.replies`
carried `Коментари`, the same value as `user.comments`, in a catalogue
whose two reply strings are both `Одговори`. That option subscribes to
`/rss/reply?user=`, which `UserReplies` documents as comments replied to
that user, so the feed is replies.

`auth.user-not-found` is removed. It reached every catalogue but could not
render: the only dynamic path to it is `messages[invalidReason]`, and
`invalidReason` comes from `getTokenInvalidReason`, which returns
`expiredToken`, `invalidToken` or null, or from a backend error string,
and the backend emits nothing matching. Catalogues go from 181 keys to
180.
This commit is contained in:
Dmitry Verkhoturov
2026-08-21 19:17:49 -05:00
committed by GitHub
parent a5b2fe3cfc
commit 7c312da199
27 changed files with 39 additions and 32 deletions
@@ -17,10 +17,6 @@ export const messages = defineMessages<string>({
id: 'auth.symbols-restriction',
defaultMessage: 'Username must contain only letters, numbers, underscores or spaces',
},
userNotFound: {
id: 'auth.user-not-found',
defaultMessage: 'No user was found',
},
emailAddress: {
id: 'auth.email-address',
defaultMessage: 'Email Address',
@@ -1,7 +1,10 @@
import '@testing-library/jest-dom';
import { screen } from '@testing-library/preact';
import { screen, render as renderRaw } from '@testing-library/preact';
import { render } from 'tests/utils';
import { IntlProvider } from 'common/intl';
import en from 'locales/en.json';
import ja from 'locales/ja.json';
import { TelegramLink } from './telegram-link';
@@ -26,6 +29,29 @@ describe('<TelegramLink/>', () => {
},
});
});
it.each([
['ja', ja, ''],
['en', en, ' '],
// loadLocale matches catalogue names exactly, so an unconventional casing loads English;
// the separator has to follow the catalogue that loaded rather than the requested name
['zh-TW', en, ' '],
])('joins the paragraph fragments correctly for %s', (locale, messages, gap) => {
const m = messages as Record<string, string>;
const { container, unmount } = renderRaw(
<IntlProvider locale={locale} messages={m}>
<TelegramLink {...defaultProps} />
</IntlProvider>
);
const text = container.querySelector('p')?.textContent ?? '';
const parts = [m['auth.telegram-message-1'], m['auth.telegram-link']];
if (window.screen.width >= 768) {
parts.push(m['auth.telegram-optional-qr']);
}
parts.push(m['auth.telegram-message-2']);
expect(text).toBe(parts.join(gap) + m['auth.telegram-message-3']);
unmount();
});
it('should render text', () => {
render(<TelegramLink {...defaultProps} />);
expect(screen.getByText(/Open Telegram/)).toBeInTheDocument();
@@ -15,17 +15,26 @@ export type TelegramLinkProps = {
errorMessage?: string | null;
};
const SPACELESS_LOCALES = new Set(['ja', 'zh', 'zh-tw']);
export const TelegramLink: FunctionComponent<TelegramLinkProps> = ({ bot, token, errorMessage, onSubmit }) => {
const intl = useIntl();
const telegramLink = `https://t.me/${bot}/?start=${token}`;
// this paragraph is assembled from separate messages, and Japanese and Chinese do not put
// spaces between words, so joining their fragments with one produces spaces mid-sentence.
// matched exactly as `loadLocale` matches, so the separator can never disagree with the
// catalogue that actually loaded: an unrecognised casing falls back to English and keeps spaces
const gap = SPACELESS_LOCALES.has(intl.locale) ? '' : ' ';
return (
<>
<p className={clsx('telegram', styles.telegram)}>
{intl.formatMessage(messages.telegramMessage1)}{' '}
{intl.formatMessage(messages.telegramMessage1)}
{gap}
<a target="_blank" rel="noopener noreferrer" href={telegramLink}>
{intl.formatMessage(messages.telegramLink)}
</a>
{window.screen.width >= 768 && ` ${intl.formatMessage(messages.telegramOptionalQR)}`}{' '}
{window.screen.width >= 768 && `${gap}${intl.formatMessage(messages.telegramOptionalQR)}`}
{gap}
{intl.formatMessage(messages.telegramMessage2)}
<br />
{intl.formatMessage(messages.telegramMessage3)}
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "ثم انقر «تأكد» أسفله.",
"auth.telegram-optional-qr": "أو عبر مسح رمز QR",
"auth.telegram-qr": "رمز QR للتيليجرام",
"auth.user-not-found": "لا مستخدم بهذا الاسم",
"auth.username": "اسم المستخدم",
"authPanel.disable-comments": "عطّل التعليقات",
"authPanel.disabled-cookies": "ألغِ حظرَ كوكيز الطرف الثالث للدخول أو أنشئ تعليقاً في",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Потым націсніце «Праверыць» ніжэй.",
"auth.telegram-optional-qr": "або адсканаваўшы QR-код",
"auth.telegram-qr": "QR-код Telegram",
"auth.user-not-found": "Карыстальнік не знойдзены",
"auth.username": "Імя карыстальніка",
"authPanel.disable-comments": "Адключыць каментары",
"authPanel.disabled-cookies": "Адключыце блакаванне старонніх кукаў, каб увайсці або адкрыць каментары",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "След това натиснете „Провери“ по-долу.",
"auth.telegram-optional-qr": "или чрез сканиране на QR кода",
"auth.telegram-qr": "Telegram QR код",
"auth.user-not-found": "Не бе намерен потребител",
"auth.username": "Потребителско име",
"authPanel.disable-comments": "Забрани коментарите",
"authPanel.disabled-cookies": "Забрани бисквитки от трета страна блокиращи входа или коментарите",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Em seguida, clique em “Verificar” abaixo.",
"auth.telegram-optional-qr": "ou digitalizando o código QR",
"auth.telegram-qr": "Código QR do Telegram",
"auth.user-not-found": "Nenhum usuário encontrado",
"auth.username": "Nome do usuário",
"authPanel.disable-comments": "Desabilitar comentários",
"authPanel.disabled-cookies": "Desabilite o bloqueio de cookies de terceiros para fazer login ou abrir comentários em uma",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Poté klepněte na tlačítko „Zkontrolovat“ níže.",
"auth.telegram-optional-qr": "nebo naskenováním QR kódu",
"auth.telegram-qr": "Telegram QR kód",
"auth.user-not-found": "Nebyl nalezen žádný uživatel",
"auth.username": "Uživatelské jméno",
"authPanel.disable-comments": "Zakázat komentáře",
"authPanel.disabled-cookies": "Zakázat blokování souborů cookie třetích stran pro přihlášení nebo otevření komentářů",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Klicken Sie anschließend unten auf „Überprüfen“.",
"auth.telegram-optional-qr": "oder durch Scannen des QR-Codes",
"auth.telegram-qr": "QR-Code für Telegram",
"auth.user-not-found": "Benutzer konnte nicht gefunden werden",
"auth.username": "Benutzername",
"authPanel.disable-comments": "Kommentarfunktion deaktivieren",
"authPanel.disabled-cookies": "Deaktivieren Sie die Blockierung von Drittanbieter-Cookies, um sich anzumelden oder Kommentare im Folgenden zu öffnen:",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Afterwards, click “Check” below.",
"auth.telegram-optional-qr": "or by scanning the QR code",
"auth.telegram-qr": "Telegram QR-code",
"auth.user-not-found": "No user was found",
"auth.username": "Username",
"authPanel.disable-comments": "Disable comments",
"authPanel.disabled-cookies": "Disable third-party cookies blocking to login or open comments in",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Una vez autorizado por el bot, regresa aquí y haz clic en “Verificar”.",
"auth.telegram-optional-qr": "o escaneando el código QR",
"auth.telegram-qr": "Código QR de Telegram",
"auth.user-not-found": "No se encontró el usuario",
"auth.username": "Nombre de usuario",
"authPanel.disable-comments": "Deshabilitar comentarios",
"authPanel.disabled-cookies": "Deshabilita el bloqueo de cookies de terceros para acceder y comentar",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "سپس، روی «بررسی» زیر کلیک کنید.",
"auth.telegram-optional-qr": "یا با اسکن کد QR",
"auth.telegram-qr": "کد QR تلگرام",
"auth.user-not-found": "هیچ کاربری پیدا نشد",
"auth.username": "نام کاربری",
"authPanel.disable-comments": "غیرفعال کردن نظرات",
"authPanel.disabled-cookies": "غیرفعال کردن مسدودسازی کوکی‌های شخص ثالث برای ورود یا باز کردن نظرات",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Paina sen jälkeen alla olevaa ”Tarkista”-painiketta.",
"auth.telegram-optional-qr": "tai skannaamalla QR-koodin",
"auth.telegram-qr": "Telegramin QR-koodi",
"auth.user-not-found": "Käyttäjää ei löytynyt",
"auth.username": "Käyttäjätunnus",
"authPanel.disable-comments": "Poista kommentit käytöstä",
"authPanel.disabled-cookies": "Poista kolmansien osapuolien evästeiden estäminen käytöstä kirjautuaksesi tai kommenttien avataksesi",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Cliquez ensuite sur « Vérifier » ci-dessous.",
"auth.telegram-optional-qr": "ou en scannant le code QR",
"auth.telegram-qr": "Code QR Telegram",
"auth.user-not-found": "Aucun utilisateur n'a été trouvé",
"auth.username": "Nom d'utilisateur",
"authPanel.disable-comments": "Désactiver les commentaires",
"authPanel.disabled-cookies": "Desactiver les cookies tiers pour vous connecter ou ouvrez les commentaires dans une",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Poi premi “Verifica” qui sotto.",
"auth.telegram-optional-qr": "o scansionando il codice QR",
"auth.telegram-qr": "Codice QR di Telegram",
"auth.user-not-found": "Nessun utente trovato",
"auth.username": "Nome utente",
"authPanel.disable-comments": "Disabilita commenti",
"authPanel.disabled-cookies": "Disabilita il blocco dei cookies di terze parti per l'accesso o l'apertura di commenti in",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "その後、以下の「チェック」をクリックしてください。",
"auth.telegram-optional-qr": "または QR コードで",
"auth.telegram-qr": "Telegram QR コード",
"auth.user-not-found": "ユーザーが見つかりませんでした",
"auth.username": "ユーザー名",
"authPanel.disable-comments": "コメントの無効化",
"authPanel.disabled-cookies": "サードパーティの Cookie を無効にしてログインするか次でコメントを開いてください:",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "그 후, 아래 “확인”을 클릭하세요.",
"auth.telegram-optional-qr": "또는 QR 코드로",
"auth.telegram-qr": "텔레그램 QR 코드",
"auth.user-not-found": "사용자를 찾을 수 없음",
"auth.username": "사용자 이름",
"authPanel.disable-comments": "댓글 비활성화",
"authPanel.disabled-cookies": "로그인하거나 댓글을 열기 위해 타사 쿠키 차단을 사용 중지해야 함",
+1 -2
View File
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Потоа кликни „Провери“ подолу.",
"auth.telegram-optional-qr": "или со скенирање на QR код",
"auth.telegram-qr": "Telegram QR-код",
"auth.user-not-found": "Корисникот не е пронајден",
"auth.username": "Корисничко име",
"authPanel.disable-comments": "Оневозможи коментари",
"authPanel.disabled-cookies": "Оневозможи колачиња од трети страни кои блокираат најава или коментари",
@@ -150,7 +149,7 @@
"subscribeByEmail.subscribed": "Вие сте претплатени преку е-пошта",
"subscribeByEmail.unsubscribe": "Откажи претплата",
"subscribeByRSS.button-title": "Претплати се преку RSS",
"subscribeByRSS.replies": "Коментари",
"subscribeByRSS.replies": "Одговори",
"subscribeByRSS.site": "Страница",
"subscribeByRSS.thread": "Дискусија",
"subscribeByRSS.title": "RSS",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Następnie kliknij „Sprawdź” poniżej.",
"auth.telegram-optional-qr": "lub skanując kod QR",
"auth.telegram-qr": "Kod QR Telegrama",
"auth.user-not-found": "Żaden użytkownik nie został znaleziony",
"auth.username": "Nazwa użytkownika",
"authPanel.disable-comments": "Wyłącz komentarze",
"authPanel.disabled-cookies": "Wyłącz zewnętrzne blokowanie cookies w celu logowania lub otwierania w nich komentarzy",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Apoi apasă „Verifică” mai jos.",
"auth.telegram-optional-qr": "sau prin scanarea codului QR",
"auth.telegram-qr": "Cod QR Telegram",
"auth.user-not-found": "Nu a fost găsit niciun utilizator",
"auth.username": "Nume de utilizator",
"authPanel.disable-comments": "Dezactivează comentariile",
"authPanel.disabled-cookies": "Dezactivează blocarea cookie-urilor terțe pentru a te autentifica sau deschide comentariile în",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Затем нажмите кнопку «Проверить».",
"auth.telegram-optional-qr": "или по QR-коду",
"auth.telegram-qr": "QR-код для Telegram",
"auth.user-not-found": "Пользователь не найден",
"auth.username": "Имя пользователя",
"authPanel.disable-comments": "Отключить комментарии",
"authPanel.disabled-cookies": "Отключите блокировку сторонних файлов cookie, чтобы авторизоваться или открыть комментарии в",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "หลังจากนั้นคลิก “ตรวจสอบ” ด้านล่าง",
"auth.telegram-optional-qr": "หรือสแกน QR โค้ด",
"auth.telegram-qr": "เทเลแกรม QRCODE",
"auth.user-not-found": "ไม่พบผู้ใช้งาน",
"auth.username": "ชื่อผู้ใช้",
"authPanel.disable-comments": "ปิดใช้งานแสดงความคิดเห็น",
"authPanel.disabled-cookies": "ไม่อนุญาตให้คุ๊กกี้ของบุคคลที่สามปิดกั้นระบบแสดงความคิดเห็นค่ะ",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Ardından aşağıdaki “Kontrol et” düğmesine tıklayın.",
"auth.telegram-optional-qr": "ya da QR kodunu okutun",
"auth.telegram-qr": "Telegram QR kodu",
"auth.user-not-found": "Kullanıcı bulunamadı",
"auth.username": "Kullanıcı adı",
"authPanel.disable-comments": "Yorumları devre dışı bırak",
"authPanel.disabled-cookies": "Giriş yapabilmek ve yorum yapabilmek için çerezleri etkinleştirmeniz gerekmekte",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Потім натисніть «Перевірити» нижче.",
"auth.telegram-optional-qr": "або відсканувавши QR-код",
"auth.telegram-qr": "QR-код Telegram",
"auth.user-not-found": "Користувач не знайдений",
"auth.username": "Ім’я користувача",
"authPanel.disable-comments": "Вимкнути коментарі",
"authPanel.disabled-cookies": "Заборонені third-party cookies не дозволяють працювати коментарям",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "Sau đó, nhấn “Kiểm tra” bên dưới.",
"auth.telegram-optional-qr": "hoặc quét mã QR",
"auth.telegram-qr": "Mã QR Telegram",
"auth.user-not-found": "Không tìm thấy người dùng nào",
"auth.username": "Tên của bạn",
"authPanel.disable-comments": "Tắt bình luận",
"authPanel.disabled-cookies": "Tắt tính năng chặn cookies của bên thứ ba để đăng nhập hoặc mở nhận xét trong",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "然後,點擊下方的「驗證登入」按鈕。",
"auth.telegram-optional-qr": "或掃描 QR 碼",
"auth.telegram-qr": "Telegram QR 碼",
"auth.user-not-found": "未發現任何使用者",
"auth.username": "使用者名稱",
"authPanel.disable-comments": "禁用留言",
"authPanel.disabled-cookies": "禁用第三方 Cookie 以封鎖登入和留言功能。",
@@ -18,7 +18,6 @@
"auth.telegram-message-3": "之后,点击下方的“检查”。",
"auth.telegram-optional-qr": "或扫描二维码",
"auth.telegram-qr": "Telegram 二维码",
"auth.user-not-found": "未找到用户",
"auth.username": "用户名",
"authPanel.disable-comments": "关闭评论",
"authPanel.disabled-cookies": "禁用第三方 Cookie,以登录或在以下方面打开评论:",