diff --git a/.gitignore b/.gitignore index f6d8805..7a14645 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist/ .env *.log .astro/ +.kimi/ diff --git a/src/components/generators/CountryGenerator.astro b/src/components/generators/CountryGenerator.astro new file mode 100644 index 0000000..7da02e3 --- /dev/null +++ b/src/components/generators/CountryGenerator.astro @@ -0,0 +1,249 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const regions = isRu + ? [ + { value: "all", label: "Все" }, + { value: "europe", label: "Европа" }, + { value: "asia", label: "Азия" }, + { value: "americas", label: "Америка" }, + { value: "africa", label: "Африка" }, + { value: "oceania", label: "Океания" }, + ] + : [ + { value: "all", label: "All" }, + { value: "europe", label: "Europe" }, + { value: "asia", label: "Asia" }, + { value: "americas", label: "Americas" }, + { value: "africa", label: "Africa" }, + { value: "oceania", label: "Oceania" }, + ]; +--- + +
+ +
+ + +
+ + +
+
+ +
+ + +
+ +
+
+ + diff --git a/src/components/generators/LoremGenerator.astro b/src/components/generators/LoremGenerator.astro new file mode 100644 index 0000000..244e80a --- /dev/null +++ b/src/components/generators/LoremGenerator.astro @@ -0,0 +1,252 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const lengthOptions = isRu + ? [ + { value: "short", label: "Короткие" }, + { value: "medium", label: "Средние" }, + { value: "long", label: "Длинные" }, + ] + : [ + { value: "short", label: "Short" }, + { value: "medium", label: "Medium" }, + { value: "long", label: "Long" }, + ]; + +const startLabel = isRu + ? "Начать с Lorem ipsum dolor sit amet" + : "Start with Lorem ipsum dolor sit amet"; +--- + +
+ +
+
+ +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ +
+
+ + +
+
+ + {isRu ? "Результат" : "Result"} + + +
+ +
+
+
+ + diff --git a/src/components/generators/PaletteGenerator.astro b/src/components/generators/PaletteGenerator.astro new file mode 100644 index 0000000..8ce7b9b --- /dev/null +++ b/src/components/generators/PaletteGenerator.astro @@ -0,0 +1,346 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const i18n = { + harmonyLabel: isRu ? "Гармония" : "Harmony", + countLabel: isRu ? "Количество цветов" : "Number of colors", + copyAll: T.copyAll, + generate: T.generate, + copied: T.copied, + copy: T.copy, + modes: { + random: isRu ? "Случайная" : "Random", + analogous: isRu ? "Аналоговая" : "Analogous", + complementary: isRu ? "Комплементарная" : "Complementary", + triadic: isRu ? "Триадная" : "Triadic", + monochromatic: isRu ? "Монохромная" : "Monochromatic", + splitComplementary: isRu ? "Сплит-комплементарная" : "Split-Complementary", + tetradic: isRu ? "Тетрадная" : "Tetradic", + }, +}; +--- + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+
+ + + + + +
+ +
+
+ + diff --git a/src/components/generators/WeightedGenerator.astro b/src/components/generators/WeightedGenerator.astro new file mode 100644 index 0000000..0f01759 --- /dev/null +++ b/src/components/generators/WeightedGenerator.astro @@ -0,0 +1,365 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); +--- + +
+
+
+ +
+ +
+ +
+ + +
+ + + + + +
+ +
+ + +
+
+
+ + diff --git a/src/content/generators/country.json b/src/content/generators/country.json new file mode 100644 index 0000000..e22ddb0 --- /dev/null +++ b/src/content/generators/country.json @@ -0,0 +1,49 @@ +{ + "slug": "country", + "title": "Country", + "description": "Generate a random country with flag, capital, region, and population.", + "icon": "user", + "status": "live", + "seoTitle": "Random Country Generator — Flag, Capital, Region | Randify", + "seoDescription": "Get a random country with flag emoji, capital, region, and population. Free online country randomizer for games, quizzes, and travel inspiration.", + "ruTitle": "Страна", + "ruDescription": "Генерируйте случайную страну с флагом, столицей, регионом и населением.", + "ruSeoTitle": "Генератор случайных стран — флаг, столица, регион | Randify", + "ruSeoDescription": "Получайте случайную страну с эмодзи флага, столицей, регионом и населением. Бесплатный рандомайзер стран для игр, викторин и вдохновения.", + "pageTitle": "Random Country Generator", + "ruPageTitle": "Генератор случайных стран", + "howTo": [ + "Select a region filter or leave it on All to include every country.", + "Click Generate to pick a random country from the filtered list.", + "View the result card with flag, name, capital, region, and population.", + "Click Copy to copy the country details to your clipboard." + ], + "whenTo": [ + "Choosing a random destination for a travel challenge or game.", + "Creating quiz questions about capitals, flags, or geography.", + "Picking a random country for a school project or presentation.", + "Settling a friendly debate with a random geographic draw." + ], + "ruHowTo": [ + "Выберите фильтр по региону или оставьте «Все», чтобы включить все страны.", + "Нажмите «Сгенерировать», чтобы выбрать случайную страну из отфильтрованного списка.", + "Посмотрите карточку результата с флагом, названием, столицей, регионом и населением.", + "Нажмите «Копировать», чтобы скопировать данные о стране в буфер обмена." + ], + "ruWhenTo": [ + "Выбор случайного направления для путешественнического челленджа или игры.", + "Создание вопросов для викторин о столицах, флагах или географии.", + "Выбор случайной страны для школьного проекта или презентации.", + "Решение спора случайным географическим выбором." + ], + "faq": [ + { "q": "How many countries are in the generator?", "a": "The generator includes 40 countries from all inhabited continents, covering a wide range of regions, populations, and cultures." }, + { "q": "Can I filter by region?", "a": "Yes. You can filter by Europe, Asia, Americas, Africa, or Oceania, or choose All to include every country." }, + { "q": "Is the population data accurate?", "a": "Population figures are approximate and based on recent estimates. They are intended for casual use rather than precise research." } + ], + "ruFaq": [ + { "q": "Сколько стран в генераторе?", "a": "Генератор включает 40 стран со всех обитаемых континентов, охватывая широкий спектр регионов, населения и культур." }, + { "q": "Можно ли фильтровать по региону?", "a": "Да. Вы можете фильтровать по Европе, Азии, Америке, Африке или Океании, либо выбрать «Все», чтобы включить все страны." }, + { "q": "Данные о населении актуальны?", "a": "Численность населения приблизительная и основана на недавних оценках. Она предназначена для повседневного использования, а не для точных исследований." } + ] +} diff --git a/src/content/generators/lorem.json b/src/content/generators/lorem.json new file mode 100644 index 0000000..77979f4 --- /dev/null +++ b/src/content/generators/lorem.json @@ -0,0 +1,47 @@ +{ + "slug": "lorem", + "title": "Lorem Ipsum", + "description": "Generate random Lorem Ipsum placeholder text in paragraphs.", + "icon": "type", + "status": "live", + "seoTitle": "Lorem Ipsum Generator — Free Placeholder Text | Randify", + "seoDescription": "Generate random Lorem Ipsum placeholder text instantly. Choose paragraph count and length. Free online dummy text generator for designers and developers.", + "ruTitle": "Lorem Ipsum", + "ruDescription": "Генерируйте случайный текст-заполнитель Lorem Ipsum абзацами.", + "ruSeoTitle": "Генератор Lorem Ipsum — бесплатный текст-заполнитель | Randify", + "ruSeoDescription": "Генерируйте текст-заполнитель Lorem Ipsum мгновенно. Выбирайте количество абзацев и длину. Бесплатный генератор текста для дизайнеров и разработчиков.", + "pageTitle": "Lorem Ipsum Generator", + "ruPageTitle": "Генератор Lorem Ipsum", + "howTo": [ + "Choose the number of paragraphs (1–20) and the desired length: Short, Medium, or Long.", + "Optionally check \"Start with Lorem ipsum...\" to begin the first paragraph with the classic opening.", + "Click Generate to create the text, then use the Copy button to copy it to your clipboard." + ], + "whenTo": [ + "Filling mockups and wireframes with realistic placeholder text.", + "Testing typography, line height, and readability in designs.", + "Creating demo content for websites and applications.", + "Needing neutral Latin text that won't distract from layout." + ], + "ruHowTo": [ + "Выберите количество абзацев (1–20) и желаемую длину: Короткие, Средние или Длинные.", + "При желании отметьте «Начать с Lorem ipsum...», чтобы первый абзац начинался классически.", + "Нажмите «Сгенерировать», а затем «Копировать», чтобы скопировать текст в буфер обмена." + ], + "ruWhenTo": [ + "Заполнение макетов и каркасов реалистичным текстом-заполнителем.", + "Тестирование типографики, межстрочного интервала и читаемости в дизайнах.", + "Создание демонстрационного контента для сайтов и приложений.", + "Необходимость в нейтральном латинском тексте, который не отвлекает от макета." + ], + "faq": [ + { "q": "What is Lorem Ipsum?", "a": "Lorem Ipsum is a classic placeholder text used in publishing and graphic design. It has been the industry's standard dummy text since the 1500s." }, + { "q": "Can I choose how long each paragraph is?", "a": "Yes. You can select Short (2–4 sentences), Medium (4–6 sentences), or Long (6–10 sentences) per paragraph." }, + { "q": "Is the generated text truly random?", "a": "Yes. Each paragraph is built from a shuffled pool of classic Latin words, so every generation produces unique text." } + ], + "ruFaq": [ + { "q": "Что такое Lorem Ipsum?", "a": "Lorem Ipsum — это классический текст-заполнитель, используемый в издательском деле и графическом дизайне. Он является стандартным текстом-рыбой с XVI века." }, + { "q": "Можно ли выбрать длину каждого абзаца?", "a": "Да. Вы можете выбрать Короткие (2–4 предложения), Средние (4–6 предложений) или Длинные (6–10 предложений) абзацы." }, + { "q": "Сгенерированный текст действительно случайный?", "a": "Да. Каждый абзац собирается из перемешанного набора классических латинских слов, поэтому каждая генерация уникальна." } + ] +} diff --git a/src/content/generators/palette.json b/src/content/generators/palette.json new file mode 100644 index 0000000..12da908 --- /dev/null +++ b/src/content/generators/palette.json @@ -0,0 +1,47 @@ +{ + "slug": "palette", + "title": "Color Palette", + "description": "Generate harmonious color palettes with one click.", + "icon": "palette", + "status": "live", + "seoTitle": "Color Palette Generator — Random & Harmonic Palettes | Randify", + "seoDescription": "Generate beautiful color palettes instantly. Choose from Random, Analogous, Complementary, Triadic, and Monochromatic harmony modes. Free online tool for designers.", + "ruTitle": "Палитра цветов", + "ruDescription": "Генерируйте гармоничные палитры цветов одним нажатием.", + "ruSeoTitle": "Генератор палитры цветов — случайные и гармоничные палитры | Randify", + "ruSeoDescription": "Создавайте красивые палитры цветов мгновенно. Выбирайте режимы гармонии: случайный, аналоговый, комплементарный, триадный, монохромный. Бесплатный онлайн-инструмент для дизайнеров.", + "pageTitle": "Color Palette Generator", + "ruPageTitle": "Генератор палитры цветов", + "howTo": [ + "Select the number of colors and a harmony mode.", + "Click Generate to create a new palette.", + "Click any color swatch to copy its HEX code, or use Copy All to copy the entire palette." + ], + "whenTo": [ + "Designing a brand identity or logo color scheme.", + "Choosing colors for a website or app UI.", + "Creating illustrations, presentations, or social media graphics.", + "Exploring color theory and harmony relationships." + ], + "ruHowTo": [ + "Выберите количество цветов и режим гармонии.", + "Нажмите «Сгенерировать», чтобы создать новую палитру.", + "Нажмите на любой цвет, чтобы скопировать его HEX-код, или используйте «Копировать всё»." + ], + "ruWhenTo": [ + "Разработка фирменного стиля или цветовой схемы логотипа.", + "Выбор цветов для дизайна сайта или приложения.", + "Создание иллюстраций, презентаций или графики для соцсетей.", + "Изучение теории цвета и цветовых гармоний." + ], + "faq": [ + { "q": "What harmony modes are available?", "a": "Random, Analogous, Complementary, Triadic, and Monochromatic. Each mode creates colors based on a different relationship on the color wheel." }, + { "q": "Can I copy the whole palette at once?", "a": "Yes. Click the Copy All button to copy all HEX codes separated by commas to your clipboard." }, + { "q": "How many colors can I generate?", "a": "You can generate palettes from 3 to 8 colors using the count selector." } + ], + "ruFaq": [ + { "q": "Какие режимы гармонии доступны?", "a": "Случайный, аналоговый, комплементарный, триадный и монохромный. Каждый режим создаёт цвета на основе разных отношений на цветовом круге." }, + { "q": "Можно ли скопировать всю палитру сразу?", "a": "Да. Нажмите кнопку «Копировать всё», чтобы скопировать все HEX-коды через запятую в буфер обмена." }, + { "q": "Сколько цветов можно сгенерировать?", "a": "Вы можете создавать палитры от 3 до 8 цветов с помощью селектора количества." } + ] +} diff --git a/src/content/generators/weighted.json b/src/content/generators/weighted.json new file mode 100644 index 0000000..38e00de --- /dev/null +++ b/src/content/generators/weighted.json @@ -0,0 +1,47 @@ +{ + "slug": "weighted", + "title": "Weighted Random", + "description": "Pick a random item from a list using custom weights for each option.", + "icon": "list", + "status": "live", + "seoTitle": "Weighted Random Generator — Weighted Choice Picker | Randify", + "seoDescription": "Pick random items with custom weights. Perfect for raffles, weighted decisions, and probability experiments. Free online weighted random selector.", + "ruTitle": "Взвешенный случайный выбор", + "ruDescription": "Выбирайте случайный элемент из списка с учётом заданных весов для каждого варианта.", + "ruSeoTitle": "Генератор взвешенного случайного выбора | Randify", + "ruSeoDescription": "Выбирайте случайные элементы с заданными весами. Идеально для розыгрышей, взвешенных решений и экспериментов с вероятностями.", + "pageTitle": "Weighted Random Generator", + "ruPageTitle": "Генератор взвешенного случайного выбора", + "howTo": [ + "Add items to the list and assign a weight to each one. Higher weight means higher chance of being picked.", + "Click Pick to select one random item based on the weights.", + "Use Pick N times to run multiple trials and see the distribution statistics." + ], + "whenTo": [ + "Running a raffle where some participants have more entries than others.", + "Making weighted decisions when options have different priorities.", + "Simulating probability distributions or testing randomness.", + "Creating balanced teams or assignments with different skill weights." + ], + "ruHowTo": [ + "Добавьте элементы в список и назначьте каждому вес. Чем выше вес, тем больше шанс быть выбранным.", + "Нажмите «Выбрать», чтобы случайно выбрать один элемент с учётом весов.", + "Используйте «Выбрать N раз» для множественных испытаний и просмотра статистики распределения." + ], + "ruWhenTo": [ + "Проведение розыгрыша, где у некоторых участников больше шансов, чем у других.", + "Принятие взвешенных решений, когда у вариантов разные приоритеты.", + "Моделирование распределений вероятностей или тестирование случайности.", + "Создание сбалансированных команд или назначений с разными весами навыков." + ], + "faq": [ + { "q": "How does weighted random selection work?", "a": "Each item's chance of being picked is proportional to its weight. If item A has weight 2 and item B has weight 8, B is 4 times more likely to be selected than A." }, + { "q": "Can I use decimal weights?", "a": "Yes, you can use any positive number as a weight, including decimals. The generator normalizes all weights automatically." }, + { "q": "Is the result truly random?", "a": "Yes. The generator uses a cryptographically secure random number generator to ensure fair and unpredictable results every time." } + ], + "ruFaq": [ + { "q": "Как работает взвешенный случайный выбор?", "a": "Шанс выбора каждого элемента пропорционален его весу. Если у элемента А вес 2, а у элемента Б вес 8, то Б в 4 раза чаще будет выбран, чем А." }, + { "q": "Можно ли использовать дробные веса?", "a": "Да, весом может быть любое положительное число, включая десятичные дроби. Генератор автоматически нормализует все веса." }, + { "q": "Результат действительно случайный?", "a": "Да. Генератор использует криптографически стойкий генератор случайных чисел, чтобы гарантировать честный и непредсказуемый результат каждый раз." } + ] +} diff --git a/src/pages/generators/country.astro b/src/pages/generators/country.astro new file mode 100644 index 0000000..7ca9dac --- /dev/null +++ b/src/pages/generators/country.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CountryGenerator from "@/components/generators/CountryGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "country")!; +--- + + + + diff --git a/src/pages/generators/lorem.astro b/src/pages/generators/lorem.astro new file mode 100644 index 0000000..fd121df --- /dev/null +++ b/src/pages/generators/lorem.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LoremGenerator from "@/components/generators/LoremGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "lorem")!; +--- + + + + diff --git a/src/pages/generators/palette.astro b/src/pages/generators/palette.astro new file mode 100644 index 0000000..6786ff5 --- /dev/null +++ b/src/pages/generators/palette.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PaletteGenerator from "@/components/generators/PaletteGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "palette")!; +--- + + + + diff --git a/src/pages/generators/weighted.astro b/src/pages/generators/weighted.astro new file mode 100644 index 0000000..ebcf2d8 --- /dev/null +++ b/src/pages/generators/weighted.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WeightedGenerator from "@/components/generators/WeightedGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "weighted")!; +--- + + + + diff --git a/src/pages/ru/generators/country.astro b/src/pages/ru/generators/country.astro new file mode 100644 index 0000000..7ca9dac --- /dev/null +++ b/src/pages/ru/generators/country.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CountryGenerator from "@/components/generators/CountryGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "country")!; +--- + + + + diff --git a/src/pages/ru/generators/lorem.astro b/src/pages/ru/generators/lorem.astro new file mode 100644 index 0000000..fd121df --- /dev/null +++ b/src/pages/ru/generators/lorem.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LoremGenerator from "@/components/generators/LoremGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "lorem")!; +--- + + + + diff --git a/src/pages/ru/generators/palette.astro b/src/pages/ru/generators/palette.astro new file mode 100644 index 0000000..6786ff5 --- /dev/null +++ b/src/pages/ru/generators/palette.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PaletteGenerator from "@/components/generators/PaletteGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "palette")!; +--- + + + + diff --git a/src/pages/ru/generators/weighted.astro b/src/pages/ru/generators/weighted.astro new file mode 100644 index 0000000..ebcf2d8 --- /dev/null +++ b/src/pages/ru/generators/weighted.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WeightedGenerator from "@/components/generators/WeightedGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "weighted")!; +--- + + + +