Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Website] - Added Internationalization Support (English & French) (#2479) #2527

Open
wants to merge 28 commits into
base: staging
Choose a base branch
from

Conversation

TroyMoses
Copy link
Contributor

@TroyMoses TroyMoses commented Feb 25, 2025

Summary of Changes (What does this PR do?)

This PR introduces internationalization (i18n) support for the frontend, allowing the application to support English and French. It leverages next-intl for language translation and routing.

What’s been done so far:

  • Installed next-intl for handling translations.
  • Configured next.config.mjs to integrate next-intl.
  • Added translation JSON files:
    • public/locales/en.json (English translations)
    • public/locales/fr.json (French translations)
  • Created i18n configuration files:
    • src/config.ts → Defines supported locales and pathnames.
    • src/i18n.ts → Handles locale-based message loading.
    • src/middleware.ts → Middleware for locale redirection.
    • src/navigation.ts → Provides a custom Link component for localized navigation.
  • Refactored the app folder:
    • Moved all pages in the app directory into [locale] to support dynamic language routing.
    • Wrapped the children in the src/app/[locale]/layout.tsx file with <NextIntlClientProvider> to manage translations.
  • Implemented translations of some sections in the Homepage (src/views/home/HomePage.tsx):
    • Used useTranslations('home') to fetch localized text.
    • Updated key sections like ReversibleContentSection, AnalyticsContentSection, AppDownloadSection, and FeaturedCarousel to support translations.
    • Ensured smooth animations while preserving the translated content.

🚧 What’s still in progress?

  • Adding a language switch button to toggle between English and French.
  • Translating the rest of the pages of the website.

Status of maturity (all need to be checked before merging):

How should this be manually tested?

  1. Setup the project:
    npm install next-intl
    npm run dev
    
  2. Verify locale support:
  • Open the homepage (/en and /fr) and confirm that translations are loading correctly.
  • Check if the translated content updates dynamically in Home Page.
  1. Confirm middleware routing:
  • Try accessing / (should redirect to /en).
  • Try manually navigating to /fr and confirm the page loads in French.

What are the relevant tickets?

Screenshots (optional)

**In English

en

**In French

fr

Summary by CodeRabbit

  • New Features

    • Improved password update experience with clearer error messaging to guide users on meeting password requirements.
    • Expanded internationalization support with added French language content and updated interface text for enhanced clarity.
    • New localization files introduced for English and French, enhancing user engagement with relevant information.
    • New command added to the Dockerfile for streamlined container startup processes.
    • New CleanAirPage component added to display information about the CLEAN-Air Network.
  • Chores

    • Reformatting and reorganization of various code files for consistency and readability without functional changes.
    • Re-addition of existing environment variable definitions in .env.sample files for both the website and mobile applications.
    • Complete reformatting of multiple components and files, ensuring consistent code style without altering functionality.

Copy link

coderabbitai bot commented Feb 25, 2025

📝 Walkthrough

Walkthrough

This pull request restores a previously removed API key declaration in an Android properties file, adds a new error message for password validation in a UI component, and updates a Dockerfile to introduce a new entry point command. Most other files have been reformatted through whitespace, indentation, or reordering of properties for improved clarity without changing functionality.

Changes

Files Change Summary
src/mobile-v3/android/local.defaults.properties Restores the PLACES_API_KEY_DEV=DEFAULT_API_KEY line.
src/website/Dockerfile Adds a new CMD instruction to run migrations, collect static files, and start Gunicorn.
src/mobile-v3/ios/…/LaunchImage.imageset/Contents.json Inserts blank lines for improved readability.
src/mobile/ios/…/LaunchImage.imageset/Contents.json Reorders JSON properties (e.g., moving filename after idiom and repositioning author).
src/netmanager-app/components/PasswordEdit.tsx Introduces an error message for password validation using a new check against a regex.
src/netmanager-app/components/layout.tsx, sidebar.tsx, topbar.tsx, ApiTokens.tsx Applies minor formatting changes, including adding the "use client"; directive and an extra comma for syntactical consistency.
Multiple files (in src/platform, src/website, and src/website2) Implements widespread reformatting and whitespace adjustments without altering functional logic.
src/website2/next.config.mjs, src/website2/package.json, src/website2/public/locales/* Adds Next Intl plugin support and introduces new locale JSON files for internationalization.

Sequence Diagram(s)

sequenceDiagram
    participant Container as Docker Container
    participant Django as Django Process
    participant Gunicorn as Gunicorn Server

    Container->>Django: Run "python manage.py migrate"
    Container->>Django: Run "python manage.py collectstatic --noinput"
    Django-->>Container: Migrations & static collection complete
    Container->>Gunicorn: Execute "gunicorn --bind=0.0.0.0:8080 backend.wsgi"
    Gunicorn-->>Container: Application now running
Loading

Possibly related PRs

Suggested labels

ready for review

Suggested reviewers

  • Baalmart
  • Codebmk

Poem

In lines of code we find our art,
Whitespace, order—a clean restart.
API keys restored with care,
Docker wakes the server fair.
Reformatting brings fresh light 😊,
A subtle win in code’s own fight!
Cheers to progress day and night!

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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 (16)
src/website/frontend/ErrorBoundary.jsx (1)

43-44: Adopt Optional Chaining for Error Details Rendering

The current conditional rendering using logical AND can be streamlined. Replacing it with optional chaining improves readability and reduces risk of runtime errors when the properties are undefined.

-                <p>{this.state.error && this.state.error.toString()}</p>
-                <pre>{this.state.errorInfo && this.state.errorInfo.componentStack}</pre>
+                <p>{this.state.error?.toString()}</p>
+                <pre>{this.state.errorInfo?.componentStack}</pre>
🧰 Tools
🪛 Biome (1.9.4)

[error] 43-43: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/platform/src/core/hooks/useGetActiveGroupId.jsx (2)

1-92: File naming inconsistency with exported function.

The filename useGetActiveGroupId.jsx doesn't match the exported function name useGetActiveGroup. This naming inconsistency could lead to confusion for developers trying to locate or import this hook.

Consider renaming either:

  1. The file to useGetActiveGroup.jsx to match the exported function name, or
  2. The function to useGetActiveGroupId to match the file name

This will improve code maintainability and make imports more intuitive.


28-91: Multiple return statements with similar structures could be refactored.

The function contains multiple return statements with similar structures but slight variations, which works but might be harder to maintain long-term.

Consider refactoring to use a single return statement with conditionally determined values:

export function useGetActiveGroup() {
  const [activeGroup, setActiveGroup] = useState(null);
  const [loading, setLoading] = useState(true);
  const userInfo = useSelector((state) => state?.login?.userInfo);
  const chartData = useSelector((state) => state.chart);

  useEffect(() => {
    setLoading(true);

    const matchingGroup = findGroupByOrgName(
      userInfo?.groups,
      chartData?.organizationName,
    );

    setActiveGroup(matchingGroup);
    setLoading(false);
  }, [chartData?.organizationName]);

  // Determine the group to use based on various conditions
  let id = null;
  let title = null;
  let selectedGroup = null;

  if (userInfo && userInfo.groups && chartData?.organizationName) {
    // Prioritize stored group if it exists in user's groups
    const storedGroupInUserGroups = findGroupByOrgName(
      userInfo.groups,
      chartData.organizationName,
    );

    if (storedGroupInUserGroups) {
      selectedGroup = storedGroupInUserGroups;
    } else {
      // Find group matching chart organization name
      const matchingGroup = findGroupByOrgName(
        userInfo.groups,
        chartData.organizationName,
      );
      
      if (matchingGroup) {
        selectedGroup = matchingGroup;
      } else if (userInfo.groups.length > 0) {
        // Fallback to first group if available
        selectedGroup = userInfo.groups[0];
      }
    }
  } else {
    // Use activeGroup from state if no userInfo or groups
    selectedGroup = activeGroup;
  }

  // Construct the return object
  return {
    loading,
    id: selectedGroup?._id || null,
    title: selectedGroup?.grp_title || null,
    userID: userInfo?._id || null,
    groupList: userInfo?.groups || [],
  };
}

This approach centralizes the logic and makes the function easier to understand and maintain.

src/mobile-v3/android/local.defaults.properties (1)

1-1: Verify if this empty line addition is intentional.

The addition of an empty line at the beginning of the file doesn't impact functionality but might affect file parsing in some environments. Consider whether this change is intentional or could be omitted.

Also applies to: 5-5

src/website2/public/locales/en.json (1)

1-24: Well-structured English localization file.

The English localization file is well-organized with a clear hierarchical structure for the "home" section. The content provides comprehensive text for various UI elements including titles, descriptions, and call-to-action buttons.

However, I'd recommend implementing some best practices for localization files:

 {
     "home": {
       "title": "Home | AirQo",
       "description": "Explore the air quality monitoring data and tools by AirQo.",
       "keywords": "air quality, pollution, monitoring, AirQo, environment",
+      "sections": {
+        "highResolution": {
+          "title": "High-resolution air quality monitoring network",
+          "monitor": "Air Quality Monitor",
+          "description": "We deploy a high-resolution air quality monitoring network in target urban areas across Africa to increase awareness and understanding of air quality management, provide actionable information, and derive actions against air pollution.",
+          "cta": "Learn more"
+        },
+        "analytics": {
+          "title": "An interactive air quality analytics platform",
+          "monitor": "Air Quality Analytics",
+          "description": "Access and visualise real-time and historical air quality information across Africa through our easy-to-use air quality analytics dashboard.",
+          "cta": "Learn more"
+        }
       },
-      "highResolutionTitle": "High-resolution air quality monitoring network",
-      "airQualityMonitor": "Air Quality Monitor",
-      "monitorDescription": "We deploy a high-resolution air quality monitoring network in target urban areas across Africa to increase awareness and understanding of air quality management, provide actionable information, and derive actions against air pollution.",
-      "learnMore": "Learn more",
-      "analyticsTitle": "An interactive air quality analytics platform",
-      "airQualityAnalytics": "Air Quality Analytics",
-      "analyticsDescription": "Access and visualise real-time and historical air quality information across Africa through our easy-to-use air quality analytics dashboard.",
       // ... (similar nesting pattern for other sections)
     }
   }

This nested structure will make the file more maintainable as it grows and clearly groups related strings together.

src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx (2)

105-109: Unnecessary Fragment can be simplified.

The Fragment wrapping the glossarySections mapping is unnecessary when it has only one child element.

- {glossarySections && glossarySections.length > 0 && (
-   <>
-     {glossarySections.map((section: any) => (
-       <SectionDisplay key={section.id} section={section} />
-     ))}
-   </>
- )}
+ {glossarySections && glossarySections.length > 0 && 
+   glossarySections.map((section: any) => (
+     <SectionDisplay key={section.id} section={section} />
+   ))
+ }
🧰 Tools
🪛 Biome (1.9.4)

[error] 105-109: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


39-43: Type safety improvement needed.

The code uses any type for section objects, which reduces type safety. Since you're filtering based on specific properties, consider defining a proper interface.

+ interface Section {
+   id: string | number;
+   pages: string[];
+   content: any; // Consider a more specific type
+ }

- const glossarySections = selectedEvent.sections?.filter((section: any) => {
+ const glossarySections = selectedEvent.sections?.filter((section: Section) => {
    if (!section.pages.includes('glossary')) return false;
    const html = renderContent(section.content);
    return html.trim().length > 0;
  });
src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx (1)

88-93: Consider removing unnecessary Fragment.

The Fragment wrapper is redundant here as it contains only a single child element and doesn't require keying.

-      {committeeSections.length > 0 && (
-        <>
-          {committeeSections.map((section: any) => (
-            <SectionDisplay key={section.id} section={section} />
-          ))}
-        </>
-      )}
+      {committeeSections.length > 0 && (
+        committeeSections.map((section: any) => (
+          <SectionDisplay key={section.id} section={section} />
+        ))
+      )}
🧰 Tools
🪛 Biome (1.9.4)

[error] 88-92: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx (1)

137-142: Consider removing unnecessary Fragment.

This Fragment is redundant as it only contains a single map operation and doesn't require keying.

-      {speakersExtraSections && speakersExtraSections.length > 0 && (
-        <>
-          {speakersExtraSections.map((section: any) => (
-            <SectionDisplay key={section.id} section={section} />
-          ))}
-        </>
-      )}
+      {speakersExtraSections && speakersExtraSections.length > 0 && (
+        speakersExtraSections.map((section: any) => (
+          <SectionDisplay key={section.id} section={section} />
+        ))
+      )}
🧰 Tools
🪛 Biome (1.9.4)

[error] 137-141: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx (1)

118-123: Remove unnecessary Fragment wrappers.

There are two unnecessary Fragment wrappers that could be simplified for cleaner code.

-      {sessionSections && sessionSections.length > 0 && (
-        <>
-          {sessionSections.map((section: any) => (
-            <SectionDisplay key={section.id} section={section} />
-          ))}
-        </>
-      )}
+      {sessionSections && sessionSections.length > 0 && (
+        sessionSections.map((section: any) => (
+          <SectionDisplay key={section.id} section={section} />
+        ))
+      )}

-      <>
-        {selectedEvent.programs?.map((program: any) => (
-          <AccordionItem
-            key={program.id}
-            title={program.title}
-            subText={program.sub_text}
-            sessions={program.sessions}
-            isOpen={openAccordion === program.id}
-            onToggle={() => handleToggle(program.id)}
-          />
-        ))}
-      </>
+      {selectedEvent.programs?.map((program: any) => (
+        <AccordionItem
+          key={program.id}
+          title={program.title}
+          subText={program.sub_text}
+          sessions={program.sessions}
+          isOpen={openAccordion === program.id}
+          onToggle={() => handleToggle(program.id)}
+        />
+      ))}

Also applies to: 125-136

🧰 Tools
🪛 Biome (1.9.4)

[error] 118-122: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx (1)

51-57: Unnecessary Fragment Wrapper:
The fragment wrapping the sponsorship sections mapping appears redundant since it isn’t adding extra structure. Simplifying it might improve readability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 52-56: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx (1)

86-90: Streamline Additional Logistics Sections:
The extra fragment wrapping the mapped logistics sections may be unnecessary. Removing it could simplify the JSX without affecting layout or functionality.

🧰 Tools
🪛 Biome (1.9.4)

[error] 86-90: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx (2)

17-44: Refactor Repeated Partner Filtering:
The filtering and mapping logic for convening, host, program, and funding partners is very similar. Abstracting this to a helper function could reduce repetition and streamline future maintenance.


68-72: Simplify JSX for Partners Sections:
The fragment wrapping the partners sections mapping may not be necessary. Removing it can simplify the markup without impacting rendering.

🧰 Tools
🪛 Biome (1.9.4)

[error] 68-72: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/README.md (1)

27-35: Markdown Formatting Reminders:
Some headings trigger markdown lint warnings (e.g., trailing punctuation and heading style). Addressing these will improve the overall consistency and aesthetics of the documentation.

src/website/Dockerfile (1)

64-70: New CMD Command – Ensure Database Readiness:
The addition of the CMD that runs migrations, collects static files, and then starts Gunicorn is a solid approach. However, confirm that the database is accessible when the container starts—consider a wait-for-db script or mechanism to handle startup dependencies if needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7a7ee4 and 45fd28a.

⛔ Files ignored due to path filters (14)
  • src/platform/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • src/website/package-lock.json is excluded by !**/package-lock.json
  • src/website2/package-lock.json is excluded by !**/package-lock.json
  • src/website2/public/assets/images/partners/UN.svg is excluded by !**/*.svg
  • src/website2/public/assets/images/partners/enabel.svg is excluded by !**/*.svg
  • src/website2/public/assets/images/partners/google.svg is excluded by !**/*.svg
  • src/website2/public/assets/images/partners/usmissionuganda.svg is excluded by !**/*.svg
  • src/website2/public/assets/images/partners/worldbankgroup.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Community.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Monitor.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Network.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Partners.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Publications.svg is excluded by !**/*.svg
  • src/website2/public/assets/svgs/ImpactNumbers/Records.svg is excluded by !**/*.svg
📒 Files selected for processing (107)
  • src/mobile-v3/android/local.defaults.properties (1 hunks)
  • src/mobile-v3/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (2 hunks)
  • src/mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (1 hunks)
  • src/netmanager-app/components/Settings/ApiTokens.tsx (9 hunks)
  • src/netmanager-app/components/Settings/PasswordEdit.tsx (4 hunks)
  • src/netmanager-app/components/layout.tsx (1 hunks)
  • src/netmanager-app/components/sidebar.tsx (1 hunks)
  • src/netmanager-app/components/topbar.tsx (3 hunks)
  • src/netmanager/.env.sample (1 hunks)
  • src/platform/next-env.d.ts (1 hunks)
  • src/platform/public/icons/Analytics/checkCircleIcon.js (1 hunks)
  • src/platform/src/common/components/AQNumberCard/components/index.jsx (1 hunks)
  • src/platform/src/common/components/Charts/components/SkeletonLoader.jsx (1 hunks)
  • src/platform/src/common/components/Charts/components/StandardsMenu.jsx (1 hunks)
  • src/platform/src/common/components/Charts/constants/index.jsx (1 hunks)
  • src/platform/src/common/components/Charts/utils/index.jsx (1 hunks)
  • src/platform/src/common/components/Modal/dataDownload/components/TableLoadingSkeleton.jsx (1 hunks)
  • src/platform/src/common/components/Modal/dataDownload/modules/SelectMore.jsx (1 hunks)
  • src/platform/src/common/components/Toast/CustomToast.jsx (1 hunks)
  • src/platform/src/core/hooks/useDataDownload.jsx (1 hunks)
  • src/platform/src/core/hooks/useFetchAnalyticsData.jsx (1 hunks)
  • src/platform/src/core/hooks/useGetActiveGroupId.jsx (1 hunks)
  • src/platform/src/core/hooks/useInactivityLogout.jsx (1 hunks)
  • src/platform/src/core/hooks/useSitesSummary.jsx (1 hunks)
  • src/platform/src/core/hooks/useUserChecklists.jsx (1 hunks)
  • src/platform/src/core/hooks/useUserPreferences.jsx (1 hunks)
  • src/platform/src/core/utils/dateUtils.js (1 hunks)
  • src/platform/src/core/utils/formatDateRangeToISO.js (1 hunks)
  • src/platform/src/core/utils/useResizeObserver.js (1 hunks)
  • src/platform/src/lib/store/services/sitesSummarySlice/index.js (1 hunks)
  • src/platform/src/pages/api/proxy/analytics.js (1 hunks)
  • src/platform/src/pages/api/proxy/data-download.js (1 hunks)
  • src/platform/src/pages/api/proxy/sites.js (1 hunks)
  • src/website/.env.sample (1 hunks)
  • src/website/Dockerfile (1 hunks)
  • src/website/frontend/App.js (1 hunks)
  • src/website/frontend/ErrorBoundary.jsx (1 hunks)
  • src/website/frontend/index.js (1 hunks)
  • src/website/frontend/locales/en/translation.json (1 hunks)
  • src/website/frontend/reduxStore/AirQlouds/index.js (1 hunks)
  • src/website/frontend/src/components/Footer.js (1 hunks)
  • src/website/frontend/src/components/LoctionTracker/LocationTracker.js (1 hunks)
  • src/website/frontend/src/pages/Legal/AirQo_Data.js (1 hunks)
  • src/website/frontend/src/pages/Legal/AirQo_Payments.js (1 hunks)
  • src/website/frontend/src/pages/Legal/PrivacyPolicy.js (1 hunks)
  • src/website/frontend/src/pages/Legal/TermsOfService.js (1 hunks)
  • src/website/frontend/src/pages/Legal/index.js (1 hunks)
  • src/website/frontend/styles/Footer.scss (1 hunks)
  • src/website/frontend/styles/Legal.scss (1 hunks)
  • src/website/frontend/styles/index.scss (1 hunks)
  • src/website/package.json (1 hunks)
  • src/website/webpack.dev.config.js (1 hunks)
  • src/website/webpack.fullapp.config.js (1 hunks)
  • src/website2/.prettierignore (1 hunks)
  • src/website2/.prettierrc (1 hunks)
  • src/website2/Dockerfile (1 hunks)
  • src/website2/README.md (2 hunks)
  • src/website2/jest.setup.ts (1 hunks)
  • src/website2/next.config.mjs (2 hunks)
  • src/website2/package.json (1 hunks)
  • src/website2/public/assets/icons/Icon1.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon2.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon3.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon4.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon5.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon6.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon7.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon8.tsx (1 hunks)
  • src/website2/public/locales/en.json (1 hunks)
  • src/website2/public/locales/fr.json (1 hunks)
  • src/website2/src/app/[locale]/(about)/about-us/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/careers/[id]/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/careers/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/careers/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/events/[id]/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/events/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/events/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/press/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/(about)/resources/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/MaintenancePage.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/about/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/resources/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/events/[id]/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/events/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/membership/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/resources/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/ContactPage.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/form/FormPage.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/form/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/success/SuccessPage.tsx (1 hunks)
  • src/website2/src/app/[locale]/contact/success/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/error.tsx (1 hunks)
  • src/website2/src/app/[locale]/explore-data/ExplorePage.tsx (1 hunks)
⛔ Files not processed due to max files limit (13)
  • src/website2/src/app/[locale]/explore-data/layout.tsx
  • src/website2/src/app/[locale]/explore-data/mobile-app/page.tsx
  • src/website2/src/app/[locale]/explore-data/page.tsx
  • src/website2/src/app/[locale]/home/page.tsx
  • src/website2/src/app/[locale]/layout.tsx
  • src/website2/src/app/[locale]/legal/airqo-data/page.tsx
  • src/website2/src/app/[locale]/legal/layout.tsx
  • src/website2/src/app/[locale]/legal/payment-refund-policy/page.tsx
  • src/website2/src/app/[locale]/legal/privacy-policy/page.tsx
  • src/website2/src/app/[locale]/legal/terms-of-service/page.tsx
  • src/website2/src/app/[locale]/not-found.tsx
  • src/website2/src/app/[locale]/partners/[id]/page.tsx
  • src/website2/src/app/[locale]/partners/layout.tsx
✅ Files skipped from review due to trivial changes (91)
  • src/website2/src/app/[locale]/clean-air-network/events/[id]/page.tsx
  • src/netmanager-app/components/layout.tsx
  • src/netmanager-app/components/topbar.tsx
  • src/website2/src/app/[locale]/(about)/about-us/page.tsx
  • src/mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
  • src/website2/src/app/[locale]/(about)/press/page.tsx
  • src/platform/next-env.d.ts
  • src/website2/src/app/[locale]/clean-air-network/page.tsx
  • src/website2/src/app/[locale]/clean-air-network/membership/page.tsx
  • src/website/webpack.dev.config.js
  • src/website/frontend/index.js
  • src/website/webpack.fullapp.config.js
  • src/website2/src/app/[locale]/contact/form/page.tsx
  • src/website2/src/app/[locale]/(about)/layout.tsx
  • src/platform/src/pages/api/proxy/data-download.js
  • src/website2/src/app/[locale]/(about)/careers/page.tsx
  • src/platform/src/core/hooks/useDataDownload.jsx
  • src/website2/src/app/[locale]/contact/layout.tsx
  • src/website2/.prettierignore
  • src/platform/src/core/utils/useResizeObserver.js
  • src/platform/src/core/utils/dateUtils.js
  • src/netmanager-app/components/Settings/ApiTokens.tsx
  • src/platform/src/common/components/Charts/components/SkeletonLoader.jsx
  • src/website2/src/app/[locale]/clean-air-network/events/page.tsx
  • src/website2/.prettierrc
  • src/website2/src/app/[locale]/contact/success/page.tsx
  • src/platform/src/common/components/Modal/dataDownload/components/TableLoadingSkeleton.jsx
  • src/website2/src/app/[locale]/clean-air-network/resources/page.tsx
  • src/website/.env.sample
  • src/website/frontend/reduxStore/AirQlouds/index.js
  • src/website2/src/app/[locale]/(about)/careers/layout.tsx
  • src/platform/src/lib/store/services/sitesSummarySlice/index.js
  • src/website/frontend/App.js
  • src/platform/src/core/hooks/useSitesSummary.jsx
  • src/platform/src/core/hooks/useFetchAnalyticsData.jsx
  • src/website2/public/assets/icons/Icon5.tsx
  • src/website/package.json
  • src/website/frontend/src/pages/Legal/index.js
  • src/mobile-v3/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
  • src/website2/public/assets/icons/Icon3.tsx
  • src/website2/public/assets/icons/Icon1.tsx
  • src/website2/src/app/[locale]/(about)/events/page.tsx
  • src/website2/src/app/[locale]/(about)/resources/page.tsx
  • src/website2/Dockerfile
  • src/website/frontend/src/components/LoctionTracker/LocationTracker.js
  • src/platform/src/common/components/Charts/utils/index.jsx
  • src/website2/src/app/[locale]/clean-air-network/layout.tsx
  • src/website2/public/assets/icons/Icon6.tsx
  • src/website2/src/app/[locale]/error.tsx
  • src/platform/src/core/hooks/useInactivityLogout.jsx
  • src/platform/src/core/hooks/useUserChecklists.jsx
  • src/platform/src/common/components/AQNumberCard/components/index.jsx
  • src/netmanager-app/components/sidebar.tsx
  • src/website2/src/app/[locale]/(about)/events/layout.tsx
  • src/website2/src/app/[locale]/clean-air-forum/layout.tsx
  • src/website/frontend/styles/Footer.scss
  • src/website2/src/app/[locale]/contact/page.tsx
  • src/website2/src/app/[locale]/(about)/careers/[id]/page.tsx
  • src/website2/src/app/[locale]/clean-air-forum/resources/page.tsx
  • src/website/frontend/styles/Legal.scss
  • src/platform/src/common/components/Charts/constants/index.jsx
  • src/website2/src/app/[locale]/clean-air-forum/about/page.tsx
  • src/website2/public/locales/fr.json
  • src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx
  • src/website2/src/app/[locale]/explore-data/ExplorePage.tsx
  • src/platform/src/pages/api/proxy/analytics.js
  • src/website/frontend/src/pages/Legal/PrivacyPolicy.js
  • src/platform/src/core/hooks/useUserPreferences.jsx
  • src/netmanager/.env.sample
  • src/website2/public/assets/icons/Icon4.tsx
  • src/website2/public/assets/icons/Icon2.tsx
  • src/website2/src/app/[locale]/contact/success/SuccessPage.tsx
  • src/website/frontend/src/pages/Legal/TermsOfService.js
  • src/website/frontend/styles/index.scss
  • src/website/frontend/src/pages/Legal/AirQo_Payments.js
  • src/website2/jest.setup.ts
  • src/platform/src/common/components/Modal/dataDownload/modules/SelectMore.jsx
  • src/website2/public/assets/icons/Icon8.tsx
  • src/website2/src/app/[locale]/contact/ContactPage.tsx
  • src/platform/src/common/components/Charts/components/StandardsMenu.jsx
  • src/website2/public/assets/icons/Icon7.tsx
  • src/website/frontend/src/pages/Legal/AirQo_Data.js
  • src/website2/src/app/[locale]/(about)/events/[id]/page.tsx
  • src/platform/src/pages/api/proxy/sites.js
  • src/platform/src/core/utils/formatDateRangeToISO.js
  • src/website2/src/app/[locale]/MaintenancePage.tsx
  • src/website/frontend/locales/en/translation.json
  • src/website/frontend/src/components/Footer.js
  • src/platform/public/icons/Analytics/checkCircleIcon.js
  • src/website2/src/app/[locale]/contact/form/FormPage.tsx
  • src/platform/src/common/components/Toast/CustomToast.jsx
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
src/website2/README.md

4-4: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


26-26: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


39-39: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)


39-39: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

🪛 Biome (1.9.4)
src/website/frontend/ErrorBoundary.jsx

[error] 43-43: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx

[error] 95-95: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 105-109: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx

[error] 53-53: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 75-75: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 86-90: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx

[error] 60-60: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 68-72: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx

[error] 78-78: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 88-92: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx

[error] 48-48: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 66-66: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 110-110: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 118-122: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


[error] 125-136: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


[error] 147-147: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx

[error] 81-81: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 137-141: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx

[error] 43-43: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 52-56: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🔇 Additional comments (22)
src/platform/src/core/hooks/useGetActiveGroupId.jsx (1)

1-92: Missing the i18n integration mentioned in PR objective.

This file doesn't contain any internationalization (i18n) related code despite the PR objective mentioning i18n support implementation.

If this file should support internationalization, consider integrating with the next-intl library as mentioned in the PR objective. For example, any user-facing strings should be translated using the appropriate i18n functions.

However, since this hook appears to only deal with internal data structures and doesn't output any user-facing strings, it might not need i18n integration.

src/website2/package.json (1)

36-36: Addition of next-intl dependency for internationalization support.

The inclusion of the next-intl package (v3.26.4) aligns perfectly with the PR objectives to add internationalization support for both English and French languages. This is the foundation for the multi-language capabilities being implemented.

src/mobile-v3/android/local.defaults.properties (1)

5-5: API key restoration appears unrelated to internationalization.

The restoration of PLACES_API_KEY_DEV=DEFAULT_API_KEY doesn't seem to be related to the internationalization efforts mentioned in the PR objectives. This change might belong in a separate PR focused on API configurations.

Please confirm if this change is intentional and necessary for this internationalization PR, or if it was accidentally included.

src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx (2)

1-115: The file structure supports internationalization but lacks i18n implementation.

The file path includes the [locale] parameter indicating it's part of the internationalization routing structure, but the component itself doesn't utilize any internationalization features like text translation.

Consider adding translation support for user-facing strings in this component. For example:

import { useTranslations } from 'next-intl';

const GlossaryPage: React.FC = () => {
  const t = useTranslations('glossary');
  
  // Then use t() for translations:
  // <h1>{t('cleanAirForumEvents')}</h1>
  // instead of hardcoded "Clean Air Forum Events"
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 105-109: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


95-97: Security concern with dangerouslySetInnerHTML usage.

While you're using DOMPurify to sanitize the HTML content, directly rendering HTML is always a potential security risk. Consider additional measures to ensure the glossary content is safe.

<div
  className="md:w-2/3"
  dangerouslySetInnerHTML={{
    __html: DOMPurify.sanitize(glossaryHTML),
  }}
></div>

Consider implementing additional safeguards:

  1. Restrict allowed HTML tags and attributes in DOMPurify configuration
  2. Add server-side validation of content before it reaches the client
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/next.config.mjs (1)

1-3: Well-implemented next-intl configuration.

The integration of next-intl with the Next.js configuration is clean and follows best practices. The plugin is properly imported, initialized, and applied to the existing configuration without disrupting other settings.

This configuration is essential for the internationalization features to work correctly throughout the application, enabling the routing and message loading for different locales.

Also applies to: 57-57

src/netmanager-app/components/Settings/PasswordEdit.tsx (2)

58-59: Good addition of detailed error message for password validation.

The error message "Password must meet all requirements" provides clear feedback to users when their password doesn't match the required regex pattern. This improves the user experience by explaining why their input was rejected.


3-3: Minor whitespace changes.

These whitespace additions maintain consistent code style throughout the file.

Also applies to: 14-14, 144-144

src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx (2)

1-121: Formatting changes maintain functionality while improving readability.

The file has been thoroughly reformatted with consistent spacing and indentation while preserving all functionality. The component logic, hooks usage, and rendering approach remain unchanged.

🧰 Tools
🪛 Biome (1.9.4)

[error] 78-78: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 88-92: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


78-80: XSS protection is properly implemented.

The use of dangerouslySetInnerHTML is appropriately paired with DOMPurify.sanitize(), which mitigates the risk of cross-site scripting attacks when rendering potentially untrusted HTML content.

🧰 Tools
🪛 Biome (1.9.4)

[error] 78-78: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx (2)

1-148: Consistent formatting applied throughout the file.

The code has been reformatted with consistent spacing and indentation while maintaining all functionality. This improves readability without changing the component's behavior.

🧰 Tools
🪛 Biome (1.9.4)

[error] 81-81: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 137-141: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


81-83: Proper HTML sanitization in place.

The dangerouslySetInnerHTML usage is correctly paired with DOMPurify.sanitize(), which helps protect against XSS vulnerabilities when rendering HTML content.

🧰 Tools
🪛 Biome (1.9.4)

[error] 81-81: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx (2)

1-159: Comprehensive formatting with preserved functionality.

The entire file has been reformatted with consistent spacing and indentation while maintaining all functional aspects. The component logic, state management, and rendering approach remain unchanged.

🧰 Tools
🪛 Biome (1.9.4)

[error] 48-48: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 66-66: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 110-110: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 118-122: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


[error] 125-136: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


[error] 147-147: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


47-49: Consistent HTML sanitization across the component.

All instances of dangerouslySetInnerHTML are appropriately using either renderContent or DOMPurify.sanitize(), which helps protect against XSS vulnerabilities when rendering HTML content from potentially untrusted sources.

Also applies to: 66-68, 110-112, 147-149

🧰 Tools
🪛 Biome (1.9.4)

[error] 48-48: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx (2)

1-11: General Structure & Localization Readiness:
The reformatting and placement under the [locale] folder align well with internationalization support. As you continue to add dynamic language content, verify that all user-visible strings (including titles and headings) are marked for translation.


42-46: Cautious Use of dangerouslySetInnerHTML:
The HTML output is sanitized using DOMPurify, which is good practice. Please double-check that the DOMPurify configuration covers all potential XSS vectors for content that originates from external sources.

🧰 Tools
🪛 Biome (1.9.4)

[error] 43-43: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx (3)

1-11: Consistent Code Formatting & Clarity:
The file’s overall formatting is clear, and the comments help explain the logic for loading event logistics. Keeping the code modular will support future i18n enhancements.


52-60: Vaccination Content Security Check:
The vaccination section’s use of dangerouslySetInnerHTML is safeguarded by DOMPurify. Just ensure that any future modifications to the source HTML maintain this sanitization step.

🧰 Tools
🪛 Biome (1.9.4)

[error] 53-53: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


73-77: Visa Section HTML Safety:
Similarly, the visa invitation HTML is sanitized appropriately. Please confirm that no unsanitized content reaches the DOM in later updates.

🧰 Tools
🪛 Biome (1.9.4)

[error] 75-75: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx (2)

1-16: Consistent Rendering with i18n Considerations:
The structure looks solid. As with other pages, as you localize more content, ensure that textual elements are set up for translation while preserving the layout.


60-64: HTML Injection Safety in Partners Section:
The main partners HTML is sanitized with DOMPurify before being rendered via dangerouslySetInnerHTML. Continue to verify that any dynamic content remains secure against XSS risks.

🧰 Tools
🪛 Biome (1.9.4)

[error] 60-60: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

src/website/Dockerfile (1)

1-34: Dockerfile Staging & Environment Configuration:
The multi-stage build is well structured. The careful separation of the frontend and backend stages helps keep the image lean and efficient.

Comment on lines 1 to 26
<<<<<<< HEAD
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started.

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

test

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resolve Merge Conflict Markers:
There are unresolved merge conflict markers (<<<<<<<, =======, >>>>>>>) present in the README. Please resolve these conflicts to ensure the documentation is clear and professional.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

4-4: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


26-26: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

Copy link

@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: 0

🧹 Nitpick comments (4)
src/website2/README.md (4)

1-2: Remove trailing punctuation from the "Getting Started" heading.

The period at the end of "## Getting Started." violates markdown style guidelines (MD026). Consider updating it to "## Getting Started" for consistency and clarity.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

1-1: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


15-16: Remove stray text "test".

The standalone word "test" appears to be leftover from debugging or an incomplete edit. Please remove it to ensure the documentation remains professional.


23-23: Remove trailing punctuation from the "Learn More" heading.

The heading "## Learn More." includes an unnecessary period. Removing it will better conform to markdown conventions.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

23-23: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


1-222: Ensure consistent header styling and overall content organization.

After resolving merge conflicts, verify that the document follows a consistent heading style (preferably ATX) and that duplicate sections are removed. A streamlined, well-structured README will improve readability and maintainability.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~100-~100: The official spelling of this programming framework is “Node.js”.
Context: ...ll or update Node.js, download it from nodejs.org or use a vers...

(NODE_JS)


[uncategorized] ~163-~163: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ting We welcome contributions from the open source community. To help you get started, ple...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.17.2)

1-1: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


23-23: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


36-36: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)


36-36: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45fd28a and a060c8b.

📒 Files selected for processing (1)
  • src/website2/README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
src/website2/README.md

1-1: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


23-23: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)


36-36: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)


36-36: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

🔇 Additional comments (1)
src/website2/README.md (1)

37-41: ⚠️ Potential issue

Resolve unresolved merge conflict markers.

There are visible merge conflict markers (e.g., "=======") starting around this region. This indicates that two versions of the content have not been reconciled, leading to duplicate or conflicting sections. Please resolve these conflicts—choose the appropriate content and remove all conflict markers—to ensure a clear and professional README.

Copy link

@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: 0

🔭 Outside diff range comments (2)
src/website2/README.md (2)

35-40: ⚠️ Potential issue

Resolve Merge Conflict Markers

There are unresolved merge conflict markers (e.g., =======) present beginning at line 35. This results in duplicated and ambiguous content in the README. Please resolve the conflicts by choosing the appropriate content—either the new instructions or the legacy sections—and remove all conflict markers.


35-44: ⚠️ Potential issue

Clarify and Consolidate Documentation Structure

Due to the merge conflict, the README currently presents two overlapping sections (the new "Getting Started" segment and the original content starting with "# Website"). Once the conflict markers are resolved, please ensure that the final document has a clear, unified structure. Decide whether to merge the introductions into one cohesive guide or organize them under distinct, well-labeled sections.

🧹 Nitpick comments (1)
src/website2/README.md (1)

30-34: Markdown Heading Style Consistency

Please verify that all headings throughout the file adhere to the ATX style (using # characters) and omit any trailing punctuation, as suggested by markdownlint (MD003 and MD026). Although the headings in this segment appear fine, double-checking can help maintain a consistent style across the documentation.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

34-34: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)


34-34: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a060c8b and 8354efd.

📒 Files selected for processing (1)
  • src/website2/README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
src/website2/README.md

34-34: Heading style
Expected: atx; Actual: setext

(MD003, heading-style)


34-34: Trailing punctuation in heading
Punctuation: '.'

(MD026, no-trailing-punctuation)

🔇 Additional comments (1)
src/website2/README.md (1)

1-20: New "Getting Started" Section is Clear and Informative

The newly added "Getting Started" instructions effectively guide developers through installing dependencies and launching the development server. The step-by-step format and inclusion of commands for various package managers are a strong addition. In future iterations, consider integrating notes on internationalization once those features are ready to be documented.

Copy link

@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

🔭 Outside diff range comments (2)
src/website2/public/locales/fr.json (1)

137-137: ⚠️ Potential issue

Extra Trailing Content Detected.
A stray numeral appears on line 137 that does not conform to valid JSON syntax. Please remove this extraneous line to avoid potential parsing errors.

src/website2/public/locales/en.json (1)

137-137: ⚠️ Potential issue

Extra Trailing Content Detected.
Similar to the French file, a stray numeral is present on line 137. Removing this extra line will ensure the JSON remains valid and parseable.

🧹 Nitpick comments (5)
src/website2/public/locales/fr.json (1)

37-46: Language Phrasing Consistency Suggestion.
In the engagementDialog options (e.g., for "partner" and "communityChampion"), the descriptions use “Intéressé à…” which might be less idiomatic in French. Consider revising to “Intéressé par…” for a more natural expression of interest.

src/website2/public/locales/en.json (1)

15-15: Phrasing Clarity Improvement.
The "apiDescription" reads “...leverage our open-air quality data on your App.” Consider rephrasing it to “in your app” (or “within your application”) for clarity and to follow conventional usage.

src/website2/README.md (3)

36-44: Header Consistency and Enhancement Suggestion

  • The heading “# Website” is inconsistent with the other section headers that use “##”. For uniformity, consider changing it to “## Website”.
  • Additionally, since this PR introduces internationalization support, it might be helpful to briefly mention the new language capabilities (English & French) in this introductory section.

Suggested changes:

-# Website
+## Website

and consider adding a new sub-section such as:

+## Internationalization (i18n)
+This project now supports multiple languages (English and French) using the next-intl library. Navigate to /en or /fr in your browser to view localized content.

63-99: Detailed Setup Instructions: Well Organized
The step-by-step guide for cloning the repository, navigating to the correct folder, and installing dependencies is clear and useful. There is a slight repetition when compared to the Quick Start section—consider consolidating or clarifying the purpose of each to avoid potential confusion.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~98-~98: The official spelling of this programming framework is “Node.js”.
Context: ...ll or update Node.js, download it from nodejs.org or use a vers...

(NODE_JS)


217-220: Consider Documenting Internationalization Features
While the README covers setup and deployment comprehensively, it currently lacks a dedicated section on the new internationalization support introduced in this PR. Including a brief note on how to test and use the language features (e.g., switching between English and French) would enhance clarity for contributors and users.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8354efd and b0dd96f.

📒 Files selected for processing (3)
  • src/website2/README.md (1 hunks)
  • src/website2/public/locales/en.json (1 hunks)
  • src/website2/public/locales/fr.json (1 hunks)
🔇 Additional comments (12)
src/website2/public/locales/fr.json (1)

1-136: Overall Structure and Content Verification.
The new French localization file is well-organized and covers key sections (home, playerSection, engagementDialog, navbar) with comprehensive translations. The hierarchical structure aligns nicely with the frontend’s expected usage.

src/website2/public/locales/en.json (1)

1-136: Overall Structure and Content Verification.
The new English localization file is clearly structured and provides detailed translations for each website section. The keys and values are organized consistently to support the internationalization efforts.

src/website2/README.md (10)

1-14: Quick Start Instructions: Clear and Concise Guidance
The instructions for launching the development server using various package managers (npm, yarn, pnpm, bun) are well presented and easy to follow.


19-29: "Learn More" Section Approved
The section providing links to Next.js Documentation and the interactive tutorial is informative and aligns well with project documentation.


30-34: Deploy on Vercel Section Looks Good
Clear instructions for using Vercel to deploy the application are present.


46-60: Table of Contents Updated
The detailed table of contents enhances navigation through the README and is structured well.


109-118: Development Server Launch Instructions Are Clear
The guidance for launching the development server is concise and straightforward.


126-143: Environment Variables & Workflow Updates: Comprehensive and Helpful
The instructions for updating environment variables and workflow configurations are thorough—this is very useful for maintaining consistency across different environments.


146-156: Backend & Database Integration Instructions: Clear
The guidelines provided for connecting to the backend and setting up database integration are detailed and well explained.


159-198: Contribution Guidelines: Excellent Onboarding Documentation
The “Contributing” section offers clear, step-by-step instructions for contributors. This level of detail is great for helping new contributors get started with the project.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ting We welcome contributions from the open source community. To help you get started, ple...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


201-209: Additional Learning Resources are Well Curated
The “Learn More” section concisely aggregates several valuable resources for Next.js development.


211-214: Deployment Instructions: Solid Guidance Provided
The deployment section provides clear direction with relevant links, making it easy for users to understand the deployment process.

Comment on lines 15 to 18
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update File Modification Reference
The line “You can start editing the page by modifying app/page.tsx” should be updated to reflect the new file structure where pages have been moved to the [locale] directory (e.g., app/[locale]/page.tsx). This will help avoid confusion when navigating the project after the internationalization refactor.

Suggested change:

-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+You can start editing the page by modifying files under `app/[locale]/` (e.g., `app/[locale]/page.tsx`). The page auto-updates as you edit the files.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying files under `app/[locale]/` (e.g., `app/[locale]/page.tsx`). The page auto-updates as you edit the files.

Copy link

@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: 0

🧹 Nitpick comments (1)
src/website2/README.md (1)

200-209: Potential Redundancy in “Learn More” Sections
There are two “Learn More” sections in the README (one at the top and another later on). Consolidating these into a single, unified section could reduce confusion and streamline the documentation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0dd96f and a2f5c42.

📒 Files selected for processing (1)
  • src/website2/README.md (2 hunks)
🔇 Additional comments (16)
src/website2/README.md (16)

1-13: Clear Quick Start Instructions for Development Server
The “Getting Started” section now provides clear, concise commands for running the development server using various package managers. This greatly improves the onboarding experience for new developers.


15-17: Accurate Update of Page Editing Instructions
The instructions now reference app/[locale]/page.tsx, which aligns with the new internationalized file structure. This clarity minimizes confusion when editing the page.


19-20: Optimized Font Handling Using next/font
Linking to Next.js’s optimized font loading via next/font is a great touch for performance.


21-29: Helpful “Learn More” Section with Updated Resources
The newly added “Learn More” section offers worthwhile links directly to Next.js resources and tutorials. The list is succinct and well organized.


30-35: Clear Deployment Instructions via Vercel
The “Deploy on Vercel” section provides an accessible guide for deploying the app, which is particularly useful for new deployments.


46-60: Updated Table of Contents Reflecting New Structure
The Table of Contents has been refreshed to include links to “Getting Started,” “Learn More,” and other key sections. Ensure that the anchor links (e.g., #getting-started-1 and #learn-more-1) correctly map to the intended headers.


63-77: Detailed “Getting Started” Instructions for Repository Setup
The step-by-step instructions for cloning the repository and initial setup are clearly laid out. This section is very reader-friendly and covers multiple operating systems.


78-88: Straightforward Navigation to the Website Folder
The directions on navigating to the correct folder (AirQo-frontend/src/website2) are clear and precise.


89-108: Comprehensive Dependency Installation Guidelines
The instructions for checking Node.js and installing dependencies, complete with OS-specific tips, ensure that newcomers will encounter fewer hurdles during setup.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~98-~98: The official spelling of this programming framework is “Node.js”.
Context: ...ll or update Node.js, download it from nodejs.org or use a vers...

(NODE_JS)


109-118: Concise Command for Launching the Development Server
The command to launch the development server is repeated here for emphasis; it serves as a useful reminder after the detailed setup instructions.


119-123: Useful Additional Tips for Mac/Linux Users
The extra tips help address common issues (like permission problems) on Mac and Linux, further improving the developer experience.


125-143: Clear Guidance on Environment Variables & Workflow Updates
The section on updating environment variables and CI/CD workflows is detailed and offers actionable instructions. Verifying that secret names match in the workflows is a good practice here.


146-157: Thorough Instructions for Backend & Database Integration
This part clearly outlines the need for a database URL and instructs contributors to contact the project admin, which helps maintain security and proper configuration.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~151-~151: Possible missing comma found.
Context: ...ccess:** You will need the database URL which is not publicly available. **...

(AI_HYDRA_LEO_MISSING_COMMA)


159-198: Well-Structured Contributing Guidelines
The “Contributing” section is comprehensive, providing clear, ordered steps from forking the repository to opening a pull request. This structure promotes a smooth contribution process.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ting We welcome contributions from the open source community. To help you get started, ple...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


211-214: Straightforward Deployment Section
The “Deployment” section clearly directs users to the Vercel platform with a link to detailed documentation. This clarity is beneficial for new deployers.


215-220: Friendly Concluding Remarks
The final “Thank you” section adds a welcoming tone and acknowledges contributor efforts—a nice finishing touch to the documentation.

Copy link

@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 (2)
src/website2/public/locales/fr.json (1)

1-229: Localization Language and Consistency Check
The French translations are comprehensive and mostly accurate. For example, the phrasing in several sections is clear and contextually fitting. One minor suggestion: consider reviewing phrases like
"Intéressé à soutenir la vision d'AirQo"
for possibly more idiomatic alternatives (e.g. "Intéressé par le soutien à la vision d'AirQo"). It might be helpful to have a native speaker validate these subtle choices.

src/website2/public/locales/en.json (1)

13-16: API Description Text Consistency
In the "home" section, the "apiDescription" reads:
"Are you a developer? We invite you to leverage our open-air quality data on your App"
The phrase "on your App" sounds slightly unconventional. Consider rephrasing it to "in your app" for a more natural flow. For example:

-"apiDescription": "Are you a developer? We invite you to leverage our open-air quality data on your App"
+"apiDescription": "Are you a developer? We invite you to leverage our open-air quality data in your app"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a13f54 and 62dbc3a.

📒 Files selected for processing (2)
  • src/website2/public/locales/en.json (1 hunks)
  • src/website2/public/locales/fr.json (1 hunks)
🔇 Additional comments (3)
src/website2/public/locales/fr.json (1)

1-229: Overall Structure & JSON Validity
The French localization file is well-organized with a clear hierarchical structure and proper JSON syntax. All key sections such as "home", "playerSection", "engagementDialog", "navbar", etc., are present and consistently formatted.

src/website2/public/locales/en.json (2)

1-229: Overall Structure & JSON Validity
The English localization file mirrors its French counterpart in structure and coverage, ensuring that all major sections (home, playerSection, engagementDialog, navbar, notificationBanner, homeStatsSection, highlight, actionButtons, and newsletter) are addressed. The JSON format is correct and the organization is clear.


1-229: Cross-language Consistency Check
Ensure that all keys and their implied contexts remain consistent across both the English and French localization files. A small discrepancy, such as the difference in interpretation for "champions" in the statistics section, may lead to confusion on the frontend. Verifying this consistency with the product specifications would be beneficial.

Comment on lines 158 to 165
"statistics": {
"african_cities": "Cities",
"champions": "Policy Makers",
"deployed_monitors": "Monitors",
"data_records": "Data Points",
"research_papers": "Researchers",
"partners": "Partners"
},
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Consistency in the 'champions' Key
Within the "statistics" section, the key "champions" is set to "Policy Makers". However, the French file uses a term suggesting community champions (i.e. "Les champions de la communauté"), and other sections (like the "engagementDialog" and "accordion" under "communities") imply a focus on community leadership rather than policymaking. Please confirm whether "champions" should be consistently translated as "Community Champions" (or equivalent) in English to match the intended context.


Action Required: Revisit the Translation of the "champions" Key

It appears that in the English locale file (src/website2/public/locales/en.json), the "champions" key is currently set to "Policy Makers", which conflicts with the French translation and other sections (like the "engagementDialog" and "accordion" under "communities") that imply a focus on community leadership. Please confirm whether this key should consistently be translated in English as "Community Champions" (or a similar term) to align with the intended context. If the decision is to update it, kindly change the value from "Policy Makers" to "Community Champions" and ensure consistency across all relevant sections.

  • File to Update: src/website2/public/locales/en.json (Lines 158-165)
  • Suggested Change:
    Replace:
    "champions": "Policy Makers",
    With:
    "champions": "Community Champions",
  • Action: Confirm the intended meaning and apply the update accordingly. If sticking with "Policy Makers" is deliberate, then verify that the French translations and other related sections are updated to match this terminology.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6b19ba and cbfe661.

⛔ Files ignored due to path filters (1)
  • src/platform/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (25)
  • src/website2/next.config.mjs (2 hunks)
  • src/website2/public/assets/icons/Icon1.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon2.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon3.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon4.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon5.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon6.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon7.tsx (1 hunks)
  • src/website2/public/assets/icons/Icon8.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/about/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/layout.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/resources/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/events/[id]/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/events/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/membership/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/clean-air-network/resources/page.tsx (1 hunks)
  • src/website2/src/app/[locale]/layout.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (23)
  • src/website2/src/app/[locale]/clean-air-network/events/[id]/page.tsx
  • src/website2/src/app/[locale]/clean-air-forum/about/page.tsx
  • src/website2/src/app/[locale]/clean-air-network/resources/page.tsx
  • src/website2/src/app/[locale]/clean-air-network/events/page.tsx
  • src/website2/public/assets/icons/Icon3.tsx
  • src/website2/src/app/[locale]/clean-air-forum/program-committee/page.tsx
  • src/website2/public/assets/icons/Icon1.tsx
  • src/website2/src/app/[locale]/clean-air-forum/resources/page.tsx
  • src/website2/public/assets/icons/Icon5.tsx
  • src/website2/src/app/[locale]/clean-air-network/membership/page.tsx
  • src/website2/public/assets/icons/Icon2.tsx
  • src/website2/public/assets/icons/Icon8.tsx
  • src/website2/src/app/[locale]/clean-air-forum/layout.tsx
  • src/website2/src/app/[locale]/clean-air-forum/logistics/page.tsx
  • src/website2/src/app/[locale]/clean-air-forum/speakers/page.tsx
  • src/website2/src/app/[locale]/clean-air-forum/sessions/page.tsx
  • src/website2/next.config.mjs
  • src/website2/public/assets/icons/Icon7.tsx
  • src/website2/public/assets/icons/Icon4.tsx
  • src/website2/src/app/[locale]/clean-air-forum/partners/page.tsx
  • src/website2/public/assets/icons/Icon6.tsx
  • src/website2/src/app/[locale]/clean-air-forum/glossary/page.tsx
  • src/website2/src/app/[locale]/clean-air-forum/sponsorships/page.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx

[error] 1-2: Expected a statement but instead found '<<<<<<<< HEAD:src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx
'use client''.

Expected a statement here.

(parse)


[error] 198-200: Expected a statement but instead found '========
'use client''.

Expected a statement here.

(parse)


[error] 394-395: Illegal duplicate default export declarations

multiple default exports are erroneous

the module's default export is first defined here

(parse)


[error] 197-198: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 200-200: Shouldn't redeclare 'Image'. Consider to delete it or rename it.

'Image' is defined here:

(lint/suspicious/noRedeclare)


[error] 201-201: Shouldn't redeclare 'React'. Consider to delete it or rename it.

'React' is defined here:

(lint/suspicious/noRedeclare)


[error] 203-203: Shouldn't redeclare 'ContentSection'. Consider to delete it or rename it.

'ContentSection' is defined here:

(lint/suspicious/noRedeclare)


[error] 204-204: Shouldn't redeclare 'FeaturedEvent'. Consider to delete it or rename it.

'FeaturedEvent' is defined here:

(lint/suspicious/noRedeclare)


[error] 206-206: Shouldn't redeclare 'CleanAirPage'. Consider to delete it or rename it.

'CleanAirPage' is defined here:

(lint/suspicious/noRedeclare)

src/website2/src/app/[locale]/layout.tsx

[error] 88-88: Expected a statement but instead found ','.

Expected a statement here.

(parse)


[error] 89-89: Expected a statement but instead found ': Props)'.

Expected a statement here.

(parse)


[error] 88-88: Unexpected label.

Only loops should be labeled.
The use of labels for other statements is suspicious and unfamiliar.

(lint/suspicious/noConfusingLabels)


[error] 88-88: Unused label.

The label is not used by any break statement and continue statement.
Safe fix: Remove the unused label.

(lint/correctness/noUnusedLabels)

🔇 Additional comments (5)
src/website2/src/app/[locale]/layout.tsx (5)

1-1: Confirm correct global stylesheet import location.

The updated import for ../globals.css should be verified to ensure that the styles are loaded correctly. If the file was previously imported from a different relative path, confirm that this new location matches your final folder structure.


5-6: Good approach for introducing NextIntl.

The newly added imports for NextIntlClientProvider and getMessages look correct. This is a proper way to integrate i18n functionality in Next.js.


20-26: Re-check local font paths.

You’ve updated the paths for Inter fonts. Ensure that the referenced TTF files exist at these new relative locations and are correctly spelled.


33-36: Clear type definition for layout props.

Defining a Props type improves clarity for the layout function. Confirm that all references to params.locale throughout this file and related code adhere to the newly introduced type.


175-178: Internationalization wrapper looks good.

Wrapping <EngagementDialog> and {children} with <NextIntlClientProvider> is the correct way to ensure translations are accessible throughout the layout.

Comment on lines 197 to 395
/>
</div>
</section>

<div className="max-w-5xl mx-auto w-full mt-16">
<ContentSection
subtitle="Mission"
title="The CLEAN-Air Mission"
description={
<p>
To strengthen regional networks for sustained partnerships and
enable partners to co-develop solutions that enhance the capacity
for air quality monitoring, modelling and management across cities
in Africa.
</p>
}
contentClassName="text-left space-y-4"
buttonClassName="hidden"
imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132391/website/cleanAirForum/images/section3_vgzcbs.webp"
imgAlt="Mission Image"
reverse={true}
/>
</div>

<div className="max-w-5xl mx-auto w-full mt-16">
<ContentSection
subtitle="Membership"
title="A Synergy for Air Quality in Africa"
description={
<div className="space-y-3">
<p>
The network comprises a diverse stakeholder landscape including
research organisations, city and national governments, the
private sector, development partners, and individuals who are
championing the air quality agenda in African cities.
</p>
<p>
Are you an organization or individual interested in air quality
in Africa? We welcome you to join the CLEAN-Air Network.
</p>
</div>
}
contentClassName="text-left space-y-4"
buttonClassName="hidden"
imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132391/website/cleanAirForum/images/section4_kudfs4.webp"
imgAlt="Synergy Image"
reverse={false}
/>
</div>

<section className="bg-blue-50 w-full mt-16">
<div className="max-w-5xl mx-auto w-full py-16 px-4 lg:px-0">
<div className="text-left mb-12">
<span className="text-blue-600 text-[14px] bg-white rounded-full py-1 px-4 font-semibold mb-2 inline-block">
Goals
</span>
<h2 className="text-4xl lg:text-[48px] leading-[1.1] font-medium">
CLEAN Air Goals
</h2>
</div>

<div className="space-y-16">
{goals.map((goal) => (
<div
key={goal.id}
className="flex flex-col lg:flex-row items-center lg:gap-4"
>
{/* Icon Section */}
<div className="w-[231px] h-[174px] mb-6 lg:mb-0">
<Image
src={goal.icon}
alt={goal.title}
width={192}
height={176}
className="object-cover rounded-lg"
/>
</div>

{/* Content Section */}
<div className="flex-1">
<h3 className="text-3xl lg:text-[32px] leading-[1.1] font-medium mb-2">
{goal.title}
</h3>
<p className="text-[20px] text-gray-600">
{goal.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>

<FeaturedEvent />
</div>
);
};

export default CleanAirPage;
>>>>>>>> origin/staging:src/website2/src/views/cleanAirNetwork/about/CleanAirPage.tsx
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Eliminate duplicate defaults and finalize the merged content.

The file includes additional conflict markers (========, >>>>>>>> ...) and a second copy of the entire component. This leads to re-imported symbols (React, Image, ContentSection, FeaturedEvent) and multiple default export declarations, which break the build. Unify both sections into one clean component definition and remove all conflict markers.

Below is an example of a merged structure (keeping references to @/views/cleanAirNetwork if that is the correct final path and ensuring only one export default):

-========
-'use client';
-(repeated imports and component code)
->>>>>>>>
+// Ensure this file has only one cohesive version of the CleanAirPage component
+// and one `export default CleanAirPage;`.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 198-200: Expected a statement but instead found '========
'use client''.

Expected a statement here.

(parse)


[error] 394-395: Illegal duplicate default export declarations

multiple default exports are erroneous

the module's default export is first defined here

(parse)


[error] 197-198: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 200-200: Shouldn't redeclare 'Image'. Consider to delete it or rename it.

'Image' is defined here:

(lint/suspicious/noRedeclare)


[error] 201-201: Shouldn't redeclare 'React'. Consider to delete it or rename it.

'React' is defined here:

(lint/suspicious/noRedeclare)


[error] 203-203: Shouldn't redeclare 'ContentSection'. Consider to delete it or rename it.

'ContentSection' is defined here:

(lint/suspicious/noRedeclare)


[error] 204-204: Shouldn't redeclare 'FeaturedEvent'. Consider to delete it or rename it.

'FeaturedEvent' is defined here:

(lint/suspicious/noRedeclare)


[error] 206-206: Shouldn't redeclare 'CleanAirPage'. Consider to delete it or rename it.

'CleanAirPage' is defined here:

(lint/suspicious/noRedeclare)

Comment on lines 88 to 91
params: { locale },
}: Props) {
const maintenance = await checkMaintenance();
const messages = await getMessages();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix suspicious label or syntax.

The function signature is split, causing parse errors (unused label and mismatched brace). Merge the destructuring and the type annotation into a single declaration to resolve syntax issues. For example:

-export default async function RootLayout({
-  children,
-}: {
-  children: ReactNode;
-}) {
+export default async function RootLayout({ children, params }: Props) {
   const maintenance = await checkMaintenance();
   const messages = await getMessages();
   ...
🧰 Tools
🪛 Biome (1.9.4)

[error] 88-88: Expected a statement but instead found ','.

Expected a statement here.

(parse)


[error] 89-89: Expected a statement but instead found ': Props)'.

Expected a statement here.

(parse)


[error] 88-88: Unexpected label.

Only loops should be labeled.
The use of labels for other statements is suspicious and unfamiliar.

(lint/suspicious/noConfusingLabels)


[error] 88-88: Unused label.

The label is not used by any break statement and continue statement.
Safe fix: Remove the unused label.

(lint/correctness/noUnusedLabels)

@@ -151,6 +163,7 @@ export default async function RootLayout({
`}
</Script>
</head>
<html lang={locale} className={interFont.variable}>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid multiple tags.

There are now two <html> elements: one at line 96 and another at line 166. Retain only the later one that uses lang={locale} to ensure proper internationalization and remove the earlier <html lang="en">.

-<html lang="en" className={interFont.variable}>
+<html lang={locale} className={interFont.variable}>

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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: 8

🧹 Nitpick comments (2)
src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx (2)

92-99: Image component uses deprecated layout prop.

The Image component is using a deprecated layout="responsive" prop. In Next.js 13+, this has been replaced with style or className properties.

Update the Image component to use the new pattern:

<Image
  src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/section2_jnqqyu.webp"
  alt="Urban Scene"
-  layout="responsive"
  width={1200}
  height={400}
-  className="object-cover"
+  className="object-cover w-full h-auto"
+  style={{ aspectRatio: '3/1' }}
/>

168-174: Consider using next-image optimizations for Cloudinary images.

The Image component is correctly used but could benefit from additional optimizations when working with Cloudinary.

Consider adding loader configuration for Cloudinary in your next.config.js and updating image references:

<Image
  src={goal.icon}
  alt={goal.title}
  width={192}
  height={176}
  className="object-cover rounded-lg"
+  quality={90}
+  placeholder="blur"
+  blurDataURL="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 176'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' fill='%23f3f4f6'/%3E%3C/svg%3E"
/>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2be83bf and c0ca822.

📒 Files selected for processing (1)
  • src/website2/src/app/[locale]/clean-air-network/CleanAirPage.tsx (1 hunks)

Comment on lines +191 to +194

<FeaturedEvent />
</div>
);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

FeaturedEvent component lacks internationalization context.

The FeaturedEvent component is being used without passing any internationalization context or props.

Check if the FeaturedEvent component has its own internationalization implementation or if it needs props to be passed from this parent component:


🏁 Script executed:

#!/bin/bash
# Look for the FeaturedEvent component implementation
fd "FeaturedEvent" src/website2/src/views/

# Check for i18n implementation in the component
fd "FeaturedEvent" src/website2/src/views/ --exec grep -l "useTranslations\|next-intl" {} \;

Length of output: 195


Action Required: Address Missing Internationalization in FeaturedEvent

It appears that the FeaturedEvent component in src/website2/src/views/cleanAirNetwork/FeaturedEvent.tsx does not implement internationalization hooks (e.g., useTranslations or next-intl), nor does it receive any i18n props from its parent. Please either integrate the necessary internationalization logic directly in the component or pass the appropriate i18n context/props from CleanAirPage.

  • Verify if the component should self-manage translations.
  • If not, update CleanAirPage to provide the required i18n context to FeaturedEvent.

Comment on lines 148 to 190

<section className="bg-blue-50 w-full mt-16">
<div className="max-w-5xl mx-auto w-full py-16 px-4 lg:px-0">
<div className="text-left mb-12">
<span className="text-blue-600 text-[14px] bg-white rounded-full py-1 px-4 font-semibold mb-2 inline-block">
Goals
</span>
<h2 className="text-4xl lg:text-[48px] leading-[1.1] font-medium">
CLEAN Air Goals
</h2>
</div>

<div className="space-y-16">
{goals.map((goal) => (
<div
key={goal.id}
className="flex flex-col lg:flex-row items-center lg:gap-4"
>
{/* Icon Section */}
<div className="w-[231px] h-[174px] mb-6 lg:mb-0">
<Image
src={goal.icon}
alt={goal.title}
width={192}
height={176}
className="object-cover rounded-lg"
/>
</div>

{/* Content Section */}
<div className="flex-1">
<h3 className="text-3xl lg:text-[32px] leading-[1.1] font-medium mb-2">
{goal.title}
</h3>
<p className="text-[20px] text-gray-600">
{goal.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Goals section heading and display need internationalization.

The goals section heading and the rendering of goals should use translations.

Implement translations for the goals section:

<section className="bg-blue-50 w-full mt-16">
  <div className="max-w-5xl mx-auto w-full py-16 px-4 lg:px-0">
    <div className="text-left mb-12">
      <span className="text-blue-600 text-[14px] bg-white rounded-full py-1 px-4 font-semibold mb-2 inline-block">
-        Goals
+        {t('goals.label')}
      </span>
      <h2 className="text-4xl lg:text-[48px] leading-[1.1] font-medium">
-        CLEAN Air Goals
+        {t('goals.heading')}
      </h2>
    </div>

    <div className="space-y-16">
      {goals.map((goal) => (
        <div
          key={goal.id}
          className="flex flex-col lg:flex-row items-center lg:gap-4"
        >
          {/* Icon Section */}
          <div className="w-[231px] h-[174px] mb-6 lg:mb-0">
            <Image
              src={goal.icon}
              alt={goal.title}
              width={192}
              height={176}
              className="object-cover rounded-lg"
            />
          </div>

          {/* Content Section */}
          <div className="flex-1">
            <h3 className="text-3xl lg:text-[32px] leading-[1.1] font-medium mb-2">
              {goal.title}
            </h3>
            <p className="text-[20px] text-gray-600">
              {goal.description}
            </p>
          </div>
        </div>
      ))}
    </div>
  </div>
</section>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<section className="bg-blue-50 w-full mt-16">
<div className="max-w-5xl mx-auto w-full py-16 px-4 lg:px-0">
<div className="text-left mb-12">
<span className="text-blue-600 text-[14px] bg-white rounded-full py-1 px-4 font-semibold mb-2 inline-block">
Goals
</span>
<h2 className="text-4xl lg:text-[48px] leading-[1.1] font-medium">
CLEAN Air Goals
</h2>
</div>
<div className="space-y-16">
{goals.map((goal) => (
<div
key={goal.id}
className="flex flex-col lg:flex-row items-center lg:gap-4"
>
{/* Icon Section */}
<div className="w-[231px] h-[174px] mb-6 lg:mb-0">
<Image
src={goal.icon}
alt={goal.title}
width={192}
height={176}
className="object-cover rounded-lg"
/>
</div>
{/* Content Section */}
<div className="flex-1">
<h3 className="text-3xl lg:text-[32px] leading-[1.1] font-medium mb-2">
{goal.title}
</h3>
<p className="text-[20px] text-gray-600">
{goal.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>
<section className="bg-blue-50 w-full mt-16">
<div className="max-w-5xl mx-auto w-full py-16 px-4 lg:px-0">
<div className="text-left mb-12">
<span className="text-blue-600 text-[14px] bg-white rounded-full py-1 px-4 font-semibold mb-2 inline-block">
{t('goals.label')}
</span>
<h2 className="text-4xl lg:text-[48px] leading-[1.1] font-medium">
{t('goals.heading')}
</h2>
</div>
<div className="space-y-16">
{goals.map((goal) => (
<div
key={goal.id}
className="flex flex-col lg:flex-row items-center lg:gap-4"
>
{/* Icon Section */}
<div className="w-[231px] h-[174px] mb-6 lg:mb-0">
<Image
src={goal.icon}
alt={goal.title}
width={192}
height={176}
className="object-cover rounded-lg"
/>
</div>
{/* Content Section */}
<div className="flex-1">
<h3 className="text-3xl lg:text-[32px] leading-[1.1] font-medium mb-2">
{goal.title}
</h3>
<p className="text-[20px] text-gray-600">
{goal.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>

Comment on lines 33 to 58
return (
<div className="flex flex-col overflow-hidden">
<div className="max-w-5xl mx-auto w-full">
<ContentSection
title="The CLEAN-Air Network"
description={
<p>
<span className="text-blue-700 font-medium">
An African-led, multi-regional network
</span>
<br />
bringing together a community of practice for air quality
solutions and air quality management across Africa.
</p>
}
buttonText="Join the Network"
buttonLink="https://docs.google.com/forms/d/e/1FAIpQLScIPz7VrhfO2ifMI0dPWIQRiGQ9y30LoKUCT-DDyorS7sAKUA/viewform"
titleClassName="text-4xl lg:text-[56px] leading-[1.1]"
contentClassName="text-left space-y-4"
buttonClassName="rounded-none"
imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/section1_usfuoj.webp"
imgAlt="Mission Image"
reverse={true}
/>
</div>

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Text content in the ContentSection needs internationalization.

The first ContentSection component contains hardcoded English text that should be localized for proper i18n support.

Replace the hardcoded title and description with translated versions:

return (
  <div className="flex flex-col overflow-hidden">
    <div className="max-w-5xl mx-auto w-full">
      <ContentSection
-        title="The CLEAN-Air Network"
+        title={t('section1.title')}
        description={
          <p>
            <span className="text-blue-700 font-medium">
-              An African-led, multi-regional network
+              {t('section1.highlight')}
            </span>
            <br />
-            bringing together a community of practice for air quality
-            solutions and air quality management across Africa.
+            {t('section1.description')}
          </p>
        }
-        buttonText="Join the Network"
+        buttonText={t('cta.join')}
        buttonLink="https://docs.google.com/forms/d/e/1FAIpQLScIPz7VrhfO2ifMI0dPWIQRiGQ9y30LoKUCT-DDyorS7sAKUA/viewform"
        titleClassName="text-4xl lg:text-[56px] leading-[1.1]"
        contentClassName="text-left space-y-4"
        buttonClassName="rounded-none"
        imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/section1_usfuoj.webp"
        imgAlt="Mission Image"
        reverse={true}
      />
    </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div className="flex flex-col overflow-hidden">
<div className="max-w-5xl mx-auto w-full">
<ContentSection
title="The CLEAN-Air Network"
description={
<p>
<span className="text-blue-700 font-medium">
An African-led, multi-regional network
</span>
<br />
bringing together a community of practice for air quality
solutions and air quality management across Africa.
</p>
}
buttonText="Join the Network"
buttonLink="https://docs.google.com/forms/d/e/1FAIpQLScIPz7VrhfO2ifMI0dPWIQRiGQ9y30LoKUCT-DDyorS7sAKUA/viewform"
titleClassName="text-4xl lg:text-[56px] leading-[1.1]"
contentClassName="text-left space-y-4"
buttonClassName="rounded-none"
imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/section1_usfuoj.webp"
imgAlt="Mission Image"
reverse={true}
/>
</div>
return (
<div className="flex flex-col overflow-hidden">
<div className="max-w-5xl mx-auto w-full">
<ContentSection
title={t('section1.title')}
description={
<p>
<span className="text-blue-700 font-medium">
{t('section1.highlight')}
</span>
<br />
{t('section1.description')}
</p>
}
buttonText={t('cta.join')}
buttonLink="https://docs.google.com/forms/d/e/1FAIpQLScIPz7VrhfO2ifMI0dPWIQRiGQ9y30LoKUCT-DDyorS7sAKUA/viewform"
titleClassName="text-4xl lg:text-[56px] leading-[1.1]"
contentClassName="text-left space-y-4"
buttonClassName="rounded-none"
imgSrc="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/section1_usfuoj.webp"
imgAlt="Mission Image"
reverse={true}
/>
</div>
</div>
);

Copy link

@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: 0

🧹 Nitpick comments (3)
src/website2/public/locales/en.json (3)

1-23: Review of "home" Section Structure
The "home" section is well structured with descriptive keys (e.g., "title", "description", "keywords") that clearly capture the intended meta information and page content. The content is organized and self-explanatory.


228-273: Review of "cleanAirNetwork" Section
This section delivers a detailed narrative for the CLEAN-Air Network, split into intro, acronym, mission, membership, and goals. The descriptive texts are thorough.
One minor suggestion: please double-check the formatting of the "fullName" in the "acronym" block to ensure the quote marks and punctuation meet the design guidelines.


355-397: Review of "monitorPage" Section
The "monitorPage" spans several important components—from breadcrumb navigation and hero section to detailed descriptions for designed-for-Africa features, mobile monitoring, and installation guidelines. The translations are thorough and contextually accurate.
Minor suggestion: Consider reviewing the length and complexity of some paragraphs for enhanced readability and ease of localization updates in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c517e6b and b81d27f.

📒 Files selected for processing (2)
  • src/website2/public/locales/en.json (1 hunks)
  • src/website2/public/locales/fr.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/website2/public/locales/fr.json
🔇 Additional comments (13)
src/website2/public/locales/en.json (13)

24-32: Review of "playerSection" Translation
The "playerSection" provides engaging and concise translations, including an impactful quote and clear call-to-action buttons. The content aligns well with the intended messaging.


33-81: Review of "engagementDialog" Section
This section is comprehensive: the options for different user roles (partner, policymaker, community champion, researcher, developer) and the associated form keys (including HTML-like placeholders in the "termsAndConditions" field) are clearly defined. The error and success messages are straightforward.


82-135: Review of "navbar" Structure
The "navbar" section is neatly divided into menu items and sub-categories for products, solutions, and about pages. This clear hierarchy aligns well with the website’s navigation design.


136-143: Review of "notificationBanner" Section
The notification banner correctly includes a nested "language" key for supporting the language switch (English/French). The design is simple and meets the internationalization requirement.


144-196: Review of "homeStatsSection" and "accordion" Keys
The statistics keys (e.g., "african_cities", "champions", "deployed_monitors") are well defined. Notably, the "champions" key within the "statistics" block now uses "Community Champions", which is an improvement over previous inconsistencies. However, please note that under the "accordion" → "communities" section (lines 182-186), the title is set to "AirQommunity Champions".
Please verify whether this slight discrepancy is intentional for branding purposes or if it would be preferable to standardize the terminology.


197-203: Review of "highlight" Section
The "highlight" section is concise with clear alt text and descriptive information that maintains the context of the featured content.


204-213: Review of "actionButtons" Section
The action buttons provide clear and compelling calls-to-action with both a title and associated action text, supporting the user engagement goals.


214-227: Review of "newsletter" Section
The newsletter segment covers all necessary fields—from form labels to submission responses—which will aid in providing a smooth user subscription experience.


274-283: Review of "actionButtons2" Section
The secondary set of action buttons is succinct and consistent with the other calls-to-action found in the file.


284-293: Review of "featuredEvent" Section
The featuredEvent block correctly provides fallback text for errors and placeholders for dates and labels, ensuring a robust user experience in case data is missing.


294-336: Review of "footer" Section
The footer is comprehensive, covering taglines, social links, and navigation sections. The inclusion of legal links and additional site sections ensures complete coverage for footer content.


337-350: Review of "countrySelector" Section
The "countrySelector" is user-friendly, with clear instructions and dialog components that will help guide user interactions when selecting a country.


351-354: Review of "monitorDisplay" Section
This section makes effective use of placeholders (e.g., {country}) to facilitate dynamic content display, and it is brief yet clear.

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.

Add Internationalization Support (English & French) to Frontend
1 participant