diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md
index 36c1c57..f5193e6 100644
--- a/docs/FUNCTIONAL.md
+++ b/docs/FUNCTIONAL.md
@@ -111,7 +111,9 @@ an existing word (down a column or across a row) is accepted. A play is validate
against the game's dictionary at submit time and scored; an unlimited preview
reports the word(s) a tentative move would form and its score, or that it is not
legal, and the move is offered for submission only once it is confirmed legal. The dictionary check tool is
-unlimited and offers a complaint on any result. Hints are governed per game —
+unlimited and offers a complaint on any result; for a word it finds, it also links out to an
+external reference dictionary (gramota.ru for Russian games, scrabblewordfinder.org for
+English) to look it up. Hints are governed per game —
whether they are allowed and how many each player starts with — and draw on a
personal hint wallet once the per-game allowance is spent. The game ends when the
bag empties and a player clears their rack, after 6 consecutive scoreless turns,
diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md
index 2a7d38a..a08598a 100644
--- a/docs/FUNCTIONAL_ru.md
+++ b/docs/FUNCTIONAL_ru.md
@@ -115,7 +115,9 @@ nudge) приходят от бота **этой партии** — по язы
сдаче и считается; безлимитный предпросмотр показывает слово (или слова), которое
образует предполагаемый ход, и его очки — либо что ход недопустим, — и ход можно
отправить только после подтверждения, что он допустим. Инструмент проверки слова безлимитный и
-предлагает пожаловаться на любой результат. Подсказки управляются настройками
+предлагает пожаловаться на любой результат; для найденного слова он также даёт ссылку на
+внешний справочный словарь (gramota.ru для русских игр, scrabblewordfinder.org для английских),
+чтобы его посмотреть. Подсказки управляются настройками
партии — разрешены ли они и сколько их у каждого игрока на старте — и расходуют
личный кошелёк подсказок после исчерпания внутриигрового лимита. Партия
завершается, когда мешок пуст и игрок выложил стойку, после 6 подряд бесплодных
diff --git a/ui/e2e/game.spec.ts b/ui/e2e/game.spec.ts
index 35c62a1..db5c8a1 100644
--- a/ui/e2e/game.spec.ts
+++ b/ui/e2e/game.spec.ts
@@ -221,6 +221,13 @@ test('check-word sanitises input and shows a verdict', async ({ page }) => {
await page.locator('.check button').click(); // Check (enabled: length 3)
await expect(page.locator('.ok, .bad')).toBeVisible();
+
+ // A found word offers an external dictionary lookup link that opens in a new tab; the
+ // verdict still carries the complaint button.
+ const lookup = page.getByRole('link', { name: 'Look it up' });
+ await expect(lookup).toBeVisible();
+ await expect(lookup).toHaveAttribute('target', '_blank');
+ await expect(lookup).toHaveAttribute('href', /qza$/);
});
test('dropping the game ends it and shows the result', async ({ page }) => {
diff --git a/ui/src/game/CheckScreen.svelte b/ui/src/game/CheckScreen.svelte
index b9889dd..b4c3ea3 100644
--- a/ui/src/game/CheckScreen.svelte
+++ b/ui/src/game/CheckScreen.svelte
@@ -4,7 +4,8 @@
import { handleError, showToast } from '../lib/app.svelte';
import { t } from '../lib/i18n/index.svelte';
import { alphabetLetters } from '../lib/alphabet';
- import { canCheckWord, sanitizeCheckWord } from '../lib/checkword';
+ import { canCheckWord, dictionaryLookupUrl, sanitizeCheckWord } from '../lib/checkword';
+ import { telegramOpenExternalLink } from '../lib/telegram';
import type { Variant } from '../lib/model';
// Word-check on its own screen: unlimited dictionary lookups, each with a
@@ -57,6 +58,12 @@
handleError(e);
}
}
+ // Inside Telegram, route the dictionary link through the Mini App SDK so Telegram opens it in
+ // its in-app browser instead of the WebView's "open this link?" confirmation; in a plain
+ // browser the anchor's own target=_blank handles it.
+ function openLookup(e: MouseEvent) {
+ if (telegramOpenExternalLink((e.currentTarget as HTMLAnchorElement).href)) e.preventDefault();
+ }