Skip to content

feat(landing): add Russian locale routes and translations#600

Open
DrMaks22 wants to merge 3 commits intojamiepine:mainfrom
DrMaks22:codex/ru-landing-pr
Open

feat(landing): add Russian locale routes and translations#600
DrMaks22 wants to merge 3 commits intojamiepine:mainfrom
DrMaks22:codex/ru-landing-pr

Conversation

@DrMaks22
Copy link
Copy Markdown

@DrMaks22 DrMaks22 commented May 2, 2026

Summary

Adds a Russian (/ru) route layer and Russian landing-page copy without changing the existing English URL structure.

Scope

This PR is intentionally limited to the landing app:

  • adds localized Russian routes for the marketing site
  • preserves the existing English routes as the default experience
  • introduces a small typed locale layer for landing-only content
  • localizes the main landing pages and shared sections used across them

Routes added

  • /ru
  • /ru/capture
  • /ru/download
  • /ru/sponsors
  • /ru/linux-install

Files changed

  • localized route layer under landing/src/app/[locale]/...
  • landing page entries under landing/src/app/...
  • shared landing components under landing/src/components/...
  • locale helpers in landing/src/lib/i18n.ts

Testing

  • bun --bun next build
  • git diff --check

Build output confirms the new Russian static routes are generated successfully alongside the existing English routes.

Related PRs

  • #599 adds the Russian desktop app locale
  • #601 adds the Russian-authored overview documentation

Non-goals

  • no desktop app i18n changes
  • no docs localization
  • no generated API docs changes
  • no changes to the existing English route structure

Notes

  • This is a follow-up to the Russian app-localization work, but it is technically independent and targets the landing app only.
  • The implementation intentionally avoids introducing a heavyweight cross-repo i18n framework and keeps the landing locale layer self-contained for easier review.

Summary by CodeRabbit

  • New Features
    • Full Russian localization across the landing site (pages, components, navigation, footer, and UI copy)
    • Locale-aware routing and layout with localized URLs and per-locale metadata
    • Dedicated Russian Linux installation page with step-by-step instructions
    • Improved download routing with platform detection and locale-aware redirects
    • Locale provider and hook for consistent in-app locale handling

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 553dfe52-d2fe-4a99-a780-e2a7b3402df9

📥 Commits

Reviewing files that changed from the base of the PR and between 62ab0ae and ffb6ff7.

📒 Files selected for processing (3)
  • landing/src/app/download/page.tsx
  • landing/src/components/CaptureSection.tsx
  • landing/src/components/CapturesMockup.tsx
✅ Files skipped from review due to trivial changes (1)
  • landing/src/components/CapturesMockup.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • landing/src/components/CaptureSection.tsx
  • landing/src/app/download/page.tsx

📝 Walkthrough

Walkthrough

Adds Russian locale support and runtime locale wiring: i18n utilities, a LocaleProvider, locale-aware layout and route handling, a Russian linux-install page, many components/pages split into EN/RU datasets and switched at render time, and localized routing helpers.

Changes

Locale Infrastructure & Routing

Layer / File(s) Summary
I18n Foundation
landing/src/lib/i18n.ts
Adds DEFAULT_LOCALE, SECONDARY_LOCALES, SUPPORTED_LOCALES, Locale/SecondaryLocale types, isSecondaryLocale/isLocale guards, getLocalizedPath, and getLocaleTag.
Locale Provider (client)
landing/src/components/LocaleProvider.tsx
Adds LocaleContext, LocaleProvider({ children, locale }) and useLocale() hook for consuming locale in client components.
Locale Layout & Static Params
landing/src/app/[locale]/layout.tsx
Adds dynamicParams = false, generateStaticParams() from SECONDARY_LOCALES, generateMetadata() merging LOCALE_METADATA with alternates, and LocaleLayout validating locales and wrapping children with LocaleProvider.
Download Route Handler
landing/src/app/[locale]/download/[platform]/route.ts
Adds export const dynamic = 'force-dynamic', header parsing/validation helpers, getPublicOrigin(request), and a GET handler that restricts to secondary locales, maps platform aliases, and issues 307 redirects to locale-aware targets (including /linux-install).
Locale page re-exports
landing/src/app/[locale]/{capture,download,page,sponsors}/page.tsx
Replace locale-scoped files with export { default } from '@/app/.../page' re-exports to delegate rendering to root page implementations.
Russian Linux install page
landing/src/app/[locale]/linux-install/page.tsx
Adds a Russian-language linux-install page with metadata and localized install instructions and GitHub links.

Page & Component Localization

Layer / File(s) Summary
Locale utilities consumed
landing/src/app/{page,download,capture,sponsors}.tsx, landing/src/components/*
Pages and components import useLocale, getLocalizedPath, and getLocaleTag to read current locale and build localized URLs/formatters.
Dataset splitting
landing/src/components/*
Static content constants split into *_EN and *_RU datasets across many components (Features, ApiSection, AgentIntegration, CaptureSection, CaptureHero, Personalities, SupportedModels, TutorialsSection, VoiceCreator, ControlUI, CapturesMockup, SponsorPromo, etc.).
Locale selection & rendering
landing/src/app/*, landing/src/components/*
Components call useLocale(), compute isRussian = locale === 'ru', select EN/RU datasets, and conditionally render localized strings, CTAs, headings, and number formatting.
Behavioral adjustments
landing/src/components/{ControlUI,CaptureSection,CapturesMockup,AgentIntegration,Personalities,Features}.tsx
Animation/rotation intervals and effects updated to depend on localized dataset lengths; some setInterval loops replaced by mounted-guarded setTimeout sequences for locale-sensitive stepping.
Localized navigation & links
landing/src/components/{Navbar,Footer,SponsorPromo}.tsx
Internal links updated to use getLocalizedPath(locale, ...), donate/star labels and footer/link texts localized.
Re-used page implementations
landing/src/app/[locale]/*/page.tsx
Locale route files re-export root page implementations, keeping a single canonical component while driving locale via context.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Server as ServerLayout
    participant Route as DownloadRoute
    participant LocaleProv as LocaleProvider
    participant Component as Page/Component
    participant Intl as Intl

    Browser->>Server: GET /ru/download
    Server->>Server: validate locale (isSecondaryLocale)
    Server->>Server: generateMetadata({locale: 'ru'})
    Server->>LocaleProv: render layout with locale='ru'
    Browser->>Route: GET /ru/download/linux
    Route->>Route: parse forwarded headers, validate origin
    Route-->>Browser: 307 redirect → locale-aware URL
    LocaleProv->>Component: provide locale via context
    Component->>Component: useLocale() → 'ru' → select *_RU datasets
    Component->>Intl: Intl.NumberFormat(getLocaleTag('ru'))
    Intl-->>Component: formatted numbers/text
    Component-->>Browser: render localized UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • rhmod09-dev

Poem

🐰 I hopped from / to /ru with care,

Paths prefixed, strings turned fair,
Datasets split, the pages sing,
Locale winds unfold their spring,
Now two tongues greet the landing there.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Russian locale routes and translations to the landing application.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@DrMaks22 DrMaks22 marked this pull request as ready for review May 2, 2026 21:36
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
landing/src/app/[locale]/layout.tsx (1)

37-39: ⚡ Quick win

Derive static params from the shared locale list.

Hard-coding ru here duplicates SECONDARY_LOCALES from landing/src/lib/i18n.ts. Pulling from the shared constant keeps this route manifest from drifting when another secondary locale is added.

♻️ Proposed fix
 import { LocaleProvider } from '@/components/LocaleProvider';
 import {
   getLocalizedPath,
   isSecondaryLocale,
+  SECONDARY_LOCALES,
   type SecondaryLocale,
 } from '@/lib/i18n';
@@
 export function generateStaticParams() {
-  return [{ locale: 'ru' }];
+  return SECONDARY_LOCALES.map((locale) => ({ locale }));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@landing/src/app/`[locale]/layout.tsx around lines 37 - 39, Replace the
hard-coded locale in generateStaticParams with the shared SECONDARY_LOCALES
constant: import SECONDARY_LOCALES from the i18n module and return an array
mapped from that constant (e.g., SECONDARY_LOCALES.map(locale => ({ locale })))
so the route manifest derives its params from the single source of truth; update
the generateStaticParams function to use this import and mapping and remove the
hard-coded 'ru'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@landing/src/app/`[locale]/download/[platform]/route.ts:
- Around line 14-47: The getPublicOrigin function reads x-forwarded-host and
x-forwarded-proto unsafely; update it (or implement a centralized proxy.ts and
call it) to split comma-separated header values and use only the first trimmed
value, validate the protocol is exactly "http" or "https", validate the host
string (e.g., allow only hostnames/host:port characters or attempt to construct
a URL and catch exceptions), and fall back to new URL(request.url).origin on any
invalid/missing header; ensure GET continues to call getPublicOrigin(request) so
redirects use the sanitized origin.

In `@landing/src/app/download/page.tsx`:
- Around line 249-257: The platform metadata (PLATFORMS) is not localized,
causing meta.description to remain English; update the code to provide
locale-specific platform definitions (e.g., PLATFORMS_RU and PLATFORMS_EN or a
function getPlatforms(locale)) and use that localized list wherever PLATFORMS is
referenced (including the grid rendering in page.tsx that uses meta.description
and the post-download success component), adjust the logic that selects meta by
meta.key (used alongside isRussian) to instead choose the correct localized meta
object so macOS/linux descriptions and any reuse in the success state render in
Russian when isRussian is true.

In `@landing/src/components/CaptureSection.tsx`:
- Around line 266-283: The useEffect's step() schedules two setTimeouts that are
not cleared on unmount, so track their IDs (e.g., store timeout IDs in local
variables or an array inside the effect) when calling setTimeout in step(),
clear those timeouts with clearTimeout in the cleanup, and also clear any
pending timeouts before scheduling new ones inside step(); reference the
existing identifiers useEffect, step, setShowClean, setPairIdx, iv, and
pairs.length so you locate and update the timing logic.

In `@landing/src/components/CapturesMockup.tsx`:
- Around line 36-44: Update the Russian sidebar labels in SIDEBAR_ITEMS_RU so
all entries are localized: replace the label for the AudioLines item from
"Stories" to "Истории" and the label for the Mic item from "Captures" to
"Записи" (locate the array SIDEBAR_ITEMS_RU and edit the objects with icon
AudioLines and icon Mic accordingly).

---

Nitpick comments:
In `@landing/src/app/`[locale]/layout.tsx:
- Around line 37-39: Replace the hard-coded locale in generateStaticParams with
the shared SECONDARY_LOCALES constant: import SECONDARY_LOCALES from the i18n
module and return an array mapped from that constant (e.g.,
SECONDARY_LOCALES.map(locale => ({ locale }))) so the route manifest derives its
params from the single source of truth; update the generateStaticParams function
to use this import and mapping and remove the hard-coded 'ru'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48a2b9bd-2fc6-4931-bed3-5de4f2500b29

📥 Commits

Reviewing files that changed from the base of the PR and between b35b909 and 2acd4c2.

📒 Files selected for processing (27)
  • landing/src/app/[locale]/capture/page.tsx
  • landing/src/app/[locale]/download/[platform]/route.ts
  • landing/src/app/[locale]/download/page.tsx
  • landing/src/app/[locale]/layout.tsx
  • landing/src/app/[locale]/linux-install/page.tsx
  • landing/src/app/[locale]/page.tsx
  • landing/src/app/[locale]/sponsors/page.tsx
  • landing/src/app/capture/page.tsx
  • landing/src/app/download/page.tsx
  • landing/src/app/page.tsx
  • landing/src/app/sponsors/page.tsx
  • landing/src/components/AgentIntegration.tsx
  • landing/src/components/ApiSection.tsx
  • landing/src/components/CaptureHero.tsx
  • landing/src/components/CaptureSection.tsx
  • landing/src/components/CapturesMockup.tsx
  • landing/src/components/ControlUI.tsx
  • landing/src/components/Features.tsx
  • landing/src/components/Footer.tsx
  • landing/src/components/LocaleProvider.tsx
  • landing/src/components/Navbar.tsx
  • landing/src/components/Personalities.tsx
  • landing/src/components/SponsorPromo.tsx
  • landing/src/components/SupportedModels.tsx
  • landing/src/components/TutorialsSection.tsx
  • landing/src/components/VoiceCreator.tsx
  • landing/src/lib/i18n.ts

Comment thread landing/src/app/[locale]/download/[platform]/route.ts
Comment thread landing/src/app/download/page.tsx Outdated
Comment thread landing/src/components/CapturesMockup.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
landing/src/components/CapturesMockup.tsx (1)

541-541: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the main section title for Russian locale.

Line 541 is still hardcoded to Captures, so /ru shows mixed-language UI in a primary heading.

♻️ Proposed fix
-                  <h1 className="text-2xl px-4 font-bold">Captures</h1>
+                  <h1 className="text-2xl px-4 font-bold">
+                    {isRussian ? 'Записи' : 'Captures'}
+                  </h1>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@landing/src/components/CapturesMockup.tsx` at line 541, The main section
title in the CapturesMockup component is hardcoded as "Captures"; update the
component to use the app's localization API instead (e.g., replace the literal
"Captures" in the <h1> inside CapturesMockup with a translated string like
t('captures') or i18n.t('captures') / <Trans> wrapper as used elsewhere) so the
title is localized for the Russian locale; ensure you import/use the same
translation hook or helper that other components in the project use and use the
key 'captures' (or add that key to the translation files) so /ru renders the
translated heading.
♻️ Duplicate comments (1)
landing/src/app/download/page.tsx (1)

37-42: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Finish localizing the Russian platform metadata.

PLATFORMS_RU still leaves the macOS descriptions in English, so Russian users will still see mixed-language copy in both the download grid and the post-click message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@landing/src/app/download/page.tsx` around lines 37 - 42, PLATFORMS_RU
contains English descriptions for the macOS entries; update the description
fields for the objects with key 'macArm' and 'macIntel' in PLATFORMS_RU to their
Russian equivalents (so the download grid and any post-click messages that read
from PLATFORMS_RU show fully localized copy), and verify that any code reading
PLATFORMS_RU (e.g., the download tile renderer and post-click message component)
uses those description values rather than hardcoded English strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@landing/src/components/CaptureSection.tsx`:
- Around line 187-192: The interval cycling in the useEffect that updates
setActiveIdx uses only engines.length as a dependency, so when the app locale
changes but engines.length stays the same the animation keeps its previous
position; update the effect to also depend on the locale (e.g., the i18n
language or locale prop) and reset the animation state when locale changes by
calling setActiveIdx(0) (or otherwise reinitializing the index) before/when
starting the new interval; reference the existing useEffect, setActiveIdx, and
engines.length to locate and modify the hook.
- Around line 266-302: The effect that cycles examples (useEffect containing
step, clearPendingTimeouts, revealTimeout, advanceTimeout) currently only
depends on pairs.length, so switching locale can leave pairIdx and animations
mid-stream; include locale in the dependency array and reset the cycling state
when locale changes (e.g., clearPendingTimeouts, setPairIdx(0) or another
initial index, and let step restart) so the timer loop and reveal/advance
timeouts restart cleanly on locale switch.

---

Outside diff comments:
In `@landing/src/components/CapturesMockup.tsx`:
- Line 541: The main section title in the CapturesMockup component is hardcoded
as "Captures"; update the component to use the app's localization API instead
(e.g., replace the literal "Captures" in the <h1> inside CapturesMockup with a
translated string like t('captures') or i18n.t('captures') / <Trans> wrapper as
used elsewhere) so the title is localized for the Russian locale; ensure you
import/use the same translation hook or helper that other components in the
project use and use the key 'captures' (or add that key to the translation
files) so /ru renders the translated heading.

---

Duplicate comments:
In `@landing/src/app/download/page.tsx`:
- Around line 37-42: PLATFORMS_RU contains English descriptions for the macOS
entries; update the description fields for the objects with key 'macArm' and
'macIntel' in PLATFORMS_RU to their Russian equivalents (so the download grid
and any post-click messages that read from PLATFORMS_RU show fully localized
copy), and verify that any code reading PLATFORMS_RU (e.g., the download tile
renderer and post-click message component) uses those description values rather
than hardcoded English strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fade26a-e206-4a15-8bbc-badb95c4dece

📥 Commits

Reviewing files that changed from the base of the PR and between 2acd4c2 and 62ab0ae.

📒 Files selected for processing (5)
  • landing/src/app/[locale]/download/[platform]/route.ts
  • landing/src/app/[locale]/layout.tsx
  • landing/src/app/download/page.tsx
  • landing/src/components/CaptureSection.tsx
  • landing/src/components/CapturesMockup.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • landing/src/app/[locale]/layout.tsx
  • landing/src/app/[locale]/download/[platform]/route.ts

Comment thread landing/src/components/CaptureSection.tsx Outdated
Comment thread landing/src/components/CaptureSection.tsx Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant