Conversation
|
@AnoukRImola is attempting to deploy a commit to the Trustless Work Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds comprehensive i18n: message catalogs (en/es), next-intl wiring (routing, request, navigation, middleware), locale-aware layouts/providers, SharedTranslationProvider, language switchers, and replaces hardcoded UI strings across apps and shared packages with translation lookups. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Client (Browser)
participant MW as Middleware
participant Server as Next.js Server
participant RequestCfg as i18n Request Config
participant Messages as Message Bundles
participant App as App (Locale Layout / Providers)
Browser->>MW: Request /locale/path
MW->>RequestCfg: resolve locale (routing/request)
RequestCfg->>Messages: dynamic import ../../messages/{locale}.json
Messages-->>RequestCfg: messages
RequestCfg-->>Server: { locale, messages }
Server->>App: render with NextIntlClientProvider(messages)
App-->>Browser: localized HTML + client bundles
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (12)
packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx (1)
60-60:⚠️ Potential issue | 🟡 MinorSuccess message not translated.
This hardcoded toast message is inconsistent with the i18n changes in this PR. The button labels use
t()but the success message remains in English.🌍 Suggested fix
- toast.success("Milestone approved flag updated successfully"); + toast.success(t("escrow.approve.success"));Ensure the corresponding translation key is added to both
en.jsonandes.jsonmessage files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx` at line 60, Replace the hardcoded toast.success message in the ApproveMilestone component with a translated string using the t() function (i.e., call t('...') instead of the English literal) at the location where toast.success("Milestone approved flag updated successfully") is called; add the new translation key (for example "escrows.approveMilestone.success") to both en.json and es.json message files with appropriate English and Spanish texts so the toast displays the localized message.apps/investor-tokenization/src/features/investments/components/InvestmentCard.tsx (1)
63-70:⚠️ Potential issue | 🟠 MajorUse active locale for numeric/date formatting (not hardcoded
"en-US")Line 67, Line 83, and Line 118 force English formatting, so Spanish routes still show English-formatted numbers/dates.
💡 Proposed fix
-import { useTranslations } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; @@ export const InvestmentCard = ({ investment }: InvestmentCardProps) => { + const locale = useLocale(); const t = useTranslations("investments"); const tCampaigns = useTranslations("campaigns"); @@ - {usdcAmount.toLocaleString("en-US", { + {usdcAmount.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} @@ - {tokenAmount.toLocaleString("en-US", { + {tokenAmount.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} @@ - {createdAt.toLocaleDateString("en-US", { + {createdAt.toLocaleDateString(locale, { year: "numeric", month: "short", day: "numeric", })}This pattern also appears in InvestmentsView.tsx, InvestDialog.tsx, ProjectCard.tsx, and Carousel.tsx—consider applying the same fix across the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/investments/components/InvestmentCard.tsx` around lines 63 - 70, The code currently forces "en-US" in numeric/date formatting (e.g., usdcAmount.toLocaleString("en-US", ... ) in InvestmentCard.tsx), causing wrong locale presentation; update these calls to use the app/user active locale (obtain from i18n/locale provider or the existing translation hook like t or a locale context) instead of the hardcoded "en-US", and apply the same change for other instances (InvestmentsView.tsx, InvestDialog.tsx, ProjectCard.tsx, Carousel.tsx) and for date formatting calls so numbers and dates render with the current locale.packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/dialog/ApproveMilestone.tsx (1)
59-76:⚠️ Potential issue | 🟡 MinorIncomplete i18n migration: some strings remain hardcoded.
Several user-facing strings in this form are not internationalized:
- Line 60:
"Milestone"label- Line 70:
"Select milestone"placeholder- Line 75: Fallback text
`Milestone ${idx + 1}`For consistency with the comprehensive i18n effort in this PR, these should also use translation keys.
🌐 Proposed fix to complete i18n migration
<FormItem> <FormLabel className="flex items-center"> - Milestone<span className="text-destructive ml-1">*</span> + {t("escrow.approve.milestoneLabel")}<span className="text-destructive ml-1">*</span> </FormLabel> <FormControl> <Select value={field.value} onValueChange={(e) => { field.onChange(e); }} > <SelectTrigger className="w-full"> - <SelectValue placeholder="Select milestone" /> + <SelectValue placeholder={t("escrow.approve.selectPlaceholder")} /> </SelectTrigger> <SelectContent> {(selectedEscrow?.milestones || []).map((m, idx) => ( <SelectItem key={`ms-${idx}`} value={String(idx)}> - {m?.description || `Milestone ${idx + 1}`} + {m?.description || t("escrow.approve.milestoneFallback", { index: idx + 1 })} </SelectItem> ))} </SelectContent>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/dialog/ApproveMilestone.tsx` around lines 59 - 76, The FormLabel "Milestone", the SelectValue placeholder "Select milestone", and the SelectItem fallback `Milestone ${idx + 1}` are still hardcoded; update these to use the project's i18n helper (e.g., the t function from useTranslation) so they pull from translation keys instead. In the component where FormLabel, SelectValue and the mapping over selectedEscrow?.milestones are defined, import/use the translation hook (e.g., useTranslation or t) and replace "Milestone", "Select milestone", and the template fallback with calls like t('escrow.milestoneLabel'), t('escrow.selectMilestonePlaceholder'), and t('escrow.milestoneFallback', { number: idx + 1 }) respectively so the UI strings are localized.packages/tw-blocks-shared/src/escrows/multi-release/withdraw-remaining-funds/dialog/WithdrawRemainingFunds.tsx (1)
61-154:⚠️ Potential issue | 🟡 MinorIncomplete translation coverage.
Several UI strings remain hardcoded while others are translated, creating an inconsistent user experience:
- Line 61:
"Distributions"- Line 74:
"Address"- Line 78:
"Receiver address"- Line 95:
"Amount"- Line 131:
"Add Item"- Line 137:
"Total Amount: "- Line 142:
"Difference: "- Line 149:
"Total Balance: "Consider adding these strings to the translation files for complete i18n coverage.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/withdraw-remaining-funds/dialog/WithdrawRemainingFunds.tsx` around lines 61 - 154, The UI contains hardcoded strings inside the WithdrawRemainingFunds component (e.g., the FormLabel/placeholder/button/labels in the distributions list and totals) — replace each literal ("Distributions", "Address", "Receiver address", "Amount", "Add Item", "Total Amount: ", "Difference: ", "Total Balance: ") with translation lookups (using the project's i18n hook/function used elsewhere in this file, e.g., t(...) or useTranslation()) and add corresponding keys to the locale JSON files; ensure keys are descriptive (e.g., withdrawRemaining.distributions, withdrawRemaining.address, withdrawRemaining.receiverPlaceholder, withdrawRemaining.amount, withdrawRemaining.addItem, withdrawRemaining.totalAmount, withdrawRemaining.difference, withdrawRemaining.totalBalance) and update any tests/stories that assert visible text if present.apps/backoffice-tokenization/src/features/vaults/deploy/dialog/VaultDeploySuccessDialog.tsx (1)
64-70:⚠️ Potential issue | 🟡 MinorUse a contract/explorer label here.
This href opens a contract page (
/contract/${address}), soviewTransactionis misleading. AviewContractorviewOnExplorerkey would match the actual destination.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/vaults/deploy/dialog/VaultDeploySuccessDialog.tsx` around lines 64 - 70, The translation key used in VaultDeploySuccessDialog's Link is misleading—tCommon("viewTransaction") points to a contract explorer URL; update the component to use a more accurate key such as tCommon("viewContract") or tCommon("viewOnExplorer") and replace the string in the Link render, then add the corresponding new key(s) and translations in the i18n resource files so the label matches the destination; locate this in VaultDeploySuccessDialog.tsx around the Link component and update both the key usage and the translation entries.packages/tw-blocks-shared/src/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx (1)
84-121:⚠️ Potential issue | 🟠 MajorThis dialog still contains multiple hardcoded English strings.
The selector, distributions section, field labels/placeholders, and totals copy remain unlocalized, so Spanish mode will be partially untranslated.
Also applies to: 133-160, 182-214
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx` around lines 84 - 121, The component ResolveDispute.tsx contains multiple hardcoded English strings (e.g., the "Milestone" FormLabel, SelectValue placeholder "Select milestone", SelectItem fallback label `Milestone ${idx + 1}`, the "Distributions" label and other text around lines referenced) — replace these with calls to the app's localization utilities (e.g., useTranslation()/t or the existing i18n helper) so all labels, placeholders, button text and totals use translated keys; update usages in the Select (SelectValue placeholder), SelectItem text, FormLabel and any totals/distribution strings referenced in the other ranges (133–160, 182–214) to use translation keys (e.g., t('escrow.milestone'), t('escrow.select_milestone_placeholder'), t('escrow.milestone_index', { index: idx + 1 }), t('escrow.distributions'), etc.) and ensure keys are added to the locale files.packages/tw-blocks-shared/src/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx (1)
74-112:⚠️ Potential issue | 🟠 MajorSeveral visible strings are still hardcoded, causing mixed-language UI.
The milestone selector and evidence field still render English literals, so locale switching is incomplete in this dialog.
🌐 Suggested i18n completion
-<FormLabel className="flex items-center"> - Milestone +<FormLabel className="flex items-center"> + {t("escrow.changeStatus.milestoneLabel")} <span className="text-destructive ml-1">*</span> </FormLabel> -<SelectValue placeholder="Select milestone" /> +<SelectValue placeholder={t("escrow.changeStatus.selectMilestone")} /> -{m?.description || `Milestone ${idx + 1}`} +{m?.description || `${t("escrow.changeStatus.milestoneFallback")} ${idx + 1}`} -<FormLabel>Evidence</FormLabel> +<FormLabel>{t("escrow.changeStatus.evidenceLabel")}</FormLabel> -<Textarea placeholder="Enter evidence (optional)" {...field} /> +<Textarea placeholder={t("escrow.changeStatus.evidencePlaceholder")} {...field} />Also applies to: 131-141
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx` around lines 74 - 112, The Milestone selector uses hardcoded English strings — replace the literals in ChangeMilestoneStatus.tsx (references: showSelectMilestone, FormLabel label text "Milestone", the destructive asterisk label, SelectValue placeholder "Select milestone", and the SelectItem fallback `{m?.description || `Milestone ${idx + 1}`}`) with calls to your i18n function (e.g., t('...') or useLocale()/translate) and add corresponding locale keys; also update the evidence field strings referenced around the same component (lines ~131-141) to use the same translation keys so all displayed UI text in this dialog is localized. Ensure keys are descriptive (e.g., escrow.milestone.label, escrow.milestone.selectPlaceholder, escrow.milestone.fallback) and wired into the locales.apps/investor-tokenization/src/lib/contractErrorHandler.ts (1)
113-129:⚠️ Potential issue | 🟡 MinorUse translated title in coded-error fallback when
tis provided.When
texists but i18n path is not used, Line 121 and Line 127 still return"Contract Error", producing mixed-language output.🛠️ Proposed change
if (humanMessage) { return { - message: "Contract Error", + message: t ? t("title") : "Contract Error", details: humanMessage, }; } return { - message: "Contract Error", + message: t ? t("title") : "Contract Error", details: `Contract error code ${errorCode}`, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/lib/contractErrorHandler.ts` around lines 113 - 129, The fallback returns a hardcoded title "Contract Error" even when the translation function t is provided; update the returns in the contractErrorHandler (the branch using errorMap/humanMessage and the final fallback) to use t(...) for the title when t is defined (e.g., title: t('contractError.title') or the existing i18n key used elsewhere), keeping details as-is (humanMessage or `Contract error code ${errorCode}`) so the output language is consistent whenever the t parameter is passed into the function.packages/tw-blocks-shared/src/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx (1)
72-91:⚠️ Potential issue | 🟡 MinorComplete i18n coverage for remaining lock/help texts.
A few user-facing strings in this block are still hardcoded English (
Update escrow details and milestones,Escrow is locked, and the explanatory paragraph), so they won’t localize.🌐 Proposed change
-<p className="text-muted-foreground mt-1"> - Update escrow details and milestones -</p> +<p className="text-muted-foreground mt-1"> + {t("escrow.update.detailsAndMilestones")} +</p> -<span className="text-yellow-600 dark:text-yellow-500 font-medium"> - Escrow is locked -</span> +<span className="text-yellow-600 dark:text-yellow-500 font-medium"> + {t("escrow.update.lockedTitle")} +</span> -<p className="text-muted-foreground font-medium"> - When the escrow has balance, it cannot be updated in all - fields, just adding new milestones is allowed. -</p> +<p className="text-muted-foreground font-medium"> + {t("escrow.update.lockedDescription")} +</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx` around lines 72 - 91, Replace the three hardcoded English strings in UpdateEscrow.tsx with i18n lookups using the existing translation function t; specifically change the subtitle "Update escrow details and milestones" to a t(...) call (e.g., t("escrow.update.multiReleaseSubtitle")), the visible lock label "Escrow is locked" to t("escrow.locked.title"), and the explanatory paragraph to t("escrow.locked.description"); update any missing keys in your translation files to match these identifiers and keep the conditional block using isEscrowLocked and the Lock icon unchanged.apps/investor-tokenization/src/features/tokens/components/InvestDialog.tsx (1)
292-297:⚠️ Potential issue | 🟡 MinorUse locale-aware number formatting instead of fixed
en-US.Displayed amounts are still formatted with
"en-US", which breaks localized numeric conventions on Spanish routes.Also applies to: 327-327, 335-335, 341-341
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/tokens/components/InvestDialog.tsx` around lines 292 - 297, Replace the hardcoded "en-US" locale used for formatting amounts in the InvestDialog component with the runtime locale so numbers follow the user's language/region; e.g., obtain the current locale from your i18n instance (e.g., i18n.language) or Next/router (router.locale) and pass that variable into toLocaleString or Intl.NumberFormat when formatting totalAmount and other monetary displays (references: InvestDialog, totalAmount, currency — occurrences around the shown block and the other three spots reported at lines 327, 335, 341).apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx (2)
84-89:⚠️ Potential issue | 🟠 MajorRemove
anytype cast and use proper type narrowing.The code disables TypeScript type safety with
as anyat the point where remote data is consumed. UseArray.isArray()to narrow the type before accessing array elements:Suggested fix
- // eslint-disable-next-line `@typescript-eslint/no-explicit-any` - const data = (await getEscrowByContractIds({ + const data = await getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true, - })) as any; - if (!data || !data[0]) throw new Error(t("escrowNotFound")); + }); + if (!Array.isArray(data) || data.length === 0) { + throw new Error(t("escrowNotFound")); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx` around lines 84 - 89, Remove the unsafe "as any" cast when consuming getEscrowByContractIds; instead let the call return its declared type (or add a proper return type annotation) and use Array.isArray(data) and length checks to narrow the type before accessing elements. Specifically, replace the cast on the result of getEscrowByContractIds with a typed value (or no cast), then do: if (!Array.isArray(data) || data.length === 0) throw new Error(t("escrowNotFound")); and only use data[0] after that check so TypeScript can infer the correct element type for further use of escrowId and the retrieved escrow.
79-99:⚠️ Potential issue | 🟠 MajorAddress type safety violation and add missing callback dependencies.
The callback uses
t(...)and needs it in the dependency list to ensure fresh translations. Additionally, replace theas anycast withunknownand type narrowing to comply with TypeScript guidelines.Suggested fix
const fetchEscrow = useCallback( async (escrowId: string) => { setLoading(true); setError(null); try { - // eslint-disable-next-line `@typescript-eslint/no-explicit-any` - const data = (await getEscrowByContractIds({ + const data = await getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true, - })) as any; + }); + if (typeof data !== 'object' || data === null || !Array.isArray(data)) { + throw new Error(t("escrowNotFound")); + } - if (!data || !data[0]) throw new Error(t("escrowNotFound")); - setSelectedEscrow(data[0]); + const escrow = data[0]; + if (!escrow) throw new Error(t("escrowNotFound")); + setSelectedEscrow(escrow); } catch (err) { setError(handleError(err as ErrorResponse).message); } finally { setLoading(false); } }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [getEscrowByContractIds, setSelectedEscrow, t], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx` around lines 79 - 99, The fetchEscrow callback currently unsafely casts the response to any and omits dependencies; change the cast from as any to as unknown and perform runtime type narrowing (e.g., check Array.isArray(data) and typeof data[0] or a shape check) before assigning setSelectedEscrow(data[0]); also update the dependency array to include t and any external functions/vars used inside the callback (e.g., getEscrowByContractIds, handleError, setSelectedEscrow, setLoading, setError) so React re-creates the callback when those change.
🧹 Nitpick comments (19)
apps/backoffice-tokenization/src/features/home/BentoGrid.tsx (1)
224-224: Consider using next-intl's type utilities for stricter key validation.The current
(key: string) => stringtype works but doesn't catch typos in translation keys at compile time. You could leverage next-intl's type system for stronger guarantees if desired.♻️ Optional: Extract a shared type for translation props
// In a shared types file or at the top of this file: type TranslationFunction = ReturnType<typeof useTranslations<'home'>>; // Then use it: const SkeletonFour = ({ t }: { t: TranslationFunction }) => {This provides autocomplete for translation keys and compile-time errors for invalid keys.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/home/BentoGrid.tsx` at line 224, The prop type for SkeletonFour uses a broad (key: string) => string which doesn't enforce valid translation keys; change it to use next-intl's typed translation function by deriving a shared type (e.g. TranslationFunction = ReturnType<typeof useTranslations<'home'>>) and update the prop to ({ t }: { t: TranslationFunction }) so SkeletonFour receives a strongly-typed t that provides autocomplete and compile-time key validation.packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx (1)
16-16: Inconsistent import style with other internal package imports.Other imports in this file use the
@tokenization/tw-blocks-shared/src/...alias pattern (lines 3, 4, 13, 14), but this import uses a relative path. For consistency and maintainability, consider using the same pattern.♻️ Suggested fix
-import { useSharedTranslation } from "../../../../i18n/TranslationProvider"; +import { useSharedTranslation } from "@tokenization/tw-blocks-shared/src/i18n/TranslationProvider";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx` at line 16, In ApproveMilestone.tsx replace the relative import of useSharedTranslation with the internal package alias to match other imports; change the import source for the useSharedTranslation symbol from "../../../../i18n/TranslationProvider" to the same alias pattern used elsewhere (e.g. "@tokenization/tw-blocks-shared/src/i18n/TranslationProvider") so imports in this file are consistent and maintainable.apps/investor-tokenization/src/features/claim-roi/VaultCard.tsx (1)
5-6: Use the repository import alias instead of a relative path.Please switch
useVaultInfoto@/*alias import style in this file.🔧 Suggested change
-import { useVaultInfo } from "./hooks/useVaultInfo"; +import { useVaultInfo } from "@/features/claim-roi/hooks/useVaultInfo";As per coding guidelines: "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/claim-roi/VaultCard.tsx` around lines 5 - 6, The import of the hook useVaultInfo in VaultCard.tsx uses a relative path; update it to use the repository path alias by replacing the relative import with the `@/`* alias (e.g., import { useVaultInfo } from "@/features/claim-roi/hooks/useVaultInfo") so it resolves via the ./src/* mapping and conforms to project import guidelines; ensure the import specifier matches the existing hook file and that any related imports in this file remain unchanged.apps/backoffice-tokenization/src/components/layout/app-sidebar.tsx (1)
28-35: Footer items remain hardcoded in English.The
footerItemsarray still contains hardcoded English strings ("Documentation","View documentation") while nav items are now translated. Consider movingfooterItemsinside the component and adding corresponding translation keys for consistency.Example approach
export function AppSidebar() { const t = useTranslations("nav"); + const tCommon = useTranslations("common"); + const footerItems: AppSidebarFooterItem[] = [ + { + label: tCommon("documentation"), + icon: BookOpen, + href: "https://interactuar.gitbook.io/interactuar-x-trustless/", + tooltip: tCommon("viewDocumentation"), + }, + ]; const navItems: AppSidebarNavItem[] = [Then add the keys to
en.jsonandes.json.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/components/layout/app-sidebar.tsx` around lines 28 - 35, Move the hardcoded footerItems array into the AppSidebar component and replace literal strings with translation keys using the existing i18n hook (e.g., useTranslation or t) so footer labels and tooltips are localized; update the footerItems declaration (AppSidebarFooterItem[], BookOpen icon stays) to call t('sidebar.footer.documentation') and t('sidebar.footer.viewDocumentation') and add those keys to en.json and es.json with appropriate translations.apps/backoffice-tokenization/src/components/shared/language-switcher.tsx (1)
25-36: Consider adding accessibility attributes.The buttons lack
aria-labeloraria-pressedattributes that would help screen reader users understand the current state.Accessibility improvement
<button key={code} onClick={() => switchLocale(code)} + aria-pressed={code === locale} + aria-label={`Switch to ${code === "es" ? "Spanish" : "English"}`} className={cn(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/components/shared/language-switcher.tsx` around lines 25 - 36, The language switcher buttons are missing accessibility attributes; update the JSX button (the element that calls switchLocale(code)) to include an appropriate aria-label (e.g., the language label or a localized string like `Switch to ${label}`) and an aria-pressed attribute that reflects the current state (set aria-pressed={code === locale} so the active language is true). Ensure the change is applied to the button rendered in language-switcher.tsx so screen readers can announce the control and its selected state.apps/investor-tokenization/src/features/investments/InvestmentsView.tsx (1)
119-124: Hardcoded locale in number formatting.The
"en-US"locale is hardcoded for currency formatting, which is inconsistent with the i18n approach. Consider using the current locale for consistent number formatting based on user language preference.♻️ Proposed fix
+import { useLocale } from "next-intl";Then in the component:
+ const currentLocale = useLocale(); ... - {totalInvested.toLocaleString("en-US", { + {totalInvested.toLocaleString(currentLocale, { minimumFractionDigits: 2, maximumFractionDigits: 2, })}{" "}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/investments/InvestmentsView.tsx` around lines 119 - 124, The number formatting uses a hardcoded "en-US" locale when rendering totalInvested in InvestmentsView.tsx; replace that literal with the app's current locale (e.g., obtain from your i18n hook/provider or Next.js router.locale) and pass it to toLocaleString/Intl.NumberFormat, ensuring you provide a sensible fallback (like 'en-US') if the locale is missing; update the toLocaleString call on totalInvested to use that locale variable so formatting follows the user's language preference.packages/tw-blocks-shared/src/i18n/TranslationProvider.tsx (1)
43-60: Memoize the context value before passing it to the provider.
value={{ t, locale }}creates a new object on every parent render, so alluseSharedTranslation()consumers re-render even whenmessagesandlocaleare unchanged.♻️ Proposed fix
import { createContext, useContext, useCallback, + useMemo, type ReactNode, } from "react"; @@ export function SharedTranslationProvider({ messages, locale, children, }: SharedTranslationProviderProps) { const t: TranslationFn = useCallback( (key, params) => { const value = getNestedValue(messages, key); if (value === undefined) return key; return params ? interpolate(value, params) : value; }, [messages], ); + const value = useMemo(() => ({ t, locale }), [t, locale]); return ( - <SharedTranslationContext.Provider value={{ t, locale }}> + <SharedTranslationContext.Provider value={value}> {children} </SharedTranslationContext.Provider> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/i18n/TranslationProvider.tsx` around lines 43 - 60, The provider is creating a fresh object for value on every render causing unnecessary consumer re-renders; inside SharedTranslationProvider memoize the context value (the object containing t and locale) with useMemo and pass that memoized value to SharedTranslationContext.Provider instead of inline value={{ t, locale }}; ensure the dependency array includes t and locale (or messages/locale if you prefer) so the value only changes when translation function or locale actually change.packages/tw-blocks-shared/src/escrows/escrows-by-signer/details/MilestoneCard.tsx (1)
23-23: Use the project import alias forTranslationProvider.Please replace the deep relative import with the configured alias to keep imports stable across refactors.
♻️ Proposed change
-import { useSharedTranslation } from "../../../i18n/TranslationProvider"; +import { useSharedTranslation } from "@/i18n/TranslationProvider";As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/escrows-by-signer/details/MilestoneCard.tsx` at line 23, Replace the deep relative import of TranslationProvider with the project path alias: change the import that brings in useSharedTranslation from "../../../i18n/TranslationProvider" to use the configured alias import starting with "@/...", updating the import that provides useSharedTranslation (from the TranslationProvider module) so the file MilestoneCard.tsx uses the `@/`* path mapping instead of deep relative paths.packages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/EscrowsCards.tsx (1)
31-31: Prefer alias import foruseSharedTranslation.Replace the relative path with the configured alias for consistency.
As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/EscrowsCards.tsx` at line 31, Replace the relative import for useSharedTranslation with the project path alias: change the import line that references useSharedTranslation from "../../../i18n/TranslationProvider" to use the configured alias (e.g. "@/i18n/TranslationProvider") so the component EscrowsCards.tsx uses the `@/`* -> ./src/* mapping consistently.packages/tw-blocks-shared/src/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx (1)
33-33: Import path should use@/*alias.Please switch this relative import to the project alias to align with repo conventions.
As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx` at line 33, The import in InitializeEscrow.tsx uses a relative path for useSharedTranslation; replace the relative import "import { useSharedTranslation } from '../../../../i18n/TranslationProvider';" with the project path-alias form "import { useSharedTranslation } from '@/i18n/TranslationProvider';" so all TypeScript/TSX imports follow the `@/*` -> `./src/*` convention and satisfy the linting rule; update any other similar imports in this file referencing TranslationProvider to the alias as well.packages/tw-blocks-shared/src/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx (1)
30-30: Switch this import to the@/*alias.Use the project alias instead of a multi-level relative path for consistency and safer refactors.
As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx` at line 30, Replace the multi-level relative import of useSharedTranslation with the project alias form: locate the import statement in ChangeMilestoneStatus (the line importing useSharedTranslation from "../../../../i18n/TranslationProvider") and update it to use the `@/`* path alias that maps to ./src/* (e.g., import useSharedTranslation from "@/i18n/TranslationProvider"); keep the imported symbol name unchanged.packages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/Filters.tsx (1)
33-33: Use@/*alias for this import.Please replace the relative path with the configured alias for consistency with project rules.
As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/Filters.tsx` at line 33, Replace the relative import in Filters.tsx with the project path alias: change the import of useSharedTranslation from "../../../i18n/TranslationProvider" to use the "@/..." alias (e.g. import from "@/i18n/TranslationProvider") so it follows the required `@/*` → `./src/*` mapping and keeps imports consistent with the project's alias rules.packages/tw-blocks-shared/src/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx (1)
30-30: Use the@/*alias instead of a deep relative path.This keeps module boundaries cleaner and avoids fragile import chains.
As per coding guidelines,
**/*.{ts,tsx}must use path alias@/*mapping to./src/*for imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx` at line 30, The import in ResolveDispute.tsx currently uses a deep relative path to TranslationProvider; change the import to use the project path alias instead (e.g. import { useSharedTranslation } from "@/i18n/TranslationProvider") so it follows the /*.{ts,tsx} alias rule; update the import statement in the ResolveDispute component file (where useSharedTranslation is referenced) to use the `@/`* mapping.packages/tw-blocks-shared/src/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx (1)
32-32: Use alias import instead of deep relative traversal.Line 32 uses
../../../../..., which is brittle and conflicts with the import convention.♻️ Proposed change
-import { useSharedTranslation } from "../../../../i18n/TranslationProvider"; +import { useSharedTranslation } from "@/i18n/TranslationProvider";As per coding guidelines, "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tw-blocks-shared/src/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx` at line 32, Replace the brittle deep-relative import in UpdateEscrow.tsx that currently imports useSharedTranslation via "../../../../i18n/TranslationProvider" with the project path-alias form; update the import for the useSharedTranslation symbol to use the alias path starting with "@/i18n/TranslationProvider" to follow the codebase convention and avoid deep traversal.apps/investor-tokenization/src/features/roi/components/campaign-card.tsx (1)
19-19: Switch to@/*alias for local module import.Line 19 uses a relative import where alias import is preferred.
♻️ Proposed change
-import { getCampaignStatusConfig } from "../constants/campaign-status"; +import { getCampaignStatusConfig } from "@/features/roi/constants/campaign-status";As per coding guidelines, "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/roi/components/campaign-card.tsx` at line 19, Replace the relative import of getCampaignStatusConfig in campaign-card.tsx with the project path-alias form (use the '@/...' mapping to ./src/*) so the module is imported via the aliased campaign-status module instead of a relative path; update the import that references getCampaignStatusConfig from the campaign-status module to use the `@/`* alias.apps/backoffice-tokenization/src/i18n/request.ts (1)
2-2: Use project alias import for routing in i18n request config.Line 2 uses a relative internal import; this should use the
@/*alias for consistency with project import rules.♻️ Suggested fix
-import { routing } from "./routing"; +import { routing } from "@/i18n/routing";As per coding guidelines: "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/i18n/request.ts` at line 2, Replace the relative import of the routing module with the project path-alias form: change the import that currently references "./routing" in apps/backoffice-tokenization/src/i18n/request.ts to use "@/i18n/routing" so the module referenced by the routing symbol is imported via the `@/`* alias (update the import statement that imports routing accordingly).apps/backoffice-tokenization/src/features/campaigns/hooks/use-create-campaign.ts (1)
308-313: AddtCommonto therunDeployAndCreatedependencies.Line 309 closes over
tCommon, but Line 313 doesn’t track it. That leaves the Step 3 error handler vulnerable to stale translations after a locale change.♻️ Minimal fix
- }, [walletAddress, router, queryClient]); + }, [walletAddress, router, queryClient, tCommon]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/campaigns/hooks/use-create-campaign.ts` around lines 308 - 313, The callback runDeployAndCreate closes over the translation function tCommon but its dependency array only contains [walletAddress, router, queryClient], causing stale translations after locale changes; update the useCallback dependency array for runDeployAndCreate to include tCommon (the translation function referenced when building the error message), so the function is recreated when tCommon changes and the setPhaseStatus / setDeployFailedAt error handler uses the current locale.apps/investor-tokenization/src/app/[locale]/layout.tsx (1)
17-18: Prefer@/...imports for app-local modules.These relative imports are fragile during refactors and don’t follow the TS/TSX import-path guideline.
As per coding guidelines: "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/app/`[locale]/layout.tsx around lines 17 - 18, In layout.tsx replace the fragile relative imports of appEn and appEs with the path-alias imports mandated by our guideline: import the JSONs via the `@/`* alias (e.g., "@/messages/en.json" and "@/messages/es.json") and ensure the import symbols appEn and appEs are used unchanged; verify the tsconfig/next.config path-mapping for @ -> ./src is present so the new imports resolve correctly.apps/backoffice-tokenization/src/app/[locale]/layout.tsx (1)
17-18: Prefer@/...imports for app-local modules.These deep relative imports are brittle and break the repository import convention for TS/TSX files.
As per coding guidelines: "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/app/`[locale]/layout.tsx around lines 17 - 18, Replace the brittle deep-relative imports of localized JSON in layout.tsx by using the project path alias; change the imports that define appEn and appEs (currently from "../../../messages/en.json" and "../../../messages/es.json") to use the alias (e.g. "@/messages/en.json" and "@/messages/es.json") so they follow the `@/*` -> `./src/*` convention and build tooling; ensure the tsconfig/next config already has the alias mapping if missing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 266b29b1-db85-4bfa-9ae6-f80329224403
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (99)
apps/backoffice-tokenization/messages/en.jsonapps/backoffice-tokenization/messages/es.jsonapps/backoffice-tokenization/next.config.tsapps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/loans/[id]/page.tsxapps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/new/page.tsxapps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/page.tsxapps/backoffice-tokenization/src/app/[locale]/(dashboard)/layout.tsxapps/backoffice-tokenization/src/app/[locale]/(dashboard)/roi/page.tsxapps/backoffice-tokenization/src/app/[locale]/layout.tsxapps/backoffice-tokenization/src/app/[locale]/page.tsxapps/backoffice-tokenization/src/app/layout.tsxapps/backoffice-tokenization/src/components/layout/app-header.tsxapps/backoffice-tokenization/src/components/layout/app-sidebar.tsxapps/backoffice-tokenization/src/components/shared/campaign-card.tsxapps/backoffice-tokenization/src/components/shared/language-switcher.tsxapps/backoffice-tokenization/src/features/campaigns/components/campaign-filter.tsxapps/backoffice-tokenization/src/features/campaigns/components/campaign-list.tsxapps/backoffice-tokenization/src/features/campaigns/components/campaign-search.tsxapps/backoffice-tokenization/src/features/campaigns/components/campaigns-view.tsxapps/backoffice-tokenization/src/features/campaigns/components/create/create-campaign-stepper.tsxapps/backoffice-tokenization/src/features/campaigns/components/create/step-campaign-basics.tsxapps/backoffice-tokenization/src/features/campaigns/components/create/step-create-token.tsxapps/backoffice-tokenization/src/features/campaigns/components/create/step-escrow-config.tsxapps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/FundRoiDialog.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/ToggleVaultButton.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/create-roi-dialog.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/roi-table-row.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/roi-table.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/roi-view.tsxapps/backoffice-tokenization/src/features/campaigns/constants/campaign-status.tsapps/backoffice-tokenization/src/features/campaigns/hooks/use-create-campaign.tsapps/backoffice-tokenization/src/features/home/BentoGrid.tsxapps/backoffice-tokenization/src/features/home/HeroSection.tsxapps/backoffice-tokenization/src/features/tokens/deploy/dialog/TokenizeEscrow.tsxapps/backoffice-tokenization/src/features/tokens/deploy/dialog/TokenizeEscrowSuccessDialog.tsxapps/backoffice-tokenization/src/features/vaults/deploy/dialog/CreateVault.tsxapps/backoffice-tokenization/src/features/vaults/deploy/dialog/EnableVault.tsxapps/backoffice-tokenization/src/features/vaults/deploy/dialog/VaultDeploySuccessDialog.tsxapps/backoffice-tokenization/src/i18n/navigation.tsapps/backoffice-tokenization/src/i18n/request.tsapps/backoffice-tokenization/src/i18n/routing.tsapps/backoffice-tokenization/src/lib/contractErrorHandler.tsapps/backoffice-tokenization/src/lib/toastWithTx.tsxapps/backoffice-tokenization/src/middleware.tsapps/investor-tokenization/messages/en.jsonapps/investor-tokenization/messages/es.jsonapps/investor-tokenization/next.config.tsapps/investor-tokenization/src/app/[locale]/campaigns/layout.tsxapps/investor-tokenization/src/app/[locale]/campaigns/page.tsxapps/investor-tokenization/src/app/[locale]/claim-roi/page.tsxapps/investor-tokenization/src/app/[locale]/investments/page.tsxapps/investor-tokenization/src/app/[locale]/layout.tsxapps/investor-tokenization/src/app/[locale]/my-investments/layout.tsxapps/investor-tokenization/src/app/[locale]/my-investments/page.tsxapps/investor-tokenization/src/app/[locale]/page.tsxapps/investor-tokenization/src/app/layout.tsxapps/investor-tokenization/src/components/layout/app-header.tsxapps/investor-tokenization/src/components/layout/app-sidebar.tsxapps/investor-tokenization/src/components/shared/language-switcher.tsxapps/investor-tokenization/src/features/claim-roi/ClaimROIView.tsxapps/investor-tokenization/src/features/claim-roi/VaultCard.tsxapps/investor-tokenization/src/features/investments/InvestmentsView.tsxapps/investor-tokenization/src/features/investments/components/InvestmentCard.tsxapps/investor-tokenization/src/features/roi/components/campaign-card.tsxapps/investor-tokenization/src/features/roi/components/campaign-filter.tsxapps/investor-tokenization/src/features/roi/components/campaign-list.tsxapps/investor-tokenization/src/features/roi/components/campaign-search.tsxapps/investor-tokenization/src/features/roi/constants/campaign-status.tsapps/investor-tokenization/src/features/tokens/components/InvestDialog.tsxapps/investor-tokenization/src/features/transparency/ProjectCard.tsxapps/investor-tokenization/src/features/transparency/ProjectList.tsxapps/investor-tokenization/src/i18n/navigation.tsapps/investor-tokenization/src/i18n/request.tsapps/investor-tokenization/src/i18n/routing.tsapps/investor-tokenization/src/lib/contractErrorHandler.tsapps/investor-tokenization/src/middleware.tspackage.jsonpackages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/EscrowsCards.tsxpackages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/Filters.tsxpackages/tw-blocks-shared/src/escrows/escrows-by-signer/details/Entities.tsxpackages/tw-blocks-shared/src/escrows/escrows-by-signer/details/GeneralInformation.tsxpackages/tw-blocks-shared/src/escrows/escrows-by-signer/details/MilestoneCard.tsxpackages/tw-blocks-shared/src/escrows/escrows-by-signer/details/SuccessReleaseDialog.tsxpackages/tw-blocks-shared/src/escrows/multi-release/dispute-milestone/button/DisputeMilestone.tsxpackages/tw-blocks-shared/src/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsxpackages/tw-blocks-shared/src/escrows/multi-release/release-milestone/button/ReleaseMilestone.tsxpackages/tw-blocks-shared/src/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsxpackages/tw-blocks-shared/src/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsxpackages/tw-blocks-shared/src/escrows/multi-release/withdraw-remaining-funds/dialog/WithdrawRemainingFunds.tsxpackages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsxpackages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/dialog/ApproveMilestone.tsxpackages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/form/ApproveMilestone.tsxpackages/tw-blocks-shared/src/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsxpackages/tw-blocks-shared/src/escrows/single-multi-release/fund-escrow/dialog/FundEscrow.tsxpackages/tw-blocks-shared/src/i18n/TranslationProvider.tsxpackages/tw-blocks-shared/src/i18n/messages/en.jsonpackages/tw-blocks-shared/src/i18n/messages/es.jsonpackages/tw-blocks-shared/src/wallet-kit/WalletButtons.tsx
| const { locale } = await params; | ||
| const messages = appMessages[locale] ?? appMessages.es; | ||
|
|
||
| return ( | ||
| <html lang={locale}> | ||
| <head> | ||
| <meta charSet="utf-8" /> | ||
| </head> | ||
| <body className={cn(inter.variable, "antialiased font-sans")}> | ||
| <NextIntlClientProvider messages={messages} locale={locale} formats={{}} now={new Date()} timeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}> | ||
| <SharedTranslationProvider messages={sharedMessages[locale] ?? sharedMessages.es} locale={locale}> |
There was a problem hiding this comment.
Normalize unsupported locales before wiring providers.
Line 42 falls back messages to Spanish, but Lines 45/50/51 still use raw locale. If an unsupported locale slips through, text locale and provider/html locale diverge.
💡 Proposed fix
- const { locale } = await params;
- const messages = appMessages[locale] ?? appMessages.es;
+ const { locale: rawLocale } = await params;
+ const locale = rawLocale === "en" || rawLocale === "es" ? rawLocale : "es";
+ const messages = appMessages[locale];📝 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.
| const { locale } = await params; | |
| const messages = appMessages[locale] ?? appMessages.es; | |
| return ( | |
| <html lang={locale}> | |
| <head> | |
| <meta charSet="utf-8" /> | |
| </head> | |
| <body className={cn(inter.variable, "antialiased font-sans")}> | |
| <NextIntlClientProvider messages={messages} locale={locale} formats={{}} now={new Date()} timeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}> | |
| <SharedTranslationProvider messages={sharedMessages[locale] ?? sharedMessages.es} locale={locale}> | |
| const { locale: rawLocale } = await params; | |
| const locale = rawLocale === "en" || rawLocale === "es" ? rawLocale : "es"; | |
| const messages = appMessages[locale]; | |
| return ( | |
| <html lang={locale}> | |
| <head> | |
| <meta charSet="utf-8" /> | |
| </head> | |
| <body className={cn(inter.variable, "antialiased font-sans")}> | |
| <NextIntlClientProvider messages={messages} locale={locale} formats={{}} now={new Date()} timeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}> | |
| <SharedTranslationProvider messages={sharedMessages[locale] ?? sharedMessages.es} locale={locale}> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/backoffice-tokenization/src/app/`[locale]/layout.tsx around lines 41 -
51, Normalize the locale once and use the normalized value everywhere: compute
an effectiveLocale (e.g., const effectiveLocale = appMessages[locale] ? locale :
'es') before creating messages/sharedMessages and before rendering <html
lang={...}>, then use appMessages[effectiveLocale],
sharedMessages[effectiveLocale], and pass effectiveLocale into
NextIntlClientProvider and SharedTranslationProvider (instead of the raw
params.locale) so html lang and both providers always agree.
| <div className="flex flex-col gap-1"> | ||
| <span className="text-xs font-bold text-foreground"> | ||
| <span className="font-bold">Pool Size:</span> USDC {formatCurrency(escrowData?.balance ?? 0)} / USDC {formatCurrency(campaign.poolSize)} | ||
| <span className="font-bold">{t("poolSize")}:</span> USDC {formatCurrency(escrowData?.balance ?? 0)} / USDC {formatCurrency(campaign.poolSize)} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C3 'fromStroops|10_000_000|escrowData|getEscrowByContractIds|useGetEscrowFromIndexerByContractIds'Repository: Trustless-Work/tokenization-private-credit
Length of output: 37972
Normalize escrowData.balance to display units before formatting.
Line 89 formats escrowData?.balance directly, but the indexer returns escrow balances in stroops (7 decimal places). The same escrow object's milestones array is explicitly normalized via fromStroops throughout the codebase (see ProjectCard.tsx:81, Carousel.tsx:36), and roi-table-row.tsx:60 normalizes the balance by dividing by 10_000_000. Without conversion, the pool size will display 10 million times larger than the actual value.
Apply fromStroops() to escrowData.balance before passing to formatCurrency:
Suggested fix
// Line 89: Convert balance from stroops to display units
<span className="font-bold">{t("poolSize")}:</span> USDC {formatCurrency(fromStroops(escrowData?.balance ?? 0))} / USDC {formatCurrency(campaign.poolSize)}Import fromStroops from @/lib/utils if not already imported.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/backoffice-tokenization/src/components/shared/campaign-card.tsx` at line
89, escrowData.balance is in stroops and is being passed raw to formatCurrency
in the CampaignCard component (span that renders USDC
{formatCurrency(escrowData?.balance ?? 0)}), causing a 10Mx inflation; convert
the balance using fromStroops() before formatting (i.e., call
formatCurrency(fromStroops(escrowData?.balance ?? 0))) and add an import for
fromStroops from "@/lib/utils"; update the span that references
escrowData.balance and ensure no other references in campaign-card.tsx use the
raw stroop value.
| <div className="rounded-lg border bg-muted/50 p-4"> | ||
| <p className="text-sm text-muted-foreground"> | ||
| <strong>Note:</strong> This token represents Escrow{" "} | ||
| <span className="font-mono text-xs"> | ||
| {form.watch("escrowId") || "[Escrow ID]"} | ||
| </span>{" "} | ||
| and can only be minted by its Token Sale contract. | ||
| <strong>Note:</strong>{" "} | ||
| {t("tokenNote", { escrowId: form.watch("escrowId") || "[Escrow ID]" })} | ||
| </p> |
There was a problem hiding this comment.
This note block is still partially hardcoded in English.
Lines 135-136 bypass the tokens messages with Note: and "[Escrow ID]", so the localized dialog still leaks English in /es/.... Move both pieces into translations, or fold the whole sentence into a single message.
🌐 One possible fix
- <strong>Note:</strong>{" "}
- {t("tokenNote", { escrowId: form.watch("escrowId") || "[Escrow ID]" })}
+ <strong>{t("noteLabel")}</strong>{" "}
+ {t("tokenNote", {
+ escrowId: form.watch("escrowId") || t("escrowIdFallback"),
+ })}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/backoffice-tokenization/src/features/tokens/deploy/dialog/TokenizeEscrow.tsx`
around lines 133 - 137, The note text in TokenizeEscrow.tsx currently hardcodes
"Note:" and the fallback string "[Escrow ID]" around t("tokenNote"), causing
untranslated English to leak; change the UI to pass the entire sentence as a
single translation key (e.g., replace the split JSX with t("tokenNote", {
escrowId: form.watch("escrowId") })) and move the "Note:" label and the fallback
placeholder into the localization resource (use a placeholder like {{escrowId}}
in the tokenNote message so the translation contains the full sentence including
the fallback text instead of the hardcoded "[Escrow ID]").
| onClick={() => copyToClipboard(response.tokenFactoryAddress)} | ||
| > | ||
| {copiedKeyId ? "Copied!" : "Copy"} | ||
| {copiedKeyId ? t("copied") : t("copy")} |
There was a problem hiding this comment.
Scope “copied” state per field to avoid false positive labels.
Line 59 and Line 92 only check truthiness of copiedKeyId, so copying one address can make both buttons render the copied label.
🐛 Suggested fix
- {copiedKeyId ? t("copied") : t("copy")}
+ {copiedKeyId === response.tokenFactoryAddress ? t("copied") : t("copy")}
...
- {copiedKeyId ? t("copied") : t("copy")}
+ {copiedKeyId === response.tokenSaleAddress ? t("copied") : t("copy")}Also applies to: 92-92
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/backoffice-tokenization/src/features/tokens/deploy/dialog/TokenizeEscrowSuccessDialog.tsx`
at line 59, The "copied" label is rendered based only on the truthiness of
copiedKeyId so copying one address makes all buttons show "copied"; change the
rendering logic to scope the copied state to the specific field by storing (or
comparing) the id of the copied item and using an equality check per button
(e.g., use copiedKeyId === keyId or maintain a Set/map of copied ids) when
rendering the label and when setting the state in the copy handlers; update any
places that read copiedKeyId (the value used in the button label and the copy
handlers in TokenizeEscrowSuccessDialog) to use this per-field comparison so
only the copied button shows "copied".
| export function toastSuccessWithTx( | ||
| message: string, | ||
| txHash?: string, | ||
| viewTransactionLabel: string = "View Transaction", |
There was a problem hiding this comment.
Remove the English default from this helper.
The fallback on Line 10 lets callers skip localization silently, so this toast can regress to English whenever a call site forgets the CTA label. Making the label explicit here turns that into a compile-time miss instead of a runtime i18n leak.
💡 Minimal fix
export function toastSuccessWithTx(
message: string,
- txHash?: string,
- viewTransactionLabel: string = "View Transaction",
+ txHash: string | undefined,
+ viewTransactionLabel: string,
) {Also applies to: 20-20
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/backoffice-tokenization/src/lib/toastWithTx.tsx` around lines 7 - 10,
The helper toastSuccessWithTx currently provides an English default for
viewTransactionLabel which lets callers omit a localized label; remove the
default value so the signature toastSuccessWithTx(message: string, txHash?:
string, viewTransactionLabel: string) requires the caller to pass the localized
CTA. Apply the same change to the corresponding toastFailureWithTx function (the
other helper around line 20) so neither function supplies a hardcoded fallback
and both force callers to provide a localized label.
| "minimalistic-crypto-utils": "^1.0.1", | ||
| "motion": "^10.16.2", | ||
| "next": "16.0.10", | ||
| "next-intl": "^4.8.3", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
next-intl Next.js 16 compatibility
💡 Result:
next-intl is compatible with Next.js 16 starting from next-intl v4.4.0 (2025-10-22), which bumps the Next.js 16 peer dependency and updates the docs/examples for Next.js 16, including the new proxy.ts setup. (github.com)
If you upgrade to Next.js 16 while on an older next-intl, locale detection/routing can break because Next.js 16 expects proxy.ts instead of middleware.ts—upgrading to next-intl ≥ 4.4.0 and following the updated routing docs fixes this. (github.com)
Citations:
- 1: https://github.com/amannn/next-intl/releases?page=2
- 2: https://github.com/amannn/next-intl/releases?page=2
Update routing setup from middleware.ts to proxy.ts for Next.js 16 compatibility.
The next-intl version ^4.8.3 is compatible with Next.js 16.0.10 (supported from v4.4.0). However, Next.js 16 requires locale routing through proxy.ts instead of the previous middleware.ts approach. Without this setup change, locale detection and routing may break. Update the routing configuration and follow the updated next-intl documentation for Next.js 16.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 49, Update the Next.js routing setup to use proxy.ts
for locale routing required by Next.js 16 and next-intl (v4.8.3) by replacing
the existing middleware-based approach: locate the current middleware.ts usage
(and any middleware-based locale handling) and create a proxy.ts implementing
the next-intl recommended proxy routing pattern, move locale detection/rewrites
and any exports used by next-intl into proxy.ts, and remove or disable
middleware.ts so that next-intl uses the new proxy-based routing as described in
the next-intl Next.js 16 docs.
| variant={isActive ? "default" : "destructive"} | ||
| className="shrink-0" | ||
| > | ||
| {isActive ? "Active" : "Inactive"} | ||
| {isActive ? t("escrow.filters.active") : t("escrow.filters.inactive")} | ||
| </Badge> |
There was a problem hiding this comment.
Active badge is derived from filter state, not the escrow card data.
This makes every card show the same active/inactive label based on current filter toggles instead of each escrow’s real status.
🐛 Proposed fix
-<Badge
- variant={isActive ? "default" : "destructive"}
- className="shrink-0"
->
- {isActive ? t("escrow.filters.active") : t("escrow.filters.inactive")}
-</Badge>
+<Badge
+ variant={escrow.isActive ? "default" : "destructive"}
+ className="shrink-0"
+>
+ {escrow.isActive ? t("escrow.filters.active") : t("escrow.filters.inactive")}
+</Badge>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/tw-blocks-shared/src/escrows/escrows-by-signer/cards/EscrowsCards.tsx`
around lines 264 - 268, The Badge in EscrowsCards.tsx currently uses the
filter-derived isActive variable so every card shows the same label; change the
logic to derive active/inactive from the escrow item data (e.g., escrow.isActive
or escrow.status === "active") instead of the filter state, update the
conditional where Badge's variant and text are set (the existing isActive usage)
to reference the escrow record's active flag/property, and ensure any prop
passed into the Escrow card component supplies that escrow data if needed
(adjust prop names like escrow or item accordingly).
| } from "@trustless-work/escrow/types"; | ||
| import { EntityCard } from "./EntityCard"; | ||
| import { Separator } from "@tokenization/ui/separator"; | ||
| import { useSharedTranslation } from "../../../i18n/TranslationProvider"; |
There was a problem hiding this comment.
Use the @/* alias instead of a deep relative import.
Line 9 uses a fragile relative path (../../../i18n/TranslationProvider). Please switch it to the configured @/* alias.
Suggested change
-import { useSharedTranslation } from "../../../i18n/TranslationProvider";
+import { useSharedTranslation } from "@/i18n/TranslationProvider";As per coding guidelines, "Use path alias @/* mapping to ./src/* for imports".
📝 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.
| import { useSharedTranslation } from "../../../i18n/TranslationProvider"; | |
| import { useSharedTranslation } from "@/i18n/TranslationProvider"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/tw-blocks-shared/src/escrows/escrows-by-signer/details/Entities.tsx`
at line 9, Replace the fragile deep relative import of the translation provider
with the project path alias: change the import of useSharedTranslation from
"../../../i18n/TranslationProvider" to use the `@/`* alias (e.g.
"@/i18n/TranslationProvider") in the Entities.tsx file so the symbol
useSharedTranslation is imported via the aliased module instead of a relative
path.
| {t("escrow.successRelease.title")} | ||
| </DialogTitle> | ||
| <DialogDescription> | ||
| Funds were distributed successfully to the corresponding parties. | ||
| {t("escrow.successRelease.description")} |
There was a problem hiding this comment.
This dialog is still partly hard-coded in English.
The header is localized now, but the cards model below still builds visible labels like Platform, Trustless Work, Receiver, and Private as raw English strings. /es will therefore show a mixed-language success dialog unless those display labels are routed through shared messages too.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/tw-blocks-shared/src/escrows/escrows-by-signer/details/SuccessReleaseDialog.tsx`
around lines 126 - 129, The card display labels in SuccessReleaseDialog (the
`cards` model used to render Platform, Trustless Work, Receiver, Private, etc.)
are hard-coded in English; update the `cards` construction inside the
SuccessReleaseDialog component to use the translation function (e.g., t("...")
with appropriate shared message keys) for every user-facing label and tooltip
instead of raw strings, ensuring you reference the shared message keys used
elsewhere in the app so the dialog renders fully localized for other locales.
| <span className="ml-2">{t("escrow.approve.approving")}</span> | ||
| </div> | ||
| ) : ( | ||
| "Approve" | ||
| t("escrow.approve.approve") |
There was a problem hiding this comment.
Only the submit button was localized.
Milestone, Select milestone, and the fallback Milestone ${idx + 1} remain hard-coded above, so this form will still render partly in English for Spanish users. Please add keys for those labels/placeholders/fallbacks alongside the button copy.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/tw-blocks-shared/src/escrows/single-multi-release/approve-milestone/form/ApproveMilestone.tsx`
around lines 72 - 75, The form still has hard-coded strings for the milestone
label, the select placeholder, and the fallback "Milestone ${idx + 1}"; update
ApproveMilestone.tsx to replace those literals with i18n keys using the existing
t(...) function (e.g., add keys like "escrow.approve.milestone",
"escrow.approve.selectMilestone" and a parametrized fallback key such as
"escrow.approve.milestoneFallback" and call it with { number: idx + 1 }) so the
label, placeholder, and fallback are localized consistently with the submit
button.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx (2)
79-99:⚠️ Potential issue | 🟡 MinorAdd missing dependencies to
fetchEscrowcallback
fetchEscrowcapturest,getEscrowByContractIds, andsetSelectedEscrowbut is memoized with an empty dependency array. SincefetchEscrowis used as a dependency in theuseEffectbelow it and in other handlers, these missing dependencies can cause stale closures—particularlytwhich changes when the locale switches, leading to stale error messages and potentially stale API responses.Suggested fix
const fetchEscrow = useCallback( async (escrowId: string) => { setLoading(true); setError(null); try { // eslint-disable-next-line `@typescript-eslint/no-explicit-any` const data = (await getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true, })) as any; if (!data || !data[0]) throw new Error(t("escrowNotFound")); setSelectedEscrow(data[0]); } catch (err) { setError(handleError(err as ErrorResponse).message); } finally { setLoading(false); } }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [getEscrowByContractIds, setSelectedEscrow, t], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx` around lines 79 - 99, The fetchEscrow useCallback currently has an empty dependency array causing stale closures; update its dependencies to include all referenced values: t, getEscrowByContractIds, setSelectedEscrow, setLoading, setError, and handleError (and ErrorResponse if it's a variable/function) so the callback is recreated when any of those change; retain the async logic inside fetchEscrow and only remove the eslint-disable comment after adding these dependencies to the useCallback call.
84-89:⚠️ Potential issue | 🟠 MajorRemove the
as anycast and use proper type narrowing for escrow fetch response.The
as anycast on line 88 violates the TypeScript guideline. The codebase pattern (seen inapple-cards-carousel.tsxandProjectList.tsx) is to cast tounknown, then type-narrow with an array check:Suggested fix
- // eslint-disable-next-line `@typescript-eslint/no-explicit-any` - const data = (await getEscrowByContractIds({ + const data = (await getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true, - })) as any; + })) as unknown; if (!data || !data[0]) throw new Error(t("escrowNotFound")); setSelectedEscrow(data[0]);Alternatively, cast to the proper type from the external package:
- const data = (await getEscrowByContractIds({ + const data = (await getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true, - })) as any; - if (!data || !data[0]) throw new Error(t("escrowNotFound")); + })) as unknown; + if (!Array.isArray(data) || !data[0]) throw new Error(t("escrowNotFound")); setSelectedEscrow(data[0]);Per coding guidelines:
**/*.{ts,tsx}: Never useanyin TypeScript; useunknownwith type narrowing instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx` around lines 84 - 89, Remove the `as any` cast on the getEscrowByContractIds result: call getEscrowByContractIds({ contractIds: [escrowId], validateOnChain: true }) and assign it to an unknown-typed variable, then narrow it by checking Array.isArray(data) && data.length > 0 before accessing data[0]; after the array check, you can safely treat data as the proper escrow array (or cast to the external package type, e.g., Escrow[] from the SDK) and keep the existing throw new Error(t("escrowNotFound")) if the check fails. Target the getEscrowByContractIds call and the local data usage in manage-loans-view.tsx and remove the `as any` cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@apps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsx`:
- Around line 79-99: The fetchEscrow useCallback currently has an empty
dependency array causing stale closures; update its dependencies to include all
referenced values: t, getEscrowByContractIds, setSelectedEscrow, setLoading,
setError, and handleError (and ErrorResponse if it's a variable/function) so the
callback is recreated when any of those change; retain the async logic inside
fetchEscrow and only remove the eslint-disable comment after adding these
dependencies to the useCallback call.
- Around line 84-89: Remove the `as any` cast on the getEscrowByContractIds
result: call getEscrowByContractIds({ contractIds: [escrowId], validateOnChain:
true }) and assign it to an unknown-typed variable, then narrow it by checking
Array.isArray(data) && data.length > 0 before accessing data[0]; after the array
check, you can safely treat data as the proper escrow array (or cast to the
external package type, e.g., Escrow[] from the SDK) and keep the existing throw
new Error(t("escrowNotFound")) if the check fails. Target the
getEscrowByContractIds call and the local data usage in manage-loans-view.tsx
and remove the `as any` cast.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ea66605d-e8aa-4b79-9076-2c445461e6a6
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
apps/backoffice-tokenization/src/components/shared/campaign-card.tsxapps/backoffice-tokenization/src/features/campaigns/components/loans/manage-loans-view.tsxapps/backoffice-tokenization/src/features/campaigns/components/roi/roi-table-row.tsxapps/investor-tokenization/src/features/roi/components/campaign-card.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/backoffice-tokenization/src/components/shared/campaign-card.tsx
- apps/investor-tokenization/src/features/roi/components/campaign-card.tsx
- apps/backoffice-tokenization/src/features/campaigns/components/roi/roi-table-row.tsx
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
feat: add i18n support (EN/ES) across backoffice, investor and shared packages
Summary
Next.js apps and a lightweight SharedTranslationProvider for tw-blocks-shared
detection and Spanish as default
Changes
i18n/navigation.ts in both apps
with existing CORS)
tw-blocks-shared/src/i18n/messages/
— no next-intl dependency
vaults, escrows, wallet, invest dialog, claim ROI)
Test plan
language
Summary by CodeRabbit