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

Implement Internationalization (en and fr) for Multi-language Support #485

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

Aspireve
Copy link

@Aspireve Aspireve commented Dec 10, 2024

What kind of change does this PR introduce?

Feature: Added internationalization (en and fr) support to the website to enable multi-language functionality.

Why was this change needed?

This change was needed to provide a seamless experience for users across different regions by allowing the website to support multiple languages.

Other information:

eg: Did you discuss this change with anybody before working on it (not required, but can be a good idea for bigger changes). Any plans for the future, etc?

Yes, I did discuss the updates and changes made to the codebase.

Checklist:

Put a "X" in the boxes below to indicate you have followed the checklist;

  • I have read the CONTRIBUTING guide.
  • I checked that there were not similar issues or PRs already open for this.
  • This PR fixes just ONE issue (do not include multiple issues or types of change in the same PR) For example, don't try and fix a UI issue and include new dependencies in the same PR.

Summary by CodeRabbit

  • New Features

    • Added internationalization support across various components, allowing dynamic translation of user-facing text.
    • Introduced language preference setting via a new endpoint for users.
    • Implemented localized error messages and prompts throughout the application.
    • Added support for English and French languages with corresponding translation files.
  • Documentation

    • Added English and French translation files for UI elements.
    • Updated localization configuration to support multiple languages.
  • Chores

    • Added next-intl dependency to enhance internationalization capabilities.

Copy link

vercel bot commented Dec 10, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
gitroom ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 10, 2024 9:00am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
postiz ⬜️ Ignored (Inspect) Visit Preview Dec 10, 2024 9:00am

Copy link

vercel bot commented Dec 10, 2024

@steveCs50 is attempting to deploy a commit to the Listinai Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

coderabbitai bot commented Dec 10, 2024

Warning

Rate limit exceeded

@Aspireve has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 29 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6b4b98d and d8e4b76.

📒 Files selected for processing (3)
  • apps/frontend/src/components/layout/title.tsx (1 hunks)
  • apps/frontend/src/i18n/translations/en.json (1 hunks)
  • apps/frontend/src/i18n/translations/fr.json (1 hunks)

Walkthrough

This pull request introduces comprehensive internationalization (i18n) support to the application using the next-intl library. The changes span across multiple components, configuration files, and the backend, enabling dynamic language switching and translation of user interface elements. A new language change mechanism is implemented, allowing users to set their preferred language through a cookie, with initial support for English and French languages. The modifications include adding translation files, updating component rendering to use translation functions, and configuring Next.js to support internationalization.

Changes

File/Path Change Summary
apps/backend/src/api/routes/users.controller.ts Added changeLanguage method to set user language preference via cookie
apps/frontend/next.config.js Integrated next-intl/plugin for internationalization configuration
apps/frontend/src/app/layout.tsx Added NextIntlClientProvider to support localization
apps/frontend/src/i18n/ Added locales configuration, translation files for English and French
Multiple frontend components Replaced hardcoded strings with translation function calls
package.json Added next-intl dependency
.eslintrc.json Updated ESLint configuration for i18n files

Sequence Diagram

sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Translations

    User->>Frontend: Change language
    Frontend->>Backend: POST /changeLanguage
    Backend-->>Frontend: Set NEXT_LOCALE cookie
    Frontend->>Translations: Load translation file
    Translations-->>Frontend: Provide translated strings
    Frontend->>User: Render UI in selected language
Loading

Poem

🐰 A Rabbit's Ode to Translation

In code where words once stood so still,
Now dance with language's sweet will,
From English plains to French terrain,
Our app speaks more without a strain!

Hop, translate, and set us free! 🌍


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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. (Beta)
  • @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 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.

<NextIntlClientProvider messages={messages}>
<VariableContextComponent
storageProvider={
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

storageProvider={
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
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: 27

🧹 Outside diff range and nitpick comments (33)
apps/frontend/src/app/layout.tsx (1)

52-52: Remove redundant double-negation (!!)

The double-negation (!!) is unnecessary here, as the ternary operator will evaluate the truthiness of process.env.IS_GENERAL. Removing it improves code clarity.

Apply this diff to simplify the expression:

-                domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
+                domain={process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

apps/frontend/src/i18n/request.ts (2)

2-2: Remove unnecessary trailing comma in import statement.

The import statement contains a trailing comma that should be removed.

-import { cookies,  } from 'next/headers';
+import { cookies } from 'next/headers';

4-7: Remove incorrect comment about cookies() function.

The comment stating that cookies() is not asynchronous is incorrect. According to the Next.js documentation, cookies() returns a ReadonlyRequestCookies object synchronously.

-  const cookieStore = await cookies(); // `cookies()` is not asynchronous
+  const cookieStore = cookies();
apps/frontend/src/components/layout/logout.component.tsx (3)

12-14: Optimize translation key usage.

The "Settings" namespace is repeated in each translation key. Consider using a more concise approach.

   const t = useTranslations("Settings")
   const logout = useCallback(async () => {
-    if (await deleteDialog(t("Settings.AreYouSureYouWantToLogout"), t("Settings.YesLogout"))) {
+    if (await deleteDialog(t("AreYouSureYouWantToLogout"), t("YesLogout"))) {

Line range hint 13-21: Add missing dependency to useCallback.

The useCallback hook is missing the t function in its dependency array, which could lead to stale translations.

   }, []);
+  }, [t, fetch]);

23-23: Improve string concatenation for app name.

Consider using template literals for better readability and maintainability.

-  return <div className="text-red-400 cursor-pointer" onClick={logout}>{t("Settings.LogoutFrom")} {isGeneral ? 'Postiz' : 'Gitroom'}</div>;
+  return <div className="text-red-400 cursor-pointer" onClick={logout}>{t("LogoutFrom")} {isGeneral ? 'Postiz' : 'Gitroom'}</div>;
apps/frontend/src/components/marketplace/buyer.seller.tsx (1)

11-11: Consider using more specific translation keys.

The translation keys 'Seller' and 'Buyer' are very generic and might conflict with similar terms in other contexts. Consider using more specific keys like 'marketplace.role.seller' and 'marketplace.role.buyer'.

apps/frontend/next.config.js (1)

5-8: Add essential i18n configurations.

While the next-intl plugin is correctly integrated, consider adding the following essential i18n configurations to ensure proper internationalization behavior:

  1. Default locale
  2. Supported locales
  3. Locale detection strategy
  4. Locale routing pattern

Example configuration:

const nextConfig = {
  // ... existing config
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'fr'],
    localeDetection: true,
  }
};

Also applies to: 57-57

apps/frontend/src/components/launches/editor.tsx (1)

Line range hint 54-59: Internationalize AI-related text.

The textareaPurpose string is hardcoded. Consider moving it to translations.

             placeholder={t('WriteYourReply')}
             autosuggestionsConfig={{
-              textareaPurpose: `Assist me in writing social media posts.`,
+              textareaPurpose: t('AiAssistancePrompt'),
               chatApiConfigs: {},
             }}
apps/frontend/src/components/platform-analytics/render.analytics.tsx (1)

14-14: Fix inconsistent spacing around assignment operator

The spacing around the assignment operator is inconsistent with the codebase style.

-  const t= useTranslations("Analytics")
+  const t = useTranslations("Analytics")
apps/frontend/src/components/launches/add.post.button.tsx (1)

10-10: Fix inconsistent spacing around assignment operator

The spacing around the assignment operator is inconsistent with the codebase style.

-  const t= useTranslations('PostModal')
+  const t = useTranslations('PostModal')
apps/frontend/src/components/layout/top.menu.tsx (1)

Line range hint 13-57: Restructure menu items to use proper translation keys

Using component names directly as translation keys is error-prone and makes maintenance difficult. Consider using a more structured approach with dedicated translation keys.

 return [
    ...(!isGeneral
      ? [
          {
            name: 'Analytics',
+           translationKey: 'menu.analytics',
            icon: 'analytics',
            path: '/analytics',
          },
        ]
      : []),
    {
      name: isGeneral ? 'Calendar' : 'Launches',
+     translationKey: isGeneral ? 'menu.calendar' : 'menu.launches',
      icon: 'launches',
      path: '/launches',
    },
    // ... update remaining items similarly
  ];

Then update the usage:

-  <span>{t(item.name)}</span>
+  <span>{t(item.translationKey)}</span>
apps/frontend/src/components/notifications/notification.component.tsx (1)

Line range hint 49-60: Consider adding ARIA labels for accessibility

While the translations are implemented correctly, consider enhancing accessibility for screen readers.

- <div className={`p-[16px] border-b border-tableBorder ${interClass} font-bold`}>
+ <div 
+   className={`p-[16px] border-b border-tableBorder ${interClass} font-bold`}
+   role="heading"
+   aria-label={t("NotificationsHeading")}
+ >
   {t("Notifications")}
 </div>

- <div className="text-center p-[16px] text-textColor flex-1 flex justify-center items-center mt-[20px]">
+ <div 
+   className="text-center p-[16px] text-textColor flex-1 flex justify-center items-center mt-[20px]"
+   role="status"
+   aria-live="polite"
+ >
   {t("NoNotifications")}
 </div>
apps/frontend/src/components/settings/github.component.tsx (3)

Line range hint 20-30: Enhance error handling for disconnect operation

While the translations are implemented correctly, the disconnect operation should handle potential errors and provide user feedback.

  const disconnect = useCallback(async () => {
    if (
      !(await deleteDialog(
        t("AreYouSureYouWantToDisconnectThisRepository")
      ))
    ) {
      return;
    }
    deleteRepository();
-   await fetch(`/settings/repository/${id}`, {
-     method: 'DELETE',
-   });
+   try {
+     const response = await fetch(`/settings/repository/${id}`, {
+       method: 'DELETE',
+     });
+     if (!response.ok) {
+       throw new Error('Failed to disconnect repository');
+     }
+     toast.show(t('DisconnectSuccess'), 'success');
+   } catch (error) {
+     toast.show(t('DisconnectError'), 'error');
+     // Rollback UI state
+     setConnected(login);
+   }
  }, []);

81-83: Enhance repository URL validation feedback

The current validation only shows a generic "Repository not found" message. Consider providing more specific feedback.

  if (response.status === 404) {
-   toast.show(t('RepositoryNotFound'), 'warning');
+   toast.show(t('repository_error', {
+     error: response.status === 404 ? t('not_found') : t('no_access'),
+     repo: `${select}/${repo}`
+   }), 'warning');
    return;
  }

Add to your translation files:

{
  "repository_error": "Repository {repo}: {error}",
  "not_found": "not found or private",
  "no_access": "no access permission"
}

Line range hint 114-119: Improve GitHub URL validation

The current regex might not handle all valid GitHub repository URL formats (e.g., URLs with trailing slashes, .git extensions, or subpaths).

- !url.match(/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)/)
+ !url.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(\.git)?(\/)?$/)

Consider extracting this validation to a utility function with proper error messages:

const validateGitHubUrl = (url: string): { isValid: boolean; owner?: string; repo?: string } => {
  const match = url.match(/^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(\.git)?(\/)?$/);
  return match ? { isValid: true, owner: match[1], repo: match[2] } : { isValid: false };
};
apps/frontend/src/components/launches/time.table.tsx (1)

130-130: Consider grouping translation keys logically.

The translation keys seem scattered without a clear hierarchical structure. Consider organizing them under a common prefix for better maintenance.

-  {t('AddSlotTimeTable')}
-  {t("AddSlot")}
-  {t("Save")}
+  {t('TimeTable.Labels.AddSlotTitle')}
+  {t("TimeTable.Actions.AddSlot")}
+  {t("TimeTable.Actions.Save")}

Also applies to: 170-170, 190-190

apps/frontend/src/components/platform-analytics/platform.analytics.tsx (1)

129-137: Maintain consistent translation key hierarchy

The translation keys follow inconsistent patterns. Some use dot notation hierarchy while others don't.

-          {t("Error.CantShowAnalyticsYet")}
+          {t("Analytics.Error.CantShowAnalyticsYet")}
-          {t("Error.YouHaveToAddSocialMediaChannels")}
+          {t("Analytics.Error.YouHaveToAddSocialMediaChannels")}
-          {t("Error.GoToTheCalendarToAddChannels")}
+          {t("Analytics.Error.GoToTheCalendarToAddChannels")}
apps/frontend/src/components/marketplace/seller.tsx (3)

69-69: Fix spacing in translation hook initialization

There's inconsistent spacing around the assignment operator.

-  const t= useTranslations("Market")
+  const t = useTranslations("Market")

207-208: Maintain consistent translation key casing

The translation keys use inconsistent casing patterns.

-                  ? t("ConnectBankAccount")
-                  : t('UpdateBankAccount')}
+                  ? t("Market.Actions.ConnectBankAccount")
+                  : t("Market.Actions.UpdateBankAccount")}

Line range hint 177-196: Add missing translations for accessibility text

The alt attribute for the avatar image should be translated for accessibility.

                  className="w-full h-full rounded-full"
                  src={data?.picture?.path || ''}
-                  alt="avatar"
+                  alt={t("Market.Profile.AvatarImage")}
apps/frontend/src/components/billing/lifetime.deal.tsx (1)

38-38: Consider adding translations for all toast states

While success and error messages are translated, consider adding translations for other possible states (e.g., loading, warning).

toast.show(t('Successfully claimed the code'));
+ // Consider adding translations for other states
+ // toast.show(t('Processing your code...'), 'loading');
+ // toast.show(t('Please wait while we verify your code'), 'info');

Also applies to: 41-41

apps/frontend/src/components/settings/teams.component.tsx (2)

191-194: Consider RTL support for different languages

While translations are implemented correctly, consider adding RTL (Right-to-Left) support for languages that require it.

Consider using the following CSS utilities:

.rtl-support {
  direction: var(--text-direction, ltr);
  text-align: var(--text-align-start, left);
}

Missing translations in AddMember component need to be added to the Settings namespace

The review comment is correct. The AddMember component contains hardcoded strings that need to be translated. Looking at the codebase:

  1. The component is in the Settings section and should use the "Settings" namespace as seen in the parent TeamsComponent
  2. The translation file (en.json) already has a "Settings" namespace but lacks these specific strings
  3. The component needs to import and use useTranslations hook like other components

Required changes:

  • Add missing translations to the Settings namespace in the translation files:
    "Settings": {
      "AddMember": "Add Member",
      "SendInvitationViaEmail": "Send invitation via email?",
      "SendInvitationLink": "Send Invitation Link",
      "CopyLink": "Copy Link",
      "SelectRole": "Select Role",
      "EnterEmail": "Enter email"
    }
  • Import and use the useTranslations hook in the AddMember component
  • Replace hardcoded strings with translation keys
🔗 Analysis chain

Line range hint 1-244: Missing translations in AddMember component

The AddMember component still contains hardcoded strings that need to be translated.

The following strings need translation:

  • "Add Member" (title)
  • "Send invitation via email?"
  • "Send Invitation Link"
  • "Copy Link"
  • "Select Role"
  • "Enter email"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify if these strings exist in the translation files
rg -F "Add Member" "apps/frontend/src/i18n/translations/*.json"
rg -F "Send invitation via email" "apps/frontend/src/i18n/translations/*.json"
rg -F "Send Invitation Link" "apps/frontend/src/i18n/translations/*.json"
rg -F "Copy Link" "apps/frontend/src/i18n/translations/*.json"
rg -F "Select Role" "apps/frontend/src/i18n/translations/*.json"
rg -F "Enter email" "apps/frontend/src/i18n/translations/*.json"

Length of output: 919


Script:

#!/bin/bash
# Let's first find where the translation files are located
fd -e json -e yaml messages

Length of output: 29


Script:

#!/bin/bash
# Let's try to find any translation-related files or directories
fd -t f -e json translations

# Also search for any imports of translation files
rg -g '*.{ts,tsx}' "import.*translations"

Length of output: 179


Script:

#!/bin/bash
# Let's check the content of the translation file and its directory
ls -la apps/frontend/src/i18n/translations/
cat apps/frontend/src/i18n/translations/en.json 2>/dev/null || echo "File not found"

# Also check how translations are used in the codebase
rg -g '*.{ts,tsx}' "useTranslations" -A 2

Length of output: 28966

apps/frontend/src/i18n/translations/en.json (1)

152-154: Consider expanding error messages

The Error section contains only a generic error message. Consider adding more specific error messages for different scenarios.

Consider adding:

  "Error": {
    "We are experiencing some difficulty, try to refresh the page":"We are experiencing some difficulty, try to refresh the page",
+   "NetworkError": "Unable to connect to the server. Please check your internet connection.",
+   "ValidationError": "Please check your input and try again.",
+   "AuthenticationError": "Your session has expired. Please log in again.",
+   "PermissionError": "You don't have permission to perform this action."
  }
apps/frontend/src/i18n/translations/fr.json (1)

152-154: Consider expanding error messages section

The Error section currently only has one generic message. Consider adding more specific error messages for different scenarios to improve user experience.

 "Error": {
-    "We are experiencing some difficulty, try to refresh the page": "Nous rencontrons des difficultés, essayez de rafraîchir la page"
+    "We are experiencing some difficulty, try to refresh the page": "Nous rencontrons des difficultés, essayez de rafraîchir la page",
+    "NetworkError": "Erreur de connexion au réseau",
+    "ValidationError": "Erreur de validation des données",
+    "ServerError": "Erreur du serveur"
 }
apps/frontend/src/components/launches/add.provider.component.tsx (1)

Line range hint 79-92: Add error handling for language change

The language change implementation should include error handling and loading state management.

  const setCurrentLanguage = () => {
    const language = form.getValues("language")
-    if(language && language.value) {
+    if (language?.value) {
+      setIsLoading(true);
       fetch('/user/changeLanguage', {
         method: 'POST',
         body: JSON.stringify({language: language.value}),
-      }).then(() => {
-        toast.show('Language updated');
-        window.location.reload()
-      });
+      })
+        .then(() => {
+          toast.show('Language updated');
+          window.location.reload()
+        })
+        .catch((error) => {
+          toast.show('Failed to update language', 'error');
+          console.error('Language update failed:', error);
+        })
+        .finally(() => {
+          setIsLoading(false);
+        });
     }
   }
apps/frontend/src/components/layout/settings.component.tsx (1)

79-92: Use optional chaining for form values

Replace the conditional check with optional chaining as suggested by the static analysis tool.

  const setCurrentLanguage = () => {
    const language = form.getValues("language")
-    if(language && language.value) {
+    if(language?.value) {
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

apps/frontend/src/components/media/media.component.tsx (1)

18-18: LGTM! Consider extracting translation keys to a constant.

The internationalization implementation looks good. The useTranslations hook is properly imported and used for button labels.

Consider extracting translation keys to a constant for better maintainability and to avoid typos:

const TRANSLATION_KEYS = {
  INSERT_MEDIA: 'InsertMedia',
  DESIGN_MEDIA: 'DesignMedia',
} as const;

Then use them like:

t(TRANSLATION_KEYS.INSERT_MEDIA)

Also applies to: 291-291, 312-312

apps/frontend/src/components/launches/menu/menu.tsx (1)

Line range hint 219-226: Add context to translation keys

The translation key MenuText.Changebot might be ambiguous when combined with "Picture" and "Nickname". Consider using more specific keys.

-{t('MenuText.Changebot')}{' '}
+{t('MenuText.ChangeBotPicture')}{' '}
apps/frontend/src/components/launches/add.edit.model.tsx (2)

72-73: Fix spacing in translation hook initialization.

There's a missing space after the assignment operator in the translation hook initialization.

-  const t= useTranslations("PostModal")
+  const t = useTranslations("PostModal")

514-514: Consider extracting error messages to constants.

The error messages are now properly translated, but consider extracting these translation keys to a constants file for better maintainability and reusability.

+ // In a new file: constants/translations.ts
+ export const TRANSLATION_KEYS = {
+   GLOBAL_EDITING_MODE: "YouAreInGlobalEditingMode",
+   MIN_CHARS_ERROR: "ThePostShouldBeAtLeastCharactersLong"
+ } as const;

- <div>{t("YouAreInGlobalEditingMode")}</div>
+ <div>{t(TRANSLATION_KEYS.GLOBAL_EDITING_MODE)}</div>

- {t('ThePostShouldBeAtLeastCharactersLong')}
+ {t(TRANSLATION_KEYS.MIN_CHARS_ERROR)}

Also applies to: 541-541

apps/frontend/src/components/billing/main.billing.component.tsx (1)

47-47: Consider optimizing translation hook initialization.

Multiple components initialize the translation hook independently. Consider lifting the translation initialization to a higher level and passing it down through props or context to improve performance and maintainability.

-const t = useTranslations('Billing')

+interface Props {
+  t: ReturnType<typeof useTranslations<'Billing'>>
+}

Also applies to: 162-162, 245-245

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8966bf9 and 6f25a0a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (35)
  • apps/backend/src/api/routes/users.controller.ts (1 hunks)
  • apps/frontend/next.config.js (2 hunks)
  • apps/frontend/src/app/(site)/err/page.tsx (1 hunks)
  • apps/frontend/src/app/layout.tsx (3 hunks)
  • apps/frontend/src/components/billing/faq.component.tsx (2 hunks)
  • apps/frontend/src/components/billing/lifetime.deal.tsx (7 hunks)
  • apps/frontend/src/components/billing/main.billing.component.tsx (17 hunks)
  • apps/frontend/src/components/launches/add.edit.model.tsx (11 hunks)
  • apps/frontend/src/components/launches/add.post.button.tsx (3 hunks)
  • apps/frontend/src/components/launches/add.provider.component.tsx (17 hunks)
  • apps/frontend/src/components/launches/calendar.tsx (5 hunks)
  • apps/frontend/src/components/launches/editor.tsx (3 hunks)
  • apps/frontend/src/components/launches/filters.tsx (5 hunks)
  • apps/frontend/src/components/launches/launches.component.tsx (2 hunks)
  • apps/frontend/src/components/launches/menu/menu.tsx (11 hunks)
  • apps/frontend/src/components/launches/providers/high.order.provider.tsx (5 hunks)
  • apps/frontend/src/components/launches/time.table.tsx (7 hunks)
  • apps/frontend/src/components/layout/logout.component.tsx (2 hunks)
  • apps/frontend/src/components/layout/settings.component.tsx (7 hunks)
  • apps/frontend/src/components/layout/title.tsx (1 hunks)
  • apps/frontend/src/components/layout/top.menu.tsx (3 hunks)
  • apps/frontend/src/components/marketplace/buyer.seller.tsx (1 hunks)
  • apps/frontend/src/components/marketplace/buyer.tsx (6 hunks)
  • apps/frontend/src/components/marketplace/seller.tsx (7 hunks)
  • apps/frontend/src/components/media/media.component.tsx (4 hunks)
  • apps/frontend/src/components/notifications/notification.component.tsx (3 hunks)
  • apps/frontend/src/components/platform-analytics/platform.analytics.tsx (7 hunks)
  • apps/frontend/src/components/platform-analytics/render.analytics.tsx (2 hunks)
  • apps/frontend/src/components/settings/github.component.tsx (9 hunks)
  • apps/frontend/src/components/settings/teams.component.tsx (6 hunks)
  • apps/frontend/src/i18n/locales.ts (1 hunks)
  • apps/frontend/src/i18n/request.ts (1 hunks)
  • apps/frontend/src/i18n/translations/en.json (1 hunks)
  • apps/frontend/src/i18n/translations/fr.json (1 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🪛 eslint
apps/frontend/src/components/launches/filters.tsx

[error] 169-169: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 203-203: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 212-212: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 221-221: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)

🪛 GitHub Check: ESLint
apps/backend/src/api/routes/users.controller.ts

[warning] 211-211: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

apps/frontend/src/app/layout.tsx

[warning] 41-41: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 43-43: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 44-44: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 46-46: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 47-47: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 49-49: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

apps/frontend/src/components/launches/launches.component.tsx

[warning] 223-223: Disallow unused variables
't' is assigned a value but never used.

🪛 Biome (1.9.4)
apps/frontend/src/components/layout/settings.component.tsx

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

apps/frontend/src/app/layout.tsx

[error] 52-52: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🔇 Additional comments (17)
apps/frontend/src/i18n/locales.ts (1)

1-6: Locales are correctly defined

The supported locales and the default locale are properly specified.

apps/frontend/src/components/layout/title.tsx (1)

17-19: Verify translation key existence.

Ensure that all menu item names have corresponding translations in the "Navigation" namespace to prevent missing translation keys.

✅ Verification successful

Let me gather more information to verify the translation key usage.


Let me check the menu items implementation to complete the verification.


Based on the gathered information, I can now provide the final response:

Translation keys are properly aligned with menu items.

From the verification results, I can confirm that all menu item names used in useMenuItems() hook (Analytics, Calendar, Launches, Billing, Messages, Marketplace, Plugs, Settings) have corresponding translation keys in the Navigation namespace of the translation file. The translation system is properly set up to handle all possible values of currentTitle.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all menu item names have corresponding translations
ast-grep --pattern 'useMenuItems()' | rg -A 10 'name:'

# List all translation keys in the Navigation namespace
fd -g 'en.json' -x jq -r '.Navigation | keys[]'

Length of output: 174


Script:

#!/bin/bash
# Find where currentTitle is defined or used
rg "currentTitle" -A 3 -B 3

# Find translation key usages in the codebase
ast-grep --pattern 't($expr)'

Length of output: 1122


Script:

#!/bin/bash
# Find the implementation of useMenuItems hook
ast-grep --pattern 'useMenuItems' -A 20

# Find menu item definitions
rg "name:" -A 2 -B 2

Length of output: 68392

apps/frontend/src/components/marketplace/buyer.seller.tsx (1)

16-17: Verify translation keys exist in language files.

Ensure that the 'Seller' and 'Buyer' keys exist in all language files under the 'Market' namespace.

✅ Verification successful

Translation keys 'Seller' and 'Buyer' are properly defined in language files

The translation keys 'Seller' and 'Buyer' exist in both English (en.json) and French (fr.json) translation files under the 'Market' namespace, with appropriate translations:

  • English: "Seller" → "Seller", "Buyer" → "Buyer"
  • French: "Seller" → "Vendeur", "Buyer" → "Acheteur"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the translation keys in language files
echo "Checking for translation keys in language files..."
rg -l '"Market":\s*{[^}]*"(Seller|Buyer)"' apps/frontend/src/i18n/

Length of output: 309


Script:

#!/bin/bash
# First, let's find all translation files
echo "Finding translation files..."
fd . apps/frontend/src/i18n/ -e json

# Then, let's check the content of these files for the keys
echo -e "\nChecking content of translation files..."
fd . apps/frontend/src/i18n/ -e json -x cat {} \; | grep -A 2 -B 2 '"Seller"\|"Buyer"'

Length of output: 762

apps/frontend/src/components/platform-analytics/render.analytics.tsx (1)

63-63: Verify translation key existence

Ensure that the "ThisChannelNeedsToBeRefreshed" key exists in the translation files for all supported locales (en, fr).

✅ Verification successful

The previous search might have failed because we didn't know the exact location or format of translation files. This new script will help us:

  1. Find all potential translation files with common extensions and naming patterns
  2. Do a broader search for the key across all text files to ensure we don't miss it due to file type restrictions

We found the files where the key appears, now let's verify its actual content in both translation files to ensure it's properly defined.


Translation key "ThisChannelNeedsToBeRefreshed" is properly defined in all locales

The translation key is correctly defined in both English and French translation files with appropriate translations:

  • English (en.json): "This channel needs to be refreshed"
  • French (fr.json): "Ce canal doit être actualisé"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the translation key in locale files
rg -l "ThisChannelNeedsToBeRefreshed" --type yaml

Length of output: 49


Script:

#!/bin/bash
# First, let's find all locale/translation files in the repository
fd -e yaml -e json "translations|locale|i18n|lang"

# Then, let's do a broader search for the key in all text files
rg "ThisChannelNeedsToBeRefreshed" -l

Length of output: 247


Script:

#!/bin/bash
# Let's check the actual content of these translation files to verify the key exists and is properly defined
echo "=== English translations ==="
cat apps/frontend/src/i18n/translations/en.json | grep -A 1 "ThisChannelNeedsToBeRefreshed"

echo -e "\n=== French translations ==="
cat apps/frontend/src/i18n/translations/fr.json | grep -A 1 "ThisChannelNeedsToBeRefreshed"

Length of output: 506

apps/frontend/src/components/launches/add.post.button.tsx (1)

23-23: Verify layout impact of increased button width

The button width has been increased from 102px to 150px. Please verify this change doesn't cause layout issues in the parent container.

✅ Verification successful

Based on the search results, I can see that the AddPostButton is used in two parent components:

  1. apps/frontend/src/components/launches/add.edit.model.tsx
  2. apps/frontend/src/components/launches/providers/high.order.provider.tsx

In both cases, the button is placed within flex containers that have sufficient space to accommodate the wider button:

  1. In add.edit.model.tsx, it's within:
<div className="flex gap-[4px]">
  <div className="flex-1 editor text-textColor">
    ...
    <AddPostButton num={index} onClick={addValue(index)} />
  1. In high.order.provider.tsx, it's within:
<div className="flex gap-[4px]">
  <div className="flex-1 text-textColor editor">
    ...
    <AddPostButton onClick={addValue(index)} num={index} />

Both parent containers use flex layouts with flex-1 on the main content area, which means they will automatically adjust to accommodate the wider button width. The button is also placed at the end of these containers with proper spacing through gap utilities.

The width increase from 102px to 150px is safe and won't cause layout issues

The parent containers are using flexible layouts that can handle the increased width, and the button is properly positioned with adequate spacing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for parent components that might be affected by width change
rg -B 5 -A 5 "AddPostButton" --type typescript --type tsx

Length of output: 92


Script:

#!/bin/bash
# Search for parent components that might be affected by width change, using ripgrep without file type filters
rg -B 5 -A 5 "AddPostButton"

Length of output: 5896


Script:

#!/bin/bash
# Find the file containing AddPostButton component and its usages
fd -e tsx -e ts "add.post.button.tsx"

Length of output: 95


Script:

#!/bin/bash
# Look for any layout containers or flex/grid styles that might be affected
rg -B 2 -A 2 "flex|grid" apps/frontend/src/components/launches/

Length of output: 71939

apps/frontend/src/components/layout/top.menu.tsx (1)

110-110: Verify translation key existence for all menu items

Ensure that translation keys exist for all menu items in both English and French locale files.

✅ Verification successful

Let me try a different approach to verify the translation keys by first finding the locale files and then checking their content.


Let me try one more approach to locate translation files by searching specifically in common locations for locale files and checking for the actual translation keys being used.


Let's check the content of the translation files we found to verify if they contain the menu item translations.


Translation keys are properly configured for menu items

Based on the examination of both English (en.json) and French (fr.json) translation files, all menu items have their corresponding translations properly configured under the "Navigation" namespace. The translations exist for:

  • Analytics/Analytique
  • Calendar/Calendrier
  • Launches/Lancements
  • Settings/Paramètres
  • Plugs/Plugs
  • Billing/Facturation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for menu item translations in locale files
for item in "Analytics" "Calendar" "Launches" "Settings" "Plugs" "Billing" "Affiliate"; do
  echo "Checking translations for: $item"
  rg -l "$item" --type yaml
done

Length of output: 1386


Script:

#!/bin/bash
# First, find all locale files
echo "Locating translation files:"
fd -e yml -e yaml

# Then, let's examine their content
echo -e "\nChecking translation content:"
for file in $(fd -e yml -e yaml); do
  echo -e "\n=== Content of $file ==="
  cat "$file"
done

Length of output: 1761


Script:

#!/bin/bash
# First, let's find the actual translation keys being used in the menu component
echo "Finding translation keys in the menu component:"
ast-grep --pattern 't($_)' apps/frontend/src/components/layout/top.menu.tsx

# Then, let's search for locale files in common locations
echo -e "\nSearching for locale files in common locations:"
fd -e json -e yaml -e yml . 'apps/frontend/src/locales' 'apps/frontend/src/i18n' 'apps/frontend/public/locales'

# Let's also check the imports in the menu component to see how translations are handled
echo -e "\nChecking translation-related imports:"
rg -A 3 "import.*i18n|import.*translation" apps/frontend/src/components/layout/top.menu.tsx

Length of output: 918


Script:

#!/bin/bash
# Check contents of both translation files
echo "=== English translations (en.json) ==="
cat apps/frontend/src/i18n/translations/en.json

echo -e "\n=== French translations (fr.json) ==="
cat apps/frontend/src/i18n/translations/fr.json

# Also, let's check how the menu items are defined to see the actual translation keys
echo -e "\nChecking menu items definition:"
rg -A 10 "const.*items.*=.*\[" apps/frontend/src/components/layout/top.menu.tsx

Length of output: 23256

apps/frontend/src/components/billing/faq.component.tsx (1)

24-29: Clean translation implementation

The translation implementation for these FAQ entries is clean and follows good practices by using simple translation keys.

package.json (1)

119-119: Verify next-intl version compatibility.

The added next-intl package version 3.25.3 should be verified for compatibility with Next.js version 14.2.14.

✅ Verification successful

next-intl v3.25.3 is compatible with Next.js v14.2.14

The next-intl package version 3.25.3 supports Next.js versions ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0, which includes the project's Next.js version ^14.2.14. The versions are compatible and no issues are expected.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check next-intl compatibility with current Next.js version

# Get next-intl's peer dependencies
curl -s https://registry.npmjs.org/next-intl/3.25.3 | jq -r '.peerDependencies | to_entries | .[] | select(.key == "next") | .value'

Length of output: 197


Script:

#!/bin/bash
# Get the exact Next.js version from package.json
cat package.json | jq -r '.dependencies.next'

Length of output: 54

apps/frontend/src/components/billing/lifetime.deal.tsx (1)

13-13: LGTM: Translation hook properly initialized

The useTranslations hook is correctly imported and initialized with the "Lifetime" namespace.

Also applies to: 23-23

apps/frontend/src/components/settings/teams.component.tsx (1)

138-138: LGTM: Translation hook properly initialized

The useTranslations hook is correctly initialized with the "Settings" namespace.

apps/frontend/src/components/launches/calendar.tsx (3)

30-32: LGTM: Clean import of i18n dependencies

The import of useTranslations is appropriately placed with other imports.


182-182: Ensure consistent translation namespace usage

The MonthView component uses the same "Week" namespace as WeekView for day translations, which is good for consistency.

Also applies to: 224-224


137-137: Verify translation key structure in WeekView

The implementation looks good, but let's ensure the translation keys for days are properly structured in the locale files.

Also applies to: 149-149

✅ Verification successful

Translation keys are properly structured in locale files

The translation keys for days of the week are correctly structured in both English and French locale files under the "Week" namespace, matching the useTranslations("Week") usage. All seven days are properly defined with appropriate translations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the day translation keys exist in locale files
fd -e json . "src/i18n/translations" --exec jq -r '.Week | keys[]' {} \; | sort | uniq | grep -E "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"

Length of output: 267


Script:

#!/bin/bash
# First, let's find where the translation files are located
fd -e json . --type f | grep -i "translation\|locale\|i18n"

Length of output: 149


Script:

#!/bin/bash
# Now let's check the content of these translation files
echo "=== English translations (en.json) ==="
cat apps/frontend/src/i18n/translations/en.json | jq '.Week'

echo -e "\n=== French translations (fr.json) ==="
cat apps/frontend/src/i18n/translations/fr.json | jq '.Week'

Length of output: 640

apps/frontend/src/components/launches/menu/menu.tsx (1)

Line range hint 53-91: Verify all dialog messages are translated

Ensure that all dialog messages and success/warning notifications are properly translated.

✅ Verification successful

The previous attempt failed because we assumed the translations directory structure. Let's first locate the translation files and their content to verify the existence of the required translation keys.


The previous attempt found the translation files location but we need to examine their content to verify if all the dialog messages are properly translated.


All dialog messages are properly translated

Looking at the translation files (en.json and fr.json), I can verify that all the dialog messages used in the menu component are properly translated:

  1. DisableMenu translations are present:

    • Message
    • ConfirmedButton
    • Success
  2. DeleteMenu translations are present:

    • Message
    • ConfirmedButton
    • Warning
    • Success

The translations exist in both English and French language files under the "Lanuches" section (note: there's a typo in the key name, but it's consistently used).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all menu-related translation keys exist
fd -e json . "src/i18n/translations" --exec jq -r '.Launches.MenuText, .Launches.DisableMenu, .Launches.DeleteMenu, .Launches.EnabledMenu | keys[]' {} \; | sort | uniq

Length of output: 279


Script:

#!/bin/bash
# First, let's find the translation files
fd -e json . --type f

# Then, let's check for the specific translation keys used in the code
rg -l "DisableMenu\.|DeleteMenu\." 

# Finally, let's examine the content of any JSON files that might contain translations
fd -e json . --type f --exec cat {}

Length of output: 83160


Script:

#!/bin/bash
# Let's specifically look for the translation files in the frontend app
fd -e json . "apps/frontend/src/i18n"

# Then let's examine their content
fd -e json . "apps/frontend/src/i18n" --exec cat {}

Length of output: 22997

apps/frontend/src/components/launches/add.edit.model.tsx (2)

Line range hint 1-669: Overall implementation looks good!

The internationalization changes have been properly implemented. The component now supports multiple languages while maintaining its core functionality. The suggested improvements are mainly for better maintainability and readability.


439-439: Verify translation key consistency.

The translation keys EditPost and CreatePost are used here. Let's verify these keys are consistently used across the codebase.

✅ Verification successful

Translation keys are correctly defined and consistently used

The translation keys 'EditPost' and 'CreatePost' are properly defined in both English (en.json) and French (fr.json) translation files, and they are correctly used in the modal component. No inconsistencies found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of these translation keys
rg -A 1 "('EditPost'|'CreatePost')" --type tsx

Length of output: 88


Script:

#!/bin/bash
# Search for translation keys in all files, including different file extensions
rg -A 1 "(EditPost|CreatePost)" 

Length of output: 1902

apps/frontend/src/components/launches/providers/high.order.provider.tsx (1)

Line range hint 31-31: LGTM! Clean internationalization implementation.

The internationalization changes are well-implemented and follow the established patterns.

Also applies to: 299-324

Comment on lines 41 to 49
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using non-null assertions (!)

Using the non-null assertion operator (!) can mask potential undefined values and may lead to runtime errors. It's recommended to handle possible undefined values explicitly or provide default values.

Consider modifying the code to safely handle environment variables:

-             storageProvider={
-               process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
-             }
+             storageProvider={
+               (process.env.STORAGE_PROVIDER as 'local' | 'cloudflare') || 'local'
+             }
-             backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
+             backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL || 'https://default-backend-url.com'}
-             plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
+             plontoKey={process.env.NEXT_PUBLIC_POLOTNO || 'defaultPlontoKey'}
-             discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
+             discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT || 'https://default-discord-url.com'}
-             frontEndUrl={process.env.FRONTEND_URL!}
+             frontEndUrl={process.env.FRONTEND_URL || 'https://default-frontend-url.com'}
-             uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}
+             uploadDirectory={
+               process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY || '/default/upload/directory'
+             }
📝 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
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}
storageProvider={
(process.env.STORAGE_PROVIDER as 'local' | 'cloudflare') || 'local'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL || 'https://default-backend-url.com'}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO || 'defaultPlontoKey'}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT || 'https://default-discord-url.com'}
frontEndUrl={process.env.FRONTEND_URL || 'https://default-frontend-url.com'}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={
process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY || '/default/upload/directory'
}
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 41-41: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 43-43: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 44-44: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 46-46: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 47-47: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 49-49: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

@@ -219,6 +220,7 @@
const search = useSearchParams();
const toast = useToaster();
const fireEvents = useFireEvents();
const t = useTranslations("Lanuches");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unused variable 't' and typo in translation key

The variable t is assigned using useTranslations("Lanuches") but is never used in the component. Also, there is a typo in the translation namespace "Lanuches"; it should be "Launches".

Apply this diff to fix the typo and ensure t is used for translations:

-      const t = useTranslations("Lanuches");
+      const t = useTranslations("Launches");

Ensure that t is used in the component to replace any hardcoded strings with localized messages.

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

🧰 Tools
🪛 GitHub Check: ESLint

[warning] 223-223: Disallow unused variables
't' is assigned a value but never used.

Comment on lines 10 to 12
const t= useTranslations('Error')
return (
<div>We are experiencing some difficulty, try to refresh the page</div>
<div>{t("We are experiencing some difficulty, try to refresh the page")}</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

Use translation keys instead of raw text

When using useTranslations, it's best practice to use translation keys rather than raw text to enable proper localization.

Apply this diff to use a translation key:

   const t= useTranslations('Error')
-      return (
-          <div>{t("We are experiencing some difficulty, try to refresh the page")}</div>
-      )
+      return (
+          <div>{t('errorMessage')}</div>
+      )

Then, in your translation files, define the key errorMessage with the corresponding message.

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

Comment on lines 8 to 12
return {
locale,
messages: (await import(`./translations/${locale}.json`)).default,
};
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for missing translation files.

The dynamic import of translation files could fail if the file doesn't exist for the specified locale. Consider adding error handling to fall back to the default locale.

   return {
     locale,
-    messages: (await import(`./translations/${locale}.json`)).default,
+    messages: await import(`./translations/${locale}.json`)
+      .then(module => module.default)
+      .catch(async () => {
+        console.error(`Translation file for locale "${locale}" not found, falling back to "en"`);
+        return (await import('./translations/en.json')).default;
+      }),
   };
📝 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 {
locale,
messages: (await import(`./translations/${locale}.json`)).default,
};
});
return {
locale,
messages: await import(`./translations/${locale}.json`)
.then(module => module.default)
.catch(async () => {
console.error(`Translation file for locale "${locale}" not found, falling back to "en"`);
return (await import('./translations/en.json')).default;
}),
};
});

Comment on lines 11 to 13
const currentTitle = useMemo(() => {
return menuItems.find(item => path.indexOf(item.path) > -1)?.name;
}, [path]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for currentTitle before translation.

The currentTitle could be undefined if no matching menu item is found. This could cause runtime errors when passed to the translation function.

    const currentTitle = useMemo(() => {
-        return menuItems.find(item => path.indexOf(item.path) > -1)?.name;
+        const item = menuItems.find(item => path.indexOf(item.path) > -1);
+        return item?.name || 'default';
    }, [path]);
📝 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
const currentTitle = useMemo(() => {
return menuItems.find(item => path.indexOf(item.path) > -1)?.name;
}, [path]);
const currentTitle = useMemo(() => {
const item = menuItems.find(item => path.indexOf(item.path) > -1);
return item?.name || 'default';
}, [path]);

@@ -36,6 +37,7 @@ export const Menu: FC<{
const ref = useClickOutside<HTMLDivElement>(() => {
setShow(false);
});
const t = useTranslations("Lanuches");
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 typo in translation namespace

There's a typo in the namespace: "Lanuches" should be "Launches"

-const t = useTranslations("Lanuches");
+const t = useTranslations("Launches");
📝 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
const t = useTranslations("Lanuches");
const t = useTranslations("Launches");

Comment on lines 473 to 476
? `${t('DowngradeOn')} ${dayjs
.utc(subscription?.cancelAt)
.local()
.format('D MMM, YYYY')}`
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Localize date formatting.

The date formatting should respect the user's locale preferences. Consider using the appropriate date formatting utilities provided by next-intl.

-`${t('DowngradeOn')} ${dayjs
-    .utc(subscription?.cancelAt)
-    .local()
-    .format('D MMM, YYYY')}`
+t('DowngradeOnDate', {
+  date: new Date(subscription?.cancelAt),
+  format: {
+    dateStyle: 'long'
+  }
+})

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

Comment on lines 300 to 304
`${t('AreYouSureYouWantToCancelYourSubscription')} ${messages.join(
', '
)}`,
'Yes, cancel',
'Cancel Subscription'
t('YesCancel'),
t('CancelSubscription')
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 string concatenation in translations.

String concatenation with translated text can cause issues in languages with different grammatical structures. Use translation keys with placeholders instead.

-`${t('AreYouSureYouWantToCancelYourSubscription')} ${messages.join(
-  ', '
-)}`
+t('AreYouSureYouWantToCancelYourSubscriptionWithMessages', {
+  messages: messages.join(', ')
+})

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

@@ -18,6 +18,7 @@
import { useToaster } from '@gitroom/react/toaster/toaster';
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
import { Calendar } from './calendar';
import { useTranslations } from 'next-intl';

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'useTranslations' is defined but never used.
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 and nitpick comments (6)
apps/frontend/src/app/layout.tsx (2)

26-27: Consider adding error handling for locale and messages loading

While the internationalization setup is correct, consider handling potential failures when loading messages or locale to provide a fallback experience.

+  try {
     const locale = await getLocale();
     const messages = await getMessages();
+  } catch (error) {
+    console.error('Failed to load translations:', error);
+    // Provide fallback values
+    const locale = 'en';
+    const messages = {};
+  }

52-53: Simplify boolean condition in domain selection

The double negation !!process.env.IS_GENERAL is unnecessary as the value will already be coerced to a boolean in this context.

-              domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
+              domain={Boolean(process.env.IS_GENERAL) ? 'postiz.com' : 'gitroom.com'}
🧰 Tools
🪛 Biome (1.9.4)

[error] 53-53: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

apps/frontend/src/components/launches/launches.component.tsx (4)

Line range hint 52-60: Add error handling for group change operation

The drop handler directly calls changeItemGroup without any error handling. If the API call fails, the UI state might become inconsistent with the backend.

Add error handling:

     drop: (item: { id: string }, monitor) => {
-      changeItemGroup(item.id, group.id);
+      try {
+        changeItemGroup(item.id, group.id);
+      } catch (error) {
+        // Revert the optimistic update
+        mutate();
+        // Show error to user
+        toast.show('Failed to change group. Please try again.', 'error');
+      }
     },
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.


Line range hint 95-196: Consider decomposing MenuComponent for better maintainability

The component has grown complex with multiple responsibilities (drag-and-drop, refresh handling, integration status, tooltips). Consider breaking it down into smaller, focused components.

Suggested structure:

// IntegrationIcon.tsx
const IntegrationIcon: FC<{
  integration: Integration;
  onRefresh?: () => void;
}> = ({ integration, onRefresh }) => {
  // Handle icon rendering with refresh overlay
};

// DraggableIntegrationName.tsx
const DraggableIntegrationName: FC<{
  integration: Integration;
  totalNonDisabledChannels: number;
}> = ({ integration, totalNonDisabledChannels }) => {
  // Handle drag and tooltip
};

// MenuComponent.tsx
const MenuComponent: FC<MenuComponentInterface> = (props) => {
  return (
    <div className="flex gap-[8px] items-center">
      <IntegrationIcon 
        integration={props.integration}
        onRefresh={props.refreshChannel(props.integration)}
      />
      <DraggableIntegrationName
        integration={props.integration}
        totalNonDisabledChannels={props.totalNonDisabledChannels}
      />
      <Menu {...menuProps} />
    </div>
  );
};
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.


Line range hint 223-241: Improve optimistic update handling in changeItemGroup

The current implementation updates the UI optimistically before the API call completes, but doesn't properly handle failures.

Implement proper optimistic updates:

   const changeItemGroup = useCallback(
     async (id: string, group: string) => {
+      // Store the previous state
+      const previousIntegrations = [...integrations];
       mutate(
         integrations.map((integration: any) => {
           if (integration.id === id) {
             return { ...integration, customer: { id: group } };
           }
           return integration;
         }),
         false
       );

-      await fetch(`/integrations/${id}/group`, {
-        method: 'PUT',
-        body: JSON.stringify({ group }),
-      });
-
-      mutate();
+      try {
+        await fetch(`/integrations/${id}/group`, {
+          method: 'PUT',
+          body: JSON.stringify({ group }),
+        });
+        // Success: Refresh data
+        mutate();
+      } catch (error) {
+        // Failure: Revert to previous state
+        mutate(previousIntegrations, false);
+        toast.show('Failed to change group. Please try again.', 'error');
+      }
     },
     [integrations]
   );
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.


Line range hint 223-241: Improve API error handling

Multiple API calls (changeItemGroup, refreshChannel) don't properly validate response status or handle network errors.

Implement proper error handling for all API calls:

const handleApiCall = async (apiCall: () => Promise<Response>) => {
  try {
    const response = await apiCall();
    if (!response.ok) {
      throw new Error(`API call failed: ${response.statusText}`);
    }
    return await response.json();
  } catch (error) {
    toast.show('Operation failed. Please try again.', 'error');
    throw error;
  }
};

Apply this pattern to all API calls in the component.

Also applies to: 279-290

🧰 Tools
🪛 GitHub Check: ESLint

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6f25a0a and ca3e01c.

📒 Files selected for processing (2)
  • apps/frontend/src/app/layout.tsx (3 hunks)
  • apps/frontend/src/components/launches/launches.component.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/frontend/src/app/layout.tsx

[error] 53-53: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🪛 GitHub Check: ESLint
apps/frontend/src/app/layout.tsx

[warning] 41-41: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 43-43: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 44-44: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 46-46: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 47-47: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.


[warning] 49-49: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

apps/frontend/src/components/launches/launches.component.tsx

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.

🔇 Additional comments (4)
apps/frontend/src/app/layout.tsx (3)

16-17: LGTM: Required internationalization imports added correctly

The necessary imports for next-intl have been properly added to support the internationalization feature.


29-29: Great accessibility improvement with dynamic language attribute

The addition of the dynamic lang attribute improves accessibility by properly indicating the page's language to screen readers and browsers.


26-27: Verify translation files and key usage

Let's ensure all required translation files are present and translation keys are used consistently.

Also applies to: 38-64

✅ Verification successful

Let me generate additional verification scripts to check for translation implementation.


Based on the verification results, I can now provide a final response:

Translation implementation appears to be properly configured

The codebase shows a well-structured internationalization setup:

  • Translation configuration is properly initialized using next-intl with getLocale and getMessages
  • Consistent usage of translation keys across components using the t() function
  • Translation keys follow a logical hierarchical structure (e.g., Filters.Week, Settings.ProfileSettings)
  • Language selection functionality is implemented with proper locale handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for translation files and their usage

# Find translation files
echo "Looking for translation files..."
fd -e json -e yaml messages

# Check for hardcoded strings that might need translation
echo "\nChecking for potential hardcoded strings..."
rg -g '*.tsx' -g '*.ts' "['\"](.*?)['\"]" --no-filename | grep -v "import\|from\|require" | sort | uniq

# Look for useTranslations usage
echo "\nChecking translation key usage..."
rg "useTranslations|getTranslations" -A 2

Length of output: 66074


Script:

#!/bin/bash
# Check for translation files and their usage more specifically

# Look for translation configuration and setup
echo "Checking translation setup..."
rg -g '*.ts' -g '*.tsx' "i18n|getLocale|getMessages" -A 2

# Look for translation files in common locations
echo "\nChecking for translation files in common locations..."
fd -e json -e yaml "messages|translations|i18n|locale" -t f

# Check for translation key usage patterns
echo "\nChecking translation key usage patterns..."
rg -g '*.ts' -g '*.tsx' "t\(['\"].*?['\"]" --no-filename | sort | uniq

Length of output: 26356

apps/frontend/src/components/launches/launches.component.tsx (1)

21-21: ⚠️ Potential issue

Incomplete internationalization implementation

The useTranslations hook is imported but never used in the component. According to the PR objectives, this component should support both English and French languages.

Let's verify if translation keys exist for this component:

Please implement the translations by:

  1. Adding translation keys for all hardcoded strings
  2. Using the useTranslations hook in the component
  3. Replacing hardcoded strings with translated ones
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 21-21: Disallow unused variables
'useTranslations' is defined but never used.

Comment on lines 38 to 39
<NextIntlClientProvider messages={messages}>
{/* @ts-ignore */}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove or document the @ts-ignore comment

The @ts-ignore comment suppresses TypeScript checks without explanation. Either:

  1. Fix the underlying type issue
  2. Use @ts-expect-error with a comment explaining why it's needed
  3. If the ignore is due to a known issue, add a link to the relevant issue

const maxAge = 1000 * 60 * 60 * 24 * 365 * 1; // 1 years in milliseconds
try {
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.
@@ -6,6 +6,8 @@
import { CopilotTextarea } from '@copilotkit/react-textarea';
import clsx from 'clsx';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useTranslations } from 'next-intl';
import { number } from 'yargs';

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'number' is defined but never used.
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

🧹 Outside diff range and nitpick comments (11)
apps/frontend/src/components/launches/add.provider.component.tsx (2)

94-94: Consider using translation interpolation for dynamic content

While the current implementation is functional, consider using the translation system's interpolation feature for the dynamic title instead of template literals. This would provide better maintainability and consistency across translations.

-      <TopTitle title={`${t("ApiModal.AddApiKeyFor")} ${name}`} />
+      <TopTitle title={t("ApiModal.AddApiKeyFor", { name })} />

Also applies to: 100-100, 127-127, 130-130


216-216: Improve validation message translation handling

Similar to the previous suggestion, consider using translation interpolation for the validation error message:

-            .matches(regex, `${item.label} ${t("CustomVariables.isInvalid")}`)
+            .matches(regex, t("CustomVariables.ValidationError", { label: item.label }))

This approach would:

  1. Make translations more maintainable
  2. Allow translators to adjust the word order as needed for different languages

Also applies to: 248-248, 284-284

apps/backend/src/api/routes/users.controller.ts (1)

228-230: Improve error handling specificity.

The current error handling catches all errors and returns a generic message. Consider handling specific error cases.

-  } catch (error) {
-    throw new HttpException('Failed to set language preference', 500);
+  } catch (error) {
+    if (error instanceof Error) {
+      throw new HttpException(`Failed to set language preference: ${error.message}`, 500);
+    }
+    throw new HttpException('Failed to set language preference', 500);
apps/frontend/src/components/launches/filters.tsx (3)

Line range hint 79-92: Enhance language change implementation

The language change implementation could be improved for better robustness and error handling.

Apply these improvements:

-  const setCurrentLanguage = () => {
-    const language = form.getValues("language")
-    if(language && language.value) {
+  const setCurrentLanguage = async () => {
+    const language = form.getValues("language")
+    if(language?.value) {
+      try {
+        await fetch('/user/changeLanguage', {
+          method: 'POST',
+          body: JSON.stringify({language: language.value}),
+        });
+        toast.show('Language updated');
+        window.location.reload()
+      } catch (error) {
+        toast.show('Failed to update language');
+      }
+    }
+  }

176-177: Improve translation key handling

Using dynamic translation keys with string concatenation can be error-prone and harder to maintain.

Consider using a more explicit approach:

-  ? t("Months." + dayjs().month(week.currentMonth).format('MMMM').toLowerCase())
+  ? t("Months.month", { month: dayjs().month(week.currentMonth).format('MMMM').toLowerCase() })

This approach allows for better type checking and makes it easier to track translation key usage.


Line range hint 217-223: Enhance language selector accessibility

The language selector should include proper ARIA attributes for better accessibility.

Apply these improvements:

   <CustomSelect
     onChange={setCurrentLanguage}
     options={locales}
     label={t('Settings.PreferredLanguage')}
     name='language'
     placeholder={t('Settings.SelectPreferredLanguage')}
+    aria-label={t('Settings.PreferredLanguage')}
+    role="combobox"
+    aria-expanded="false"
   />
🧰 Tools
🪛 eslint

[error] 218-218: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 219-219: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 221-221: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 223-223: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)

apps/frontend/src/i18n/translations/en.json (2)

1-275: Well-structured translation file with room for improvement

The translation file is well-organized with logical grouping, but consider these improvements:

  1. Add metadata section with language information
  2. Consider using nested objects for better maintainability

Example improvement:

 {
+  "_metadata": {
+    "locale": "en",
+    "language": "English",
+    "fallbackLocale": "en"
+  },
   "Navigation": {
     // ... existing translations
   }
 }

152-158: Enhance error and notification messages

The error and notification messages could be more specific and user-friendly.

Consider adding more specific error messages:

   "Error": {
-    "errorMsg":"We are experiencing some difficulty, try to refresh the page"
+    "errorMsg": "We are experiencing some difficulty, try to refresh the page",
+    "networkError": "Unable to connect to the server. Please check your internet connection.",
+    "validationError": "Please check the form for errors.",
+    "serverError": "Server error occurred. Please try again later."
   },
   "Notification": {
     "Notifications": "Notifications",
-    "NoNotifications": "No notifications"
+    "NoNotifications": "No notifications yet",
+    "LoadingNotifications": "Loading notifications...",
+    "ErrorLoadingNotifications": "Failed to load notifications"
   }
apps/frontend/src/components/billing/main.billing.component.tsx (3)

89-89: Consider using locale-aware number formatting.

The price formatting should respect the user's locale preferences for better internationalization.

-({t('PayToday')} ${(price < 0 ? 0 : price)?.toFixed(1)})
+({t('PayToday')} {new Intl.NumberFormat(locale, {
+  style: 'currency',
+  currency: 'USD'
+}).format(Math.max(0, price))})

162-162: Fix spacing in translation hook declaration.

There's inconsistent spacing in the translation hook declaration.

-const t= useTranslations('Billing')
+const t = useTranslations('Billing')

412-412: Maintain consistent casing in translation keys.

The translation keys 'MONTHLY' and 'YEARLY' use uppercase, while other keys use PascalCase. Consider maintaining consistent casing across all translation keys.

-<div>{t('MONTHLY')}</div>
+<div>{t('Monthly')}</div>
 <div>
   <Slider value={monthlyOrYearly} onChange={setMonthlyOrYearly} />
 </div>
-<div>{t('YEARLY')}</div>
+<div>{t('Yearly')}</div>

Also applies to: 416-416

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ca3e01c and 3418b9a.

📒 Files selected for processing (16)
  • apps/backend/src/api/routes/users.controller.ts (2 hunks)
  • apps/frontend/src/app/(site)/err/page.tsx (1 hunks)
  • apps/frontend/src/components/billing/faq.component.tsx (2 hunks)
  • apps/frontend/src/components/billing/main.billing.component.tsx (17 hunks)
  • apps/frontend/src/components/launches/add.post.button.tsx (3 hunks)
  • apps/frontend/src/components/launches/add.provider.component.tsx (17 hunks)
  • apps/frontend/src/components/launches/editor.tsx (3 hunks)
  • apps/frontend/src/components/launches/filters.tsx (5 hunks)
  • apps/frontend/src/components/launches/menu/menu.tsx (11 hunks)
  • apps/frontend/src/components/launches/providers/high.order.provider.tsx (6 hunks)
  • apps/frontend/src/components/launches/time.table.tsx (7 hunks)
  • apps/frontend/src/components/layout/settings.component.tsx (7 hunks)
  • apps/frontend/src/components/platform-analytics/platform.analytics.tsx (7 hunks)
  • apps/frontend/src/i18n/request.ts (1 hunks)
  • apps/frontend/src/i18n/translations/en.json (1 hunks)
  • apps/frontend/src/i18n/translations/fr.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/frontend/src/app/(site)/err/page.tsx
  • apps/frontend/src/i18n/request.ts
  • apps/frontend/src/components/launches/add.post.button.tsx
  • apps/frontend/src/components/platform-analytics/platform.analytics.tsx
  • apps/frontend/src/components/launches/menu/menu.tsx
  • apps/frontend/src/i18n/translations/fr.json
  • apps/frontend/src/components/launches/time.table.tsx
  • apps/frontend/src/components/launches/providers/high.order.provider.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/frontend/src/components/layout/settings.component.tsx

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Check: ESLint
apps/backend/src/api/routes/users.controller.ts

[warning] 219-219: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

apps/frontend/src/components/launches/editor.tsx

[warning] 10-10: Disallow unused variables
'number' is defined but never used.

🪛 eslint
apps/frontend/src/components/launches/filters.tsx

[error] 169-169: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 203-203: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 212-212: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)


[error] 221-221: Mixed spaces and tabs.

(no-mixed-spaces-and-tabs)

🔇 Additional comments (14)
apps/frontend/src/components/launches/add.provider.component.tsx (5)

17-17: LGTM: Consistent translation namespace usage

The translation namespace "Launches" is correctly imported and consistently used across all components.

Also applies to: 39-39, 63-63, 142-142, 204-204, 313-313


44-44: LGTM: Clean translation implementation

The button text is properly internationalized using a well-structured translation key.


153-153: LGTM: Consistent translation implementation

All text elements are properly internationalized following the established pattern.

Also applies to: 180-180, 183-183


337-337: LGTM: Complete internationalization coverage

All user-facing text has been properly internationalized, including error messages, titles, and section headers.

Also applies to: 405-405, 426-426, 457-457


Line range hint 1-478: Verify translation key existence and completeness

While the internationalization implementation looks solid, it's important to verify that all translation keys used in this component exist in both English and French translation files.

✅ Verification successful

All translation keys are properly defined and available

After verifying the translation files, all the translation keys used in the component (Provider.AddButton, ApiModal.InvalidApiKey, ApiModal.AddApiKeyFor, etc.) are properly defined in both English and French translation files under the Launches namespace. The implementation is complete and correct.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all translation keys used in the component exist in both language files

# Find all translation files
echo "Searching for translation files..."
fd -e json -e yaml -e yml . | grep -i "translations\|locales\|i18n"

# Extract all translation keys used in the component
echo "Translation keys used in the component:"
rg -o 't\("([^"]+)"\)' --replace '$1' apps/frontend/src/components/launches/add.provider.component.tsx

# Note: Compare these keys with the translation files found above

Length of output: 756


Script:

#!/bin/bash
# Let's check the content of both translation files to verify the keys
echo "Content of English translation file:"
cat apps/frontend/src/i18n/translations/en.json | jq '.Launches'

echo -e "\nContent of French translation file:"
cat apps/frontend/src/i18n/translations/fr.json | jq '.Launches'

Length of output: 4731

apps/frontend/src/components/launches/editor.tsx (1)

25-25: LGTM: Translation implementation looks good!

The translations are properly implemented using the useTranslations hook and correctly applied to:

  • Content description
  • Edit action description
  • Textarea placeholder

Also applies to: 27-27, 33-33, 55-55

apps/frontend/src/components/billing/faq.component.tsx (1)

18-21: LGTM: Well-structured translations.

The translations for these FAQ items are properly implemented using single translation keys without concatenation.

Also applies to: 24-25, 28-29

apps/backend/src/api/routes/users.controller.ts (2)

33-36: LGTM: Well-structured DTO with proper validation.

The ChangeLanguageDto correctly validates the language input using the @isin decorator.


216-225: LGTM: Secure cookie configuration.

The cookie is properly configured with secure settings:

  • Secure flag enabled
  • HttpOnly flag enabled
  • SameSite policy set
  • Reasonable expiration time
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 219-219: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

apps/frontend/src/components/launches/filters.tsx (1)

7-11: LGTM: Proper internationalization setup

The integration of next-intl and initialization of the translation hook follows best practices.

apps/frontend/src/components/layout/settings.component.tsx (1)

79-92: Improve language change implementation

The language change implementation could be more robust with proper error handling and type safety.

🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

apps/frontend/src/components/billing/main.billing.component.tsx (3)

473-476: Use next-intl's date formatting capabilities.

This is a duplicate of a previous comment. The date formatting should use next-intl's built-in date formatting capabilities for proper localization.


300-302: 🛠️ Refactor suggestion

Improve message concatenation in translation.

The current implementation concatenates translated messages, which can cause issues in languages with different grammatical structures.

-t('AreYouSureYouWantToCancelYourSubscription', {message: messages.join(
-  ', '
-)})
+t('AreYouSureYouWantToCancelYourSubscription', {
+  messages: messages.map(msg => t(msg))
+})

Likely invalid or redundant comment.


27-27: Verify translation key definitions.

Ensure all translation keys used in this component are properly defined in both English and French translation files.

✅ Verification successful

Let me try to locate the translation files with a different approach.


Let me check the content of the translation files to verify the keys.


All translation keys are properly defined

All translation keys used in main.billing.component.tsx are correctly defined in both English (en.json) and French (fr.json) translation files under the 'Billing' namespace. The translations are complete and consistent across both languages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for undefined translation keys in the Billing namespace

# Get all translation keys used in the file
echo "Translation keys used in the component:"
rg "t\(['\"](\w+)['\"]" --only-matching | sed -E "s/t\(['\"](\w+)['\"]/\1/" | sort -u

# Check translation files for these keys
echo -e "\nChecking translation files:"
for lang in en fr; do
  echo "Checking $lang translations:"
  if [ -f "messages/$lang.json" ]; then
    jq '.Billing' "messages/$lang.json"
  else
    echo "Warning: messages/$lang.json not found"
  fi
done

Length of output: 13128


Script:

#!/bin/bash
# Find translation files in the repository
echo "Searching for translation files:"
fd -e json -e yaml -e yml . | grep -i 'messages\|translations\|locale\|i18n'

echo -e "\nChecking content of main.billing.component.tsx for translation usage:"
rg "useTranslations|t\(['\"]" apps/frontend/src/components/billing/main.billing.component.tsx -C 2

Length of output: 5341


Script:

#!/bin/bash
# Check translation files content
echo "English translations (Billing namespace):"
cat apps/frontend/src/i18n/translations/en.json | jq '.Billing'

echo -e "\nFrench translations (Billing namespace):"
cat apps/frontend/src/i18n/translations/fr.json | jq '.Billing'

Length of output: 7010

@@ -6,6 +6,8 @@
import { CopilotTextarea } from '@copilotkit/react-textarea';
import clsx from 'clsx';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useTranslations } from 'next-intl';
import { number } from 'yargs';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused import.

The number import from yargs is not used in this file.

-import { number } from 'yargs';
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 10-10: Disallow unused variables
'number' is defined but never used.

Comment on lines 14 to 15
title: `${t('trust_title', {product: isGeneral ? 'Postiz' : 'Gitroom'})}?`,
description: `${t("trust_desc", {product: isGeneral ? 'Postiz' : 'Gitroom'})} ${isGeneral ? 'Postiz' : 'Gitroom'} ${t("will live forever")}. ${t("You can check out the entire code or use it for personal projects")}. ${t("To view the open-source repository,")} <a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">${t("click here")}</a>.`,
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 string concatenation in translations.

String concatenation in translations can lead to grammatical issues in different languages. Use interpolation instead.

-      title: `${t('trust_title', {product: isGeneral ? 'Postiz' : 'Gitroom'})}?`,
-      description: `${t("trust_desc", {product: isGeneral ? 'Postiz' : 'Gitroom'})} ${isGeneral ? 'Postiz' : 'Gitroom'} ${t("will live forever")}. ${t("You can check out the entire code or use it for personal projects")}. ${t("To view the open-source repository,")} <a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">${t("click here")}</a>.`,
+      title: t('trust_title', {product: isGeneral ? 'Postiz' : 'Gitroom'}),
+      description: t('trust_description', {
+        product: isGeneral ? 'Postiz' : 'Gitroom',
+        repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
+        repoLinkEnd: '</a>'
+      })

Add to your translation files:

{
  "trust_title": "Can I trust {product}?",
  "trust_description": "{product} is proudly open-source! We believe in an ethical and transparent culture, meaning that {product} will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, {repoLink}click here{repoLinkEnd}."
}

const maxAge = 1000 * 60 * 60 * 24 * 365 * 1; // 1 years in milliseconds
try {
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove non-null assertion.

The non-null assertion operator should be avoided. Add proper null checking instead.

-        domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
+        domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
📝 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
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!),
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
🧰 Tools
🪛 GitHub Check: ESLint

[warning] 219-219: Disallow non-null assertions using the ! postfix operator
Forbidden non-null assertion.

Comment on lines 213 to 223
<Button type="submit" className='rounded-md'>{t("Settings.Save")}</Button>
</div>
)}
{!!user?.tier?.team_members && isGeneral && <TeamsComponent />}
<CustomSelect
onChange={setCurrentLanguage}
options={locales}
label={t('Settings.PreferredLanguage')}
name='language'
placeholder={t('Settings.SelectPreferredLanguage')}
/>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance form submission and language change UX

The form submission and language change could be improved for better user experience.

Consider these improvements:

   <div className="justify-end flex">
-    <Button type="submit" className='rounded-md'>{t("Settings.Save")}</Button>
+    <Button
+      type="submit"
+      className='rounded-md'
+      disabled={form.formState.isSubmitting}
+    >
+      {form.formState.isSubmitting ? t("Settings.Saving") : t("Settings.Save")}
+    </Button>
   </div>
   {!!user?.tier?.team_members && isGeneral && <TeamsComponent />}
   <CustomSelect
     onChange={setCurrentLanguage}
     options={locales}
     label={t('Settings.PreferredLanguage')}
     name='language'
     placeholder={t('Settings.SelectPreferredLanguage')}
+    disabled={form.formState.isSubmitting}
   />
📝 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
<Button type="submit" className='rounded-md'>{t("Settings.Save")}</Button>
</div>
)}
{!!user?.tier?.team_members && isGeneral && <TeamsComponent />}
<CustomSelect
onChange={setCurrentLanguage}
options={locales}
label={t('Settings.PreferredLanguage')}
name='language'
placeholder={t('Settings.SelectPreferredLanguage')}
/>
<Button
type="submit"
className='rounded-md'
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? t("Settings.Saving") : t("Settings.Save")}
</Button>
</div>
)}
{!!user?.tier?.team_members && isGeneral && <TeamsComponent />}
<CustomSelect
onChange={setCurrentLanguage}
options={locales}
label={t('Settings.PreferredLanguage')}
name='language'
placeholder={t('Settings.SelectPreferredLanguage')}
disabled={form.formState.isSubmitting}
/>

Copy link

@A1exKH A1exKH left a comment

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warnings.

<NextIntlClientProvider messages={messages}>
<VariableContextComponent
storageProvider={
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

storageProvider={
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
Copy link

Choose a reason for hiding this comment

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

@Aspireve please resolve this ESLint warning.

@Aspireve
Copy link
Author

Hey @A1exKH!

The ESLint issues seem to be related to the environment variables. Non-null assertions are necessary to prevent TypeScript from complaining about potential undefined values being assigned to variables that require a string.

If there's a way to suppress these warnings, could you guide me on how to achieve that?

@A1exKH
Copy link

A1exKH commented Dec 13, 2024

Hey @A1exKH!

The ESLint issues seem to be related to the environment variables. Non-null assertions are necessary to prevent TypeScript from complaining about potential undefined values being assigned to variables that require a string.

If there's a way to suppress these warnings, could you guide me on how to achieve that?

@jamesread could give an advice how to better handle the warning from ESLint?

Removed the typescript no null assertion
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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3418b9a and b2300c7.

📒 Files selected for processing (1)
  • .eslintrc.json (1 hunks)
🔇 Additional comments (1)
.eslintrc.json (1)

Line range hint 15-18: Justify disabling unrelated TypeScript rules

Some of the disabled rules (no-explicit-any, ban-ts-comment, react/display-name) seem unrelated to the internationalization implementation. This could introduce technical debt and reduce type safety.

Let's check if these rules are actually needed for the i18n implementation:

Consider:

  1. Keep no-explicit-any enabled and use proper types from the i18n library
  2. Address specific type issues instead of using @ts-ignore
  3. If display-name rule needs to be disabled, do it only for specific components

.eslintrc.json Outdated
@@ -6,6 +6,7 @@
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@typescript-eslint/no-non-null-assertion": "off"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider a more targeted approach for non-null assertions

While disabling no-non-null-assertion might help with i18n implementation, it could lead to potential runtime errors. Consider:

  1. Using optional chaining with fallback values
  2. Type guards to handle nullability explicitly
  3. If needed, disable this rule only for specific files or lines using inline comments

Example of a more targeted approach:

- "@typescript-eslint/no-non-null-assertion": "off"
+ "@typescript-eslint/no-non-null-assertion": "warn",
+ "overrides": [
+   {
+     "files": ["**/i18n/**/*.ts"],
+     "rules": {
+       "@typescript-eslint/no-non-null-assertion": "off"
+     }
+   }
+ ]

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

@Aspireve
Copy link
Author

Heyy Hi!! @A1exKH
I spoke to @nevo-david regarding the eslint warning, so he suggested me I could just disable the non-null warning
I added that to the PR

Copy link

@A1exKH A1exKH left a comment

Choose a reason for hiding this comment

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

@Aspireve please, could you resolve conflicts for this PR?

Added the non null assertion only for the required routes
The change route doesnt have the no null operator anymore
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 (1)
apps/backend/src/api/routes/users.controller.ts (1)

214-231: LGTM! Consider enhancing the response structure.

The implementation looks good with proper error handling and secure cookie configuration. Consider returning a more informative success response:

-      response.status(200).send();
+      response.status(200).json({
+        success: true,
+        language,
+        expiresAt: new Date(Date.now() + maxAge)
+      });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4194541 and 71cd197.

📒 Files selected for processing (1)
  • apps/backend/src/api/routes/users.controller.ts (2 hunks)
🔇 Additional comments (2)
apps/backend/src/api/routes/users.controller.ts (2)

30-36: LGTM! Well-structured DTO with proper validation.

The ChangeLanguageDto implementation with @IsIn decorator effectively validates language selection, ensuring only 'en' or 'fr' are accepted.


214-231: Verify frontend integration with next-intl.

Let's verify the integration between this backend implementation and the frontend next-intl setup:

✅ Verification successful

Integration with next-intl is properly implemented and working as expected.

The implementation is correctly integrated with next-intl and the NEXT_LOCALE cookie:

  1. The backend sets the NEXT_LOCALE cookie which is properly consumed by the frontend:

    • request.ts reads the cookie and configures next-intl with the locale value
    • layout.tsx sets up NextIntlClientProvider and applies the locale to the HTML lang attribute
    • settings.component.tsx handles language changes through the backend API
  2. The implementation follows next-intl best practices:

    • Proper configuration of messages and translations
    • Client-side language switching with cookie persistence
    • Server-side locale detection from cookies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify frontend integration with next-intl and NEXT_LOCALE cookie usage

# Check for next-intl configuration in next.config.js
echo "Checking next-intl configuration..."
rg -l "next-intl" "next.config.*"

# Check for NEXT_LOCALE cookie usage in frontend
echo "Checking NEXT_LOCALE cookie usage..."
rg -l "NEXT_LOCALE" "apps/frontend/src/"

# Check for language-related configurations
echo "Checking language configurations..."
fd -e json -e js -e ts -e tsx -p "apps/frontend/src/" -x rg -l "locale|language|i18n|translation"

Length of output: 795


Script:

#!/bin/bash
# Let's examine the content of i18n configuration and NEXT_LOCALE usage

# Check the content of i18n/request.ts for NEXT_LOCALE usage
echo "Content of i18n/request.ts:"
cat "apps/frontend/src/i18n/request.ts"

# Check i18n configuration
echo -e "\nContent of i18n/locales.ts:"
cat "apps/frontend/src/i18n/locales.ts"

# Check layout.tsx for i18n setup
echo -e "\nContent of app/layout.tsx:"
cat "apps/frontend/src/app/layout.tsx"

# Check settings component for language change functionality
echo -e "\nContent of components/layout/settings.component.tsx:"
cat "apps/frontend/src/components/layout/settings.component.tsx"

Length of output: 20567

Comment on lines +218 to +225
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add path attribute to cookie for enhanced security.

Consider adding the path attribute to the cookie configuration to restrict its scope to the minimum required path:

       response.cookie('NEXT_LOCALE', language, {
         domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
         secure: true,
         httpOnly: true,
         maxAge: maxAge,
         expires:  new Date(Date.now() + maxAge),
         sameSite: 'none',
+        path: '/',  // Restrict cookie to root path or specific path as needed
       });
📝 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
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
});
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
path: '/', // Restrict cookie to root path or specific path as needed
});

Shortend and removed string parsing in the file
Removed unused import
removed the unused variable
Added DX and a disabled state to custom select
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: 2

♻️ Duplicate comments (1)
apps/frontend/src/components/billing/faq.component.tsx (1)

22-31: 🛠️ Refactor suggestion

Improve translation key structure and usage

There are several issues with the current translation implementation:

  1. Template literals are used with the translation function (lines 27, 31)
  2. Inconsistent key naming ('channels_answer?' contains a question mark)
  3. Missing interpolation for dynamic content

Apply these improvements:

-      title: t('What are channels?'),
-      description: t('channels_answer?', {product: isGeneral ? 'Postiz' : 'Gitroom'})
+      title: t('channels_title'),
+      description: t('channels_description', {product: isGeneral ? 'Postiz' : 'Gitroom'})
    },
    {
-      title: t('What are team members?'),
-      description: t(`If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels`),
+      title: t('team_members_title'),
+      description: t('team_members_description'),
    },
    {
-      title: t('What is AI auto-complete?'),
-      description: t(`We automate ChatGPT to help you write your social posts and articles`),
+      title: t('ai_autocomplete_title'),
+      description: t('ai_autocomplete_description')

Add to your translation files:

{
  "channels_title": "What are channels?",
  "channels_description": "{product} channels allow you to...",
  "team_members_title": "What are team members?",
  "team_members_description": "If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels",
  "ai_autocomplete_title": "What is AI auto-complete?",
  "ai_autocomplete_description": "We automate ChatGPT to help you write your social posts and articles"
}
🧹 Nitpick comments (3)
apps/frontend/src/components/layout/settings.component.tsx (2)

222-229: Enhance accessibility for language selector.

The language selector should include proper ARIA attributes for better accessibility.

   <CustomSelect
     onChange={setCurrentLanguage}
     options={locales}
     label={t('Settings.PreferredLanguage')}
     name='language'
     placeholder={t('Settings.SelectPreferredLanguage')}
     disabled={form.formState.isSubmitting}
+    aria-label={t('Settings.PreferredLanguage')}
+    role="combobox"
+    aria-expanded="false"
   />

213-218: Improve button state handling during form submission.

Consider disabling the button during form submission to prevent multiple submissions.

   <Button 
     type="submit" 
     className='rounded-md'
+    disabled={form.formState.isSubmitting}
   >
     {form.formState.isSubmitting ? t("Settings.Saving") : t("Settings.Save")}
   </Button>
apps/frontend/src/components/billing/faq.component.tsx (1)

109-109: Consider using consistent key naming pattern

While the translation implementation is correct, consider using a more consistent key naming pattern:

-        {t('FrequentlyAskedQuestions')}
+        {t('faq_section_title')}

This aligns better with the kebab-case pattern used in other translations and makes the purpose of the key more explicit.

Also applies to: 113-113

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 71cd197 and a816afb.

📒 Files selected for processing (3)
  • apps/frontend/src/components/billing/faq.component.tsx (2 hunks)
  • apps/frontend/src/components/launches/editor.tsx (3 hunks)
  • apps/frontend/src/components/layout/settings.component.tsx (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/frontend/src/components/launches/editor.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/frontend/src/components/layout/settings.component.tsx

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (1)
apps/frontend/src/components/billing/faq.component.tsx (1)

7-7: LGTM: Translation hook properly integrated

The useTranslations hook is correctly imported and initialized with the appropriate scope ('Billing').

Also applies to: 11-11

Comment on lines +79 to +92
const setCurrentLanguage = () => {
const language = form.getValues("language")
if(language && language.value) {

fetch('/user/changeLanguage', {
method: 'POST',
body: JSON.stringify({language: language.value}),
}).then(() => {
toast.show('Language updated');
window.location.reload()
});
}

}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and user experience in language selection.

The language change implementation has several areas for improvement:

  1. Missing error handling for the fetch call
  2. Full page reload is not user-friendly
  3. Optional chaining can be used for safer property access
-  const setCurrentLanguage = () => {
-    const language = form.getValues("language")
-    if(language && language.value) {
+  const setCurrentLanguage = async () => {
+    try {
+      const language = form.getValues("language")
+      if(language?.value) {
+        await fetch('/user/changeLanguage', {
+          method: 'POST',
+          body: JSON.stringify({language: language.value}),
+        });
+        toast.show('Language updated');
-      fetch('/user/changeLanguage', {
-        method: 'POST',
-        body: JSON.stringify({language: language.value}),
-      }).then(() => {
-        toast.show('Language updated');
-        window.location.reload()
-      });
-    }
+        // Consider using a more graceful way to update the UI
+        // without a full page reload
+        window.location.reload()
+      }
+    } catch (error) {
+      toast.show('Failed to update language');
+      console.error('Language update failed:', error);
+    }
   }
📝 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
const setCurrentLanguage = () => {
const language = form.getValues("language")
if(language && language.value) {
fetch('/user/changeLanguage', {
method: 'POST',
body: JSON.stringify({language: language.value}),
}).then(() => {
toast.show('Language updated');
window.location.reload()
});
}
}
const setCurrentLanguage = async () => {
try {
const language = form.getValues("language")
if(language?.value) {
await fetch('/user/changeLanguage', {
method: 'POST',
body: JSON.stringify({language: language.value}),
});
toast.show('Language updated');
// Consider using a more graceful way to update the UI
// without a full page reload
window.location.reload()
}
} catch (error) {
toast.show('Failed to update language');
console.error('Language update failed:', error);
}
}
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +15 to +19
description: t('trust_description', {
product: isGeneral ? 'Postiz' : 'Gitroom',
repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
repoLinkEnd: '</a>'
}),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security: Ensure HTML in translations is properly sanitized

The use of dangerouslySetInnerHTML with HTML content from translations could potentially lead to XSS vulnerabilities. Consider:

  1. Using a sanitization library
  2. Moving the link component outside of translations

Example safer approach:

-      description: t('trust_description', {
-        product: isGeneral ? 'Postiz' : 'Gitroom',
-        repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
-        repoLinkEnd: '</a>'
-      }),
+      description: (
+        <>
+          {t('trust_description_start', { product: isGeneral ? 'Postiz' : 'Gitroom' })}
+          <a 
+            href="https://github.com/gitroomhq/postiz-app" 
+            target="_blank" 
+            rel="noopener noreferrer" 
+            style={{ textDecoration: 'underline' }}
+          >
+            {t('trust_description_link')}
+          </a>
+          {t('trust_description_end')}
+        </>
+      )
📝 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
description: t('trust_description', {
product: isGeneral ? 'Postiz' : 'Gitroom',
repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
repoLinkEnd: '</a>'
}),
description: (
<>
{t('trust_description_start', { product: isGeneral ? 'Postiz' : 'Gitroom' })}
<a
href="https://github.com/gitroomhq/postiz-app"
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'underline' }}
>
{t('trust_description_link')}
</a>
{t('trust_description_end')}
</>
)

Removed some non null symbols
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 (2)
apps/frontend/src/app/layout.tsx (2)

38-39: Document the reason for @ts-expect-error

The type ignore comment should either be:

  1. Removed by fixing the underlying type issue
  2. Documented with the specific reason why it's needed
  3. Reference the related issue if it's a known problem

53-53: Simplify boolean expression

The double negation !!process.env.IS_GENERAL is unnecessary when the value is already being used in a boolean context.

-domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
+domain={Boolean(process.env.IS_GENERAL) ? 'postiz.com' : 'gitroom.com'}

Or even simpler:

-domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
+domain={process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
🧰 Tools
🪛 Biome (1.9.4)

[error] 53-53: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a816afb and 6b4b98d.

📒 Files selected for processing (1)
  • apps/frontend/src/app/layout.tsx (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/frontend/src/app/layout.tsx

[error] 53-53: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🔇 Additional comments (3)
apps/frontend/src/app/layout.tsx (3)

16-17: LGTM: Proper i18n setup with next-intl

The implementation correctly uses server-side internationalization utilities and follows Next.js best practices for async server components.

Also applies to: 26-27


29-29: LGTM: Proper accessibility implementation with dynamic lang attribute

The HTML lang attribute is correctly set using the dynamic locale, which is essential for accessibility and SEO.


41-50: Consider implementing environment variable validation

Previous comments addressed the non-null assertions, but there's a broader architectural consideration here.

Consider implementing a centralized environment variable validation at app startup:

  1. Create a config validation module
  2. Validate all required environment variables during initialization
  3. Export a strongly-typed config object

This would eliminate the need for non-null assertions and provide better error messages if environment variables are missing.

Example implementation:

// config/environment.ts
export function validateConfig() {
  const required = [
    'STORAGE_PROVIDER',
    'NEXT_PUBLIC_BACKEND_URL',
    // ... other required vars
  ] as const;
  
  const missing = required.filter(key => !process.env[key]);
  if (missing.length > 0) {
    throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
  }
  
  return {
    storageProvider: process.env.STORAGE_PROVIDER as 'local' | 'cloudflare',
    backendUrl: process.env.NEXT_PUBLIC_BACKEND_URL,
    // ... other vars with proper typing
  };
}

Solve the possible undefined value in the condition
Added some ui changes by reducing string interpolation
Made changes for better ux in fr.json
@Aspireve
Copy link
Author

Heyy Hi!! @A1exKH I resolved all the conflicts in this PR

Copy link

@A1exKH A1exKH left a comment

Choose a reason for hiding this comment

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

@Aspireve Code LGTM.
Please, resolve conflicts for this branch.

@Aspireve
Copy link
Author

Aspireve commented Dec 23, 2024

Heyy Hi!! @A1exKH
Which other stuff should I need to change for the review

@A1exKH
Copy link

A1exKH commented Dec 23, 2024

@Aspireve please merge the latest version of main branch into your PR.

@Aspireve
Copy link
Author

It might be a bit challenging for me since I'm not fully familiar with the new code that was pushed.

However, I'll still do my best to solve them based on what I know.

If I encounter any issues or get stuck, would you be able to assist me?

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.

3 participants