Skip to content

Feature/multilingual about#95

Merged
TiZorii merged 4 commits into
developfrom
feature/multilingual-about
Dec 31, 2025
Merged

Feature/multilingual about#95
TiZorii merged 4 commits into
developfrom
feature/multilingual-about

Conversation

@TiZorii
Copy link
Copy Markdown
Collaborator

@TiZorii TiZorii commented Dec 31, 2025

Summary by CodeRabbit

  • New Features
    • Added comprehensive internationalization with English, Polish, and Ukrainian support.
    • Localized user-facing text across About (hero, features, stats, pricing, community testimonials), Blog (filters/cards), and Footer.
    • Replaced hard-coded UI copy with translated strings for titles, descriptions, labels, CTAs, placeholders, and badges to improve multi-language UX.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link
Copy Markdown

netlify Bot commented Dec 31, 2025

Deploy Preview for develop-devlovers ready!

Name Link
🔨 Latest commit a6b2e5c
🔍 Latest deploy log https://app.netlify.com/projects/develop-devlovers/deploys/69556c36153e6c00085bcbf6
😎 Deploy Preview https://deploy-preview-95--develop-devlovers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

This PR adds internationalization across the frontend by introducing next-intl useTranslations hooks and replacing hard-coded UI strings with translation lookups; it also expands translation JSON files (English, Polish, Ukrainian) with new footer, about, pricing, and community keys and nested messages.

Changes

Cohort / File(s) Summary
About page components
frontend/components/about/CommunitySection.tsx, frontend/components/about/FeaturesSection.tsx, frontend/components/about/HeroSection.tsx, frontend/components/about/PricingSection.tsx, frontend/components/about/StatsSection.tsx
Added useTranslations hooks and replaced static strings with t(...) lookups across hero, features, pricing, stats, and community/testimonials. Feature/testimonial arrays now render translated content; minor formatting tweaks only.
Blog components
frontend/components/blog/BlogCard.tsx, frontend/components/blog/BlogFilters.tsx
Integrated useTranslations('blog') and localized UI text: search placeholder, button label (visitResource), remove/add/clear labels, and no-posts message (noPostsForTags). No logic changes.
Shared footer
frontend/components/shared/Footer.tsx
Added useTranslations('footer') and replaced footer strings (builtWith, byCommunity, privacyPolicy, termsOfService) with t(...) lookups.
Translation files
frontend/messages/en.json, frontend/messages/pl.json, frontend/messages/uk.json
Substantially expanded localization content: added footer, about (hero, stats, features, pricing, community), pricing features, community.testimonials, and blog keys (visitResource, noPostsForTags, updated searchPlaceholder`). Large content additions across three locales.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Sanity #79 — Overlaps changes to frontend/components/blog/BlogCard.tsx and BlogFilters.tsx (conflicting UI/text/localization edits).

Suggested reviewers

  • AM1007
  • ViktorSvertoka

Poem

🐰 Hopping through keys and strings anew,

I swapped each label for a localized view,
English, Polski, Українська too—
Now buttons and badges speak languages true,
A little hop, a global cheer from this rabbit crew 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/multilingual about' accurately describes the main objective of the changeset, which is adding internationalization (i18n) support across multiple about-related components and localization files.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d80791a and a6b2e5c.

📒 Files selected for processing (1)
  • frontend/components/about/PricingSection.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/components/about/PricingSection.tsx

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

Copy link
Copy Markdown
Member

@ViktorSvertoka ViktorSvertoka left a comment

Choose a reason for hiding this comment

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

Good job!

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: 1

🧹 Nitpick comments (4)
frontend/components/blog/BlogFilters.tsx (2)

91-102: Variable t shadows the translation function.

The filter callback uses t as the iteration variable, which shadows the useTranslations hook result. While this works because t from useTranslations isn't used inside these callbacks, it reduces code clarity and could cause confusion during maintenance.

🔎 Suggested fix: rename the iteration variable
   const removeTag = (tag: string) => {
-    setSelectedTags(prev => prev.filter(t => t !== tag));
+    setSelectedTags(prev => prev.filter(item => item !== tag));
   };

   const toggleTag = (raw: string) => {
     const norm = normalizeTag(raw);
     if (!norm) return;

     setSelectedTags(prev =>
-      prev.includes(norm) ? prev.filter(t => t !== norm) : [...prev, norm]
+      prev.includes(norm) ? prev.filter(item => item !== norm) : [...prev, norm]
     );
   };

259-263: Consider showing a different message when there are no posts at all vs. no posts for selected tags.

Currently, if filteredPosts is empty, t('noPostsForTags') is shown regardless of whether tags are selected. If no tags are selected and there are simply no posts, the message "No posts found for selected tags" would be misleading.

🔎 Suggested fix
       {!filteredPosts.length && (
         <p className="text-center text-gray-500 mt-10">
-          {t('noPostsForTags')}
+          {selectedTags.length > 0 ? t('noPostsForTags') : t('noPosts')}
         </p>
       )}
frontend/components/about/CommunitySection.tsx (1)

82-84: Using array index as React key in duplicated list.

Since testimonials are duplicated for the infinite scroll animation ([...testimonials, ...testimonials]), using index as the key will result in duplicate keys (e.g., two elements with key 0, 1, etc.). While this works because the list is static and never re-ordered, using a more unique key would be cleaner.

🔎 Suggested fix
-              {[...testimonials, ...testimonials].map((testimonial, index) => (
-                <TestimonialCard key={index} {...testimonial} />
+              {[...testimonials, ...testimonials].map((testimonial, index) => (
+                <TestimonialCard key={`${testimonial.name}-${index}`} {...testimonial} />
               ))}
frontend/components/about/FeaturesSection.tsx (1)

17-19: Consider lifting useTranslations out of FeatureCard.

useTranslations("about.features") is called inside FeatureCard, which means it runs once per card (6 times total). Since the only usage is t("learnMore") on line 49, consider either:

  1. Adding a learnMoreLabel prop to FeatureCard and passing the translated string from the parent
  2. Using a single translation instance from the parent FeaturesSection
🔎 Proposed fix: Pass translated string as prop
 interface FeatureCardProps {
   icon: React.ReactNode
   title: string
   description: string
   href: string
   className?: string
   children?: React.ReactNode
+  learnMoreLabel: string
 }

-function FeatureCard({ icon, title, description, href, className = "", children }: FeatureCardProps) {
-  const t = useTranslations("about.features")
-
+function FeatureCard({ icon, title, description, href, className = "", children, learnMoreLabel }: FeatureCardProps) {
   return (
     // ...
         <Link
           href={href}
           className="..."
         >
-          {t("learnMore")}
+          {learnMoreLabel}
         </Link>

Then in each <FeatureCard> usage, add learnMoreLabel={t("learnMore")}.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a08c581 and d80791a.

📒 Files selected for processing (11)
  • frontend/components/about/CommunitySection.tsx
  • frontend/components/about/FeaturesSection.tsx
  • frontend/components/about/HeroSection.tsx
  • frontend/components/about/PricingSection.tsx
  • frontend/components/about/StatsSection.tsx
  • frontend/components/blog/BlogCard.tsx
  • frontend/components/blog/BlogFilters.tsx
  • frontend/components/shared/Footer.tsx
  • frontend/messages/en.json
  • frontend/messages/pl.json
  • frontend/messages/uk.json
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/components/about/StatsSection.tsx (1)
frontend/db/seed-quiz-types.ts (1)
  • t (42-48)
🔇 Additional comments (17)
frontend/messages/en.json (1)

135-142: Testimonials use numeric string keys — this is intentional and works well with next-intl.

Using "0", "1", etc. as keys allows dynamic access via t(\testimonials.${index}`)` in components. This is a valid pattern for array-like translation data.

frontend/messages/uk.json (1)

56-144: LGTM!

The Ukrainian translation structure correctly mirrors the English version with all required keys present. The translations appear natural and appropriate.

frontend/components/blog/BlogCard.tsx (1)

5-18: LGTM!

Correct usage of useTranslations hook at the component level with the 'blog' namespace. The 'use client' directive is properly in place as required for client-side hooks.

frontend/components/shared/Footer.tsx (1)

1-6: LGTM!

The 'use client' directive is correctly added to support the useTranslations hook. The footer namespace is properly initialized for localized strings.

frontend/components/about/HeroSection.tsx (1)

6-9: LGTM!

Good use of nested namespace "about.hero" to scope translations. This keeps the component focused on its specific translation keys while maintaining a clean structure.

frontend/components/about/StatsSection.tsx (1)

19-33: LGTM on the i18n integration.

The useTranslations("about.stats") hook correctly provides localized labels. The GitHub stars fetch has appropriate error handling with a fallback value.

frontend/components/about/CommunitySection.tsx (1)

7-52: Coupling between testimonialData length and translation keys.

The pattern t(\testimonials.${index}`)works well but creates an implicit contract:testimonialDatahas 6 items, and translation files must have keys"0"through"5"`. If these get out of sync, you'll get missing translation warnings.

This is acceptable for a small, stable dataset. Consider adding a comment to document this dependency for future maintainers.

frontend/components/about/PricingSection.tsx (3)

116-117: Good use of separate translation namespaces.

Using two useTranslations hooks with different namespaces (about.pricing and about.pricing.features) improves readability and keeps translation calls concise. This is a clean pattern for nested translation structures.


141-147: LGTM!

Translation integration for the title, highlight, and subtitle follows the correct pattern.


191-209: LGTM!

CTA buttons and footer text properly use translation keys. The external "Buy Me a Coffee" link is correctly preserved as a static URL.

frontend/components/about/FeaturesSection.tsx (3)

82-83: Good separation of translation namespaces.

Using tProfile for the nested about.features.profile namespace keeps the translation calls clean and readable in the profile card content.


105-125: LGTM!

Feature cards for Q&A, Quiz, and Profile correctly use translation keys for titles and descriptions.


139-151: LGTM!

Profile card content (level, achievements, badges, progress labels) properly uses the tProfile namespace for all translated strings.

frontend/messages/pl.json (4)

56-61: LGTM!

Footer translations are well-structured with clear, concise keys.


62-112: LGTM!

The about section structure (hero, stats, features) is comprehensive and well-organized. The nested structure for profile badges and progress labels matches the component usage patterns.


113-131: LGTM!

Pricing section translations are complete with all feature keys matching those used in PricingSection.tsx.


135-142: No changes needed. The testimonials pattern using numeric string keys ("0", "1", etc.) is intentional and properly implemented. The code in CommunitySection.tsx already uses indexed access with t(testimonials.${index}) to correctly retrieve translations, and this pattern is consistent across all language files (en, pl, uk). The structure pairs structured metadata with indexed translations, which is a valid approach for handling array-like translation content.

Comment thread frontend/components/about/PricingSection.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.

2 participants