feat: prepare site for Yandex Ads (RSYA) moderation
- Add About page (/about, /ru/about) with mission, history, contacts - Add FaqBlock component for generator FAQ sections - Add FAQ to all 10 generators (3 Q&A each, EN+RU) - Expand howTo/whenTo content on numbers, password, coin generators - Add About link to LanguageSwitcher navigation - Improve Yandex RTB ad block: min-height placeholder, no layout shift - Extend generator schema with optional faq/ruFaq fields - Update translations.ts with about and faq i18n keys
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
---
|
||||
import { useT } from "../i18n/translations";
|
||||
import type { Lang } from "../i18n/translations";
|
||||
|
||||
interface Props {
|
||||
questions: Array<{ q: string; a: string }>;
|
||||
lang?: Lang;
|
||||
}
|
||||
|
||||
const { questions, lang = "en" } = Astro.props;
|
||||
const T = useT(lang);
|
||||
---
|
||||
|
||||
<section class="mt-4">
|
||||
<div class="rounded-xl bg-zinc-900/50 border border-zinc-800/60 p-5">
|
||||
<h2
|
||||
class="flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-zinc-400 mb-3"
|
||||
>
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-accent" aria-hidden="true"></span>
|
||||
{T.faq}
|
||||
</h2>
|
||||
<div class="space-y-3">
|
||||
{
|
||||
questions.map(({ q, a }) => (
|
||||
<div>
|
||||
<p class="font-medium text-zinc-300 text-sm">{q}</p>
|
||||
<p class="text-zinc-400 text-sm mt-0.5">{a}</p>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -25,6 +25,13 @@ const alternatePath = isRu
|
||||
data-target-lang="ru"
|
||||
data-alternate={isRu ? "" : alternatePath}>RU</a
|
||||
>
|
||||
<span class="w-px h-3 bg-zinc-700 mx-0.5" aria-hidden="true"></span>
|
||||
<a
|
||||
href={isRu ? "/ru/about/" : "/about/"}
|
||||
class="px-2 py-1 rounded-md transition-colors text-zinc-400 hover:text-zinc-200"
|
||||
>
|
||||
{isRu ? "О проекте" : "About"}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<!-- Yandex.RTB R-A-19259130-1 -->
|
||||
<div id="yandex_rtb_R-A-19259130-1"></div>
|
||||
<div
|
||||
id="yandex_rtb_R-A-19259130-1"
|
||||
class="min-h-[90px] w-full flex items-center justify-center rounded-lg bg-zinc-900/30"
|
||||
>
|
||||
<span class="text-xs text-zinc-600 uppercase tracking-widest select-none"
|
||||
>Advertisement</span
|
||||
>
|
||||
</div>
|
||||
<script>
|
||||
window.yaContextCb.push(() => {
|
||||
Ya.Context.AdvManager.render({
|
||||
blockId: "R-A-19259130-1",
|
||||
renderTo: "yandex_rtb_R-A-19259130-1"
|
||||
})
|
||||
})
|
||||
renderTo: "yandex_rtb_R-A-19259130-1",
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -34,5 +34,15 @@
|
||||
"Случайная раздача карт для карточных игр.",
|
||||
"Выбор случайных карт для гаданий.",
|
||||
"Генерация случайных комбинаций карт для тестирования."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "What deck is used?", "a": "A standard 52-card deck with four suits: spades ♠, hearts ♥, diamonds ♦, and clubs ♣." },
|
||||
{ "q": "Can I draw the same card twice?", "a": "Only if Allow duplicates is enabled. By default, each card is drawn at most once." },
|
||||
{ "q": "Are jokers included?", "a": "No. Only the standard 52 playing cards are used." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Какая колода используется?", "a": "Стандартная колода из 52 карт четырёх мастей: пики ♠, червы ♥, бубны ♦, трефы ♣." },
|
||||
{ "q": "Можно вытащить одну карту дважды?", "a": "Только если включены повторения. По умолчанию каждая карта вытаскивается не более одного раза." },
|
||||
{ "q": "Есть ли джокеры?", "a": "Нет. Используется только стандартная колода из 52 карт." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"Drag the slider to choose how many coins to flip (1–20).",
|
||||
"Click Flip — each coin shows H for heads or T for tails.",
|
||||
"When flipping more than one coin, the heads/tails count is shown below.",
|
||||
"Click Copy to copy all results to the clipboard."
|
||||
"Click Copy to copy all results to the clipboard.",
|
||||
"Use a single coin for a classic 50/50 decision."
|
||||
],
|
||||
"whenTo": [
|
||||
"Making a quick heads-or-tails decision.",
|
||||
@@ -28,7 +29,18 @@
|
||||
"Выберите количество монет с помощью ползунка.",
|
||||
"Нажмите «Подбросить» — орёл или решка.",
|
||||
"При нескольких монетах отображается итоговый счёт.",
|
||||
"Нажмите «Копировать», чтобы сохранить результат."
|
||||
"Нажмите «Копировать», чтобы сохранить результат.",
|
||||
"Одна монетка — классическое решение «орёл или решка»."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Is the coin flip fair?", "a": "Yes. Each flip uses your browser's random function, giving a true 50/50 chance for heads or tails." },
|
||||
{ "q": "Can I flip multiple coins at once?", "a": "Yes. Use the slider to choose up to 20 coins, and see the total heads and tails count." },
|
||||
{ "q": "What do H and T stand for?", "a": "H means Heads and T means Tails — the two sides of a coin." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Подбрасывание честное?", "a": "Да. Каждый бросок использует функцию случайности браузера, давая истинный шанс 50/50 на орла или решку." },
|
||||
{ "q": "Можно подбросить несколько монет сразу?", "a": "Да. С помощью ползунка выберите до 20 монет и увидите общий счёт орлов и решек." },
|
||||
{ "q": "Что означают О и Р?", "a": "О — орёл, Р — решка. Это две стороны монеты." }
|
||||
],
|
||||
"ruWhenTo": [
|
||||
"Принятие решений по принципу «орёл или решка».",
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
"Selecting a random theme color for a project or presentation.",
|
||||
"Teaching or exploring color theory and formats."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown at once, so you can copy the one you need." },
|
||||
{ "q": "Can I use the generated color in CSS?", "a": "Yes. Copy the HEX or RGB value and paste it directly into your stylesheet or design tool." },
|
||||
{ "q": "Are the colors truly random?", "a": "Yes. Each color is generated using a cryptographically secure random number generator, covering the entire color space." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три формата отображаются одновременно — скопируйте тот, который нужен." },
|
||||
{ "q": "Можно ли использовать сгенерированный цвет в CSS?", "a": "Да. Скопируйте значение HEX или RGB и вставьте его прямо в стили или графический редактор." },
|
||||
{ "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется с помощью криптографически стойкого генератора случайных чисел, покрывая всё цветовое пространство." }
|
||||
],
|
||||
"ruHowTo": [
|
||||
"Нажмите «Сгенерировать», чтобы получить случайный цвет.",
|
||||
"Цвет отображается в форматах HEX, RGB и HSL одновременно.",
|
||||
@@ -32,5 +42,15 @@
|
||||
"Поиск вдохновения для цветовой палитры.",
|
||||
"Случайный выбор цвета для дизайна или иллюстрации.",
|
||||
"Генерация тестовых данных с цветовыми значениями."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown simultaneously for every generated color." },
|
||||
{ "q": "Can I copy just one format?", "a": "Yes. Click the copy icon next to any format to copy only that value." },
|
||||
{ "q": "Are the colors truly random?", "a": "Yes. Each color is generated using random values for hue, saturation, and lightness." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три отображаются одновременно для каждого сгенерированного цвета." },
|
||||
{ "q": "Можно скопировать только один формат?", "a": "Да. Нажмите иконку копирования рядом с нужным форматом." },
|
||||
{ "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется из случайных значений оттенка, насыщенности и освещённости." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
"Rolling exploding dice for Savage Worlds or house rules (e.g. 3d6!).",
|
||||
"Using keep/drop modifiers like 4d6kh3 to keep highest rolls."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "What dice notation can I use?", "a": "You can use standard XdY+Z notation, plus modifiers like advantage, disadvantage, keep/drop (kh/kl/dl/dh), and exploding dice (!)." },
|
||||
{ "q": "Is this fair for D&D and other RPGs?", "a": "Yes. Randify uses the browser's cryptographically secure random number generator, so every roll is unbiased." },
|
||||
{ "q": "Can I roll multiple dice at once?", "a": "Absolutely. Enter something like 5d6 or click the die buttons and set the count to roll up to 20 dice in a single throw." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Какую нотацию кубиков поддерживает генератор?", "a": "Стандартную нотацию XdY+Z, а также модификаторы: преимущество, помеха, keep/drop (kh/kl/dl/dh) и взрывающиеся кубики (!)." },
|
||||
{ "q": "Подходит ли генератор для D&D и других НРИ?", "a": "Да. Randify использует криптографически стойкий генератор случайных чисел браузера, поэтому каждый бросок честный и непредвзятый." },
|
||||
{ "q": "Можно ли бросать несколько кубиков одновременно?", "a": "Конечно. Введите что-то вроде 5d6 или настройте кубики вручную — можно бросить до 20 кубиков за раз." }
|
||||
],
|
||||
"ruHowTo": [
|
||||
"Введите нотацию, например 2d6+3 или 1d20 — или настройте кубики вручную.",
|
||||
"Выберите режим: Обычный, Преимущество или Помеха (только для одного кубика).",
|
||||
@@ -39,5 +49,15 @@
|
||||
"Бросок характеристик: 4d6, отбросить наименьший (4d6dl1).",
|
||||
"Взрывающиеся кубики для Savage Worlds или хаус-рулов (например, 3d6!).",
|
||||
"Использование keep/drop модификаторов типа 4d6kh3 (оставить три лучших)."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "What dice notation is supported?", "a": "Standard XdY+Z, advantage/disadvantage, keep/drop (kh/kl/dh/dl), reroll (r), and exploding dice (!)." },
|
||||
{ "q": "Can I roll more than one type of die at once?", "a": "Yes. Enter multiple notations separated by spaces, like 2d6 1d20." },
|
||||
{ "q": "Is the roll history saved?", "a": "Yes. Your last rolls are shown below the results until you refresh the page." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Какая нотация кубиков поддерживается?", "a": "Стандартная XdY+Z, преимущество/помеха, keep/drop (kh/kl/dh/dl), переброс (r) и взрывающиеся кубики (!)." },
|
||||
{ "q": "Можно ли бросить несколько типов кубиков сразу?", "a": "Да. Введите несколько нотаций через пробел, например 2d6 1d20." },
|
||||
{ "q": "История бросков сохраняется?", "a": "Да. Последние броски отображаются под результатами до обновления страницы." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
"Organising a Secret Santa or gift exchange draw.",
|
||||
"Selecting a random movie, restaurant, or activity from a list."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Can the same item be picked more than once?", "a": "Only if you enable the 'Allow duplicates' toggle. By default, each item can be chosen only once per draw." },
|
||||
{ "q": "Is there a limit to how many items I can add?", "a": "You can add up to 1,000 items. For most use cases — giveaways, raffles, or team assignments — this is more than enough." },
|
||||
{ "q": "Can I pick multiple winners at once?", "a": "Yes. Set the 'Pick' field to any number up to the total count of items, and Randify will select that many winners in one go." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Может ли один и тот же элемент выпасть несколько раз?", "a": "Только если включён переключатель «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." },
|
||||
{ "q": "Есть ли ограничение на количество элементов в списке?", "a": "Можно добавить до 1 000 элементов. Для розыгрышей, жеребьёвок и распределения задач этого более чем достаточно." },
|
||||
{ "q": "Можно ли выбрать несколько победителей сразу?", "a": "Да. Укажите в поле «Выбрать» любое число до общего количества элементов — Randify выберет нужное количество победителей за один раз." }
|
||||
],
|
||||
"ruHowTo": [
|
||||
"Введите список элементов — по одному на строку.",
|
||||
"Укажите, сколько элементов выбрать.",
|
||||
@@ -34,5 +44,15 @@
|
||||
"Розыгрыш победителей конкурса.",
|
||||
"Случайное распределение задач между участниками команды.",
|
||||
"Выбор случайного варианта из списка."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Can the same item be picked more than once?", "a": "Only if you toggle Allow duplicates. By default, each item is picked at most once." },
|
||||
{ "q": "Is there a limit to how many items I can add?", "a": "You can add up to 500 items, and pick up to the total number of unique items." },
|
||||
{ "q": "Does the order of items matter?", "a": "No. The selection is completely random regardless of the order you enter items." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Может ли один элемент выпасть несколько раз?", "a": "Только если включена опция «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." },
|
||||
{ "q": "Есть ли ограничение на количество элементов?", "a": "Можно добавить до 500 элементов и выбрать до общего числа уникальных элементов." },
|
||||
{ "q": "Имеет ли значение порядок элементов?", "a": "Нет. Выбор полностью случайный независимо от порядка ввода." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,5 +34,15 @@
|
||||
"Розыгрыш лотерейных номеров.",
|
||||
"Жеребьёвка участников конкурса.",
|
||||
"Случайный выбор нескольких уникальных чисел."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Are the numbers guaranteed to be unique?", "a": "Yes. The lottery generator always picks unique numbers within your chosen range." },
|
||||
{ "q": "Can I use this for real lottery tickets?", "a": "Yes, but remember that no generator can improve your odds — each combination has the same chance." },
|
||||
{ "q": "What is the maximum range?", "a": "You can set any range where the difference between From and To is at least as large as the number of picks." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Числа гарантированно уникальны?", "a": "Да. Генератор лотереи всегда выбирает уникальные числа в заданном диапазоне." },
|
||||
{ "q": "Можно ли использовать для реальных лотерей?", "a": "Да, но помните: генератор не улучшает шансы — каждая комбинация имеет равную вероятность." },
|
||||
{ "q": "Какой максимальный диапазон?", "a": "Любой диапазон, где разница между «От» и «До» не меньше количества выбираемых чисел." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"Picking a winner in a giveaway or raffle.",
|
||||
"Choosing a random starting player in a board game.",
|
||||
"Making a quick decision between numbered options.",
|
||||
"Generating test data with values in a specific range."
|
||||
"Generating test data with values in a specific range.",
|
||||
"Assigning random IDs or order numbers to items."
|
||||
],
|
||||
"ruHowTo": [
|
||||
"Введите минимальное значение в поле «От».",
|
||||
@@ -34,6 +35,17 @@
|
||||
"Выбор победителя в розыгрыше или конкурсе.",
|
||||
"Определение случайного первого игрока в настольной игре.",
|
||||
"Быстрое принятие решения между пронумерованными вариантами.",
|
||||
"Генерация тестовых данных с числами в заданном диапазоне."
|
||||
"Генерация тестовых данных с числами в заданном диапазоне.",
|
||||
"Назначение случайных номеров или порядковых идентификаторов."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Is the number truly random?", "a": "Yes. Randify uses your browser's built-in random function, which produces unpredictable results every time." },
|
||||
{ "q": "Can I generate negative numbers?", "a": "Absolutely. Just enter a negative value in the From field and a positive or negative value in the To field." },
|
||||
{ "q": "Is there a limit to the range?", "a": "You can use any whole numbers that JavaScript supports — safely up to about 9 quadrillion." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Число действительно случайное?", "a": "Да. Randify использует встроенную функцию случайности браузера, которая каждый раз выдаёт непредсказуемый результат." },
|
||||
{ "q": "Можно ли генерировать отрицательные числа?", "a": "Конечно. Введите отрицательное значение в поле «От» и любое значение в поле «До»." },
|
||||
{ "q": "Есть ли ограничение на диапазон?", "a": "Можно использовать любые целые числа, которые поддерживает JavaScript — безопасно до примерно 9 квадриллионов." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"Creating a strong password for a new account.",
|
||||
"Generating a temporary access credential.",
|
||||
"Producing a random secret key for development or testing.",
|
||||
"Replacing a weak or reused password."
|
||||
"Replacing a weak or reused password.",
|
||||
"Setting up a secure Wi-Fi passphrase."
|
||||
],
|
||||
"ruHowTo": [
|
||||
"Задайте длину пароля с помощью ползунка.",
|
||||
@@ -33,6 +34,17 @@
|
||||
"ruWhenTo": [
|
||||
"Создание надёжного пароля для нового аккаунта.",
|
||||
"Генерация секретного ключа или токена.",
|
||||
"Регулярное обновление паролей для повышения безопасности."
|
||||
"Регулярное обновление паролей для повышения безопасности.",
|
||||
"Создание сложного пароля для Wi-Fi сети."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Are the passwords stored anywhere?", "a": "No. Passwords are generated locally in your browser and never leave your device." },
|
||||
{ "q": "How strong are the generated passwords?", "a": "Strength depends on length and character types. A 16-character password with all types enabled is considered very strong." },
|
||||
{ "q": "Can I use these passwords for banking?", "a": "Yes, but always follow your bank's specific requirements and consider using a dedicated password manager." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Пароли где-то сохраняются?", "a": "Нет. Пароли генерируются локально в браузере и никогда не покидают ваше устройство." },
|
||||
{ "q": "Насколько надёжны сгенерированные пароли?", "a": "Надёжность зависит от длины и наборов символов. Пароль из 16 символов со всеми типами считается очень сильным." },
|
||||
{ "q": "Можно ли использовать эти пароли для банкинга?", "a": "Да, но всегда учитывайте требования вашего банка и рассмотрите использование специального менеджера паролей." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,5 +34,15 @@
|
||||
"Генерация уникальных идентификаторов для баз данных.",
|
||||
"Создание токенов для API и сессий.",
|
||||
"Генерация случайных ключей шифрования."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "Are these UUIDs cryptographically secure?", "a": "Yes. UUID v4 is generated using the browser's crypto.getRandomValues, which is cryptographically secure." },
|
||||
{ "q": "What is the difference between UUID, Hex, and Base64?", "a": "UUID v4 is a 128-bit standard format. Hex is raw random bytes in hexadecimal. Base64 is the same bytes encoded for compactness." },
|
||||
{ "q": "Can I use these tokens in production?", "a": "UUID v4 tokens are safe for production. For Hex and Base64, the security depends on the byte length you choose." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "UUID криптографически безопасны?", "a": "Да. UUID v4 генерируется через crypto.getRandomValues браузера, что обеспечивает криптографическую стойкость." },
|
||||
{ "q": "В чём разница между UUID, Hex и Base64?", "a": "UUID v4 — стандартный 128-битный формат. Hex — сырые случайные байты в шестнадцатеричном виде. Base64 — те же байты в компактном кодировании." },
|
||||
{ "q": "Можно ли использовать токены в продакшене?", "a": "UUID v4 безопасны для продакшена. Для Hex и Base64 безопасность зависит от выбранной длины в байтах." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,5 +34,15 @@
|
||||
"Случайный выбор победителя розыгрыша.",
|
||||
"Принятие решений в команде.",
|
||||
"Распределение ролей или задач между участниками."
|
||||
],
|
||||
"faq": [
|
||||
{ "q": "How many items can I add?", "a": "Between 2 and 24 items. The wheel automatically resizes to fit all segments." },
|
||||
{ "q": "Is the spin truly random?", "a": "Yes. The stopping position is determined by a cryptographically secure random value." },
|
||||
{ "q": "Can I save my wheel?", "a": "Not yet. You can bookmark the page or copy your item list to reuse it later." }
|
||||
],
|
||||
"ruFaq": [
|
||||
{ "q": "Сколько элементов можно добавить?", "a": "От 2 до 24. Колесо автоматически подстраивается под количество сегментов." },
|
||||
{ "q": "Вращение действительно случайное?", "a": "Да. Конечная позиция определяется криптографически стойким случайным значением." },
|
||||
{ "q": "Можно ли сохранить колесо?", "a": "Пока нет. Можно добавить страницу в закладки или скопировать список элементов для повторного использования." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ export const translations = {
|
||||
copyAll: "Copy all",
|
||||
howToUse: "How to use",
|
||||
whenToUse: "When to use",
|
||||
faq: "FAQ",
|
||||
about: "About",
|
||||
|
||||
generate: "Generate",
|
||||
draw: "Draw",
|
||||
@@ -66,6 +68,18 @@ export const translations = {
|
||||
"Simple tools for raffles, games, and everything that needs randomness.",
|
||||
brandLabel: "randify",
|
||||
backToAll: "All generators",
|
||||
|
||||
aboutTitle: "About Randify",
|
||||
aboutMission:
|
||||
"Randify exists to make randomness simple, fast, and accessible to everyone. Whether you are picking a winner, rolling dice, or generating a secure password, you should not need to install an app or create an account. Just open the page and get your result.",
|
||||
aboutHistory:
|
||||
"The idea for Randify came from a simple frustration: every time we needed a quick random number or a coin flip, we ended up on cluttered websites full of ads, pop-ups, and unnecessary steps. We wanted something cleaner — a tool that just works, without distractions.\n\nWe started with a single number generator and gradually expanded the collection based on what people actually use: dice for board games, wheels for raffles, passwords for security, and more. Each tool was built with the same philosophy — minimal interface, instant results, zero friction.",
|
||||
aboutHowItWorks:
|
||||
"Everything on Randify runs entirely in your browser. No data is sent to any server, no personal information is collected, and no account is required. The random values are generated locally using the Web Crypto API, which provides cryptographically secure randomness. This means your passwords, numbers, and choices stay private — we never see them.",
|
||||
aboutContact:
|
||||
"Have a suggestion, found a bug, or just want to say hello? Drop us a line at hello@randify.pro — we read every message.",
|
||||
aboutCta:
|
||||
"Ready to give it a spin? Explore the generators and let chance do the work.",
|
||||
},
|
||||
ru: {
|
||||
allGenerators: "Все генераторы",
|
||||
@@ -76,6 +90,8 @@ export const translations = {
|
||||
copyAll: "Копировать всё",
|
||||
howToUse: "Как пользоваться",
|
||||
whenToUse: "Когда использовать",
|
||||
faq: "Частые вопросы",
|
||||
about: "О проекте",
|
||||
|
||||
generate: "Сгенерировать",
|
||||
draw: "Тянуть",
|
||||
@@ -132,6 +148,18 @@ export const translations = {
|
||||
"Простые инструменты для розыгрышей, игр и всего, что требует случайности.",
|
||||
brandLabel: "randify",
|
||||
backToAll: "Все генераторы",
|
||||
|
||||
aboutTitle: "О проекте",
|
||||
aboutMission:
|
||||
"Randify создан, чтобы сделать случайность простой, быстрой и доступной каждому. Независимо от того, выбираете ли вы победителя, бросаете кубики или создаёте надёжный пароль — не нужно устанавливать приложение или регистрироваться. Просто откройте страницу и получите результат.",
|
||||
aboutHistory:
|
||||
"Идея Randify родилась из простого раздражения: каждый раз, когда нам нужно было быстро сгенерировать случайное число или подбросить монетку, мы попадали на перегруженные рекламой сайты с всплывающими окнами и лишними шагами. Мы хотели создать что-то чище — инструмент, который просто работает, без отвлекающих элементов.\n\nМы начали с одного генератора чисел и постепенно расширяли коллекцию, ориентируясь на реальные потребности: кубики для настольных игр, колесо фортуны для розыгрышей, генератор паролей для безопасности и многое другое. Каждый инструмент создавался по одному принципу — минимальный интерфейс, мгновенный результат, никаких препятствий.",
|
||||
aboutHowItWorks:
|
||||
"Всё на Randify работает полностью в вашем браузере. Данные не отправляются на сервер, личная информация не собирается, регистрация не требуется. Случайные значения генерируются локально с помощью Web Crypto API, который обеспечивает криптографически стойкую случайность. Это означает, что ваши пароли, числа и выбор остаются приватными — мы их не видим.",
|
||||
aboutContact:
|
||||
"Есть предложение, нашли ошибку или просто хотите поздороваться? Напишите нам на hello@randify.pro — мы читаем каждое сообщение.",
|
||||
aboutCta:
|
||||
"Готовы попробовать? Откройте генераторы и позвольте случайности сделать выбор за вас.",
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
import YandexRTB from "../components/YandexRTB.astro";
|
||||
import SeoBlock from "../components/SeoBlock.astro";
|
||||
import FaqBlock from "../components/FaqBlock.astro";
|
||||
import { useT } from "../i18n/translations";
|
||||
import type { Lang } from "../i18n/translations";
|
||||
import type { Generator } from "../data/generators";
|
||||
@@ -123,5 +124,14 @@ const breadcrumbSchema = {
|
||||
howTo={isRu ? generator.ruHowTo : generator.howTo}
|
||||
whenTo={isRu ? generator.ruWhenTo : generator.whenTo}
|
||||
/>
|
||||
|
||||
{
|
||||
(isRu ? generator.ruFaq : generator.faq) && (
|
||||
<FaqBlock
|
||||
lang={lang}
|
||||
questions={(isRu ? generator.ruFaq : generator.faq) || []}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -19,6 +19,8 @@ export const generatorSchema = z
|
||||
whenTo: z.array(z.string()),
|
||||
ruHowTo: z.array(z.string()),
|
||||
ruWhenTo: z.array(z.string()),
|
||||
faq: z.array(z.object({ q: z.string(), a: z.string() })).optional(),
|
||||
ruFaq: z.array(z.object({ q: z.string(), a: z.string() })).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import { useT } from "@/i18n/translations";
|
||||
import type { Lang } from "@/i18n/translations";
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || "en";
|
||||
const T = useT(lang);
|
||||
|
||||
const aboutSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "AboutPage",
|
||||
name: T.aboutTitle,
|
||||
description: T.aboutMission,
|
||||
url: "https://randify.pro/about/",
|
||||
inLanguage: lang,
|
||||
};
|
||||
---
|
||||
|
||||
<BaseLayout title={`${T.aboutTitle} | Randify`} description={T.aboutMission}>
|
||||
<script type="application/ld+json" set:html={JSON.stringify(aboutSchema)} slot="head" />
|
||||
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
|
||||
<nav aria-label="Breadcrumb" class="mb-10">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center gap-1.5 text-sm text-zinc-600 hover:text-zinc-300 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950 rounded"
|
||||
aria-label={T.backToAll}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="m15 18-6-6 6-6"></path>
|
||||
</svg>
|
||||
{T.backToAll}
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<header class="mb-2">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span
|
||||
class="inline-block w-2.5 h-2.5 rounded-full bg-accent"
|
||||
aria-hidden="true"></span>
|
||||
<span
|
||||
class="text-sm font-medium text-zinc-400 uppercase tracking-widest"
|
||||
>randify</span
|
||||
>
|
||||
</div>
|
||||
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">
|
||||
{T.aboutTitle}
|
||||
</h1>
|
||||
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
|
||||
</header>
|
||||
|
||||
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Mission</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutMission}</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">History</h2>
|
||||
{T.aboutHistory.split("\n\n").map((paragraph) => (
|
||||
<p class="text-zinc-400 leading-relaxed mb-4 last:mb-0">{paragraph}</p>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">How it works</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutHowItWorks}</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Contact</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutContact}</p>
|
||||
</section>
|
||||
|
||||
<section class="pt-4">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-accent text-white font-medium hover:bg-accent-hover active:bg-accent-active transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950"
|
||||
>
|
||||
{T.aboutCta}
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import { useT } from "@/i18n/translations";
|
||||
import type { Lang } from "@/i18n/translations";
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || "en";
|
||||
const T = useT(lang);
|
||||
|
||||
const aboutSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "AboutPage",
|
||||
name: T.aboutTitle,
|
||||
description: T.aboutMission,
|
||||
url: "https://randify.pro/ru/about/",
|
||||
inLanguage: lang,
|
||||
};
|
||||
---
|
||||
|
||||
<BaseLayout title={`${T.aboutTitle} | Randify`} description={T.aboutMission}>
|
||||
<script type="application/ld+json" set:html={JSON.stringify(aboutSchema)} slot="head" />
|
||||
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
|
||||
<nav aria-label="Breadcrumb" class="mb-10">
|
||||
<a
|
||||
href="/ru/"
|
||||
class="inline-flex items-center gap-1.5 text-sm text-zinc-600 hover:text-zinc-300 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950 rounded"
|
||||
aria-label={T.backToAll}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="m15 18-6-6 6-6"></path>
|
||||
</svg>
|
||||
{T.backToAll}
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<header class="mb-2">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span
|
||||
class="inline-block w-2.5 h-2.5 rounded-full bg-accent"
|
||||
aria-hidden="true"></span>
|
||||
<span
|
||||
class="text-sm font-medium text-zinc-400 uppercase tracking-widest"
|
||||
>randify</span
|
||||
>
|
||||
</div>
|
||||
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">
|
||||
{T.aboutTitle}
|
||||
</h1>
|
||||
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
|
||||
</header>
|
||||
|
||||
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Миссия</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutMission}</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">История</h2>
|
||||
{T.aboutHistory.split("\n\n").map((paragraph) => (
|
||||
<p class="text-zinc-400 leading-relaxed mb-4 last:mb-0">{paragraph}</p>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Как это работает</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutHowItWorks}</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Контакты</h2>
|
||||
<p class="text-zinc-400 leading-relaxed">{T.aboutContact}</p>
|
||||
</section>
|
||||
|
||||
<section class="pt-4">
|
||||
<a
|
||||
href="/ru/"
|
||||
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-accent text-white font-medium hover:bg-accent-hover active:bg-accent-active transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950"
|
||||
>
|
||||
{T.aboutCta}
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
Reference in New Issue
Block a user