Skip to content

Commit 9c50f9b

Browse files
authored
fix(rewards): localize campaign date (#29808)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <img width="1179" height="2556" alt="Simulator Screenshot - E2E Test - 2026-05-06 at 11 48 33" src="https://github.com/user-attachments/assets/bfdba79e-6614-403c-a1f0-6d228d8465a1" /> <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: swaps a hardcoded month-name formatter for cached `Intl.DateTimeFormat` using the app locale, plus a small Jest mock adjustment; behavior changes are limited to date label rendering. > > **Overview** > Campaign tile date labels are now **localized** by formatting month/day via `getIntlDateTimeFormatter` using `I18n.locale`, replacing the prior hardcoded English month list. > > Tests were updated to mock the `i18n` module’s default export (`locale`) so the new locale-aware formatter can run under Jest. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 38c82e4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 43a99ef commit 9c50f9b

2 files changed

Lines changed: 12 additions & 23 deletions

File tree

app/components/UI/Rewards/components/Campaigns/CampaignTile.utils.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jest.mock('@metamask/design-system-react-native', () => ({
2323
}));
2424

2525
jest.mock('../../../../../../locales/i18n', () => ({
26+
__esModule: true,
27+
default: { locale: 'en-US' },
2628
strings: jest.fn((key: string, params?: Record<string, string>) =>
2729
params ? `${key}:${JSON.stringify(params)}` : key,
2830
),

app/components/UI/Rewards/components/Campaigns/CampaignTile.utils.ts

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
type CampaignDto,
55
type CampaignStatus,
66
} from '../../../../../core/Engine/controllers/rewards-controller/types';
7-
import { strings } from '../../../../../../locales/i18n';
7+
import I18n, { strings } from '../../../../../../locales/i18n';
8+
import { getIntlDateTimeFormatter } from '../../../../../util/intl';
89

910
/**
1011
* Set of campaign types that have full UI support (details view, opt-in, etc.)
@@ -53,32 +54,18 @@ export function getCampaignStatus(campaign: CampaignDto): CampaignStatus {
5354
return 'complete';
5455
}
5556

56-
const MONTHS = [
57-
'January',
58-
'February',
59-
'March',
60-
'April',
61-
'May',
62-
'June',
63-
'July',
64-
'August',
65-
'September',
66-
'October',
67-
'November',
68-
'December',
69-
];
70-
7157
/**
72-
* Formats a date for display in campaign tiles.
58+
* Formats a date for display in campaign tiles (localized month and day).
7359
*
7460
* @param date - The date to format
75-
* @returns Formatted date string (e.g., "March 15")
61+
* @param locale - BCP 47 locale; defaults to the app locale
62+
* @returns Formatted date string (e.g., "March 15" in en-US)
7663
*/
77-
function formatCampaignDate(date: Date): string {
78-
const month = MONTHS[date.getMonth()];
79-
const day = date.getDate();
80-
81-
return `${month} ${day}`;
64+
function formatCampaignDate(date: Date, locale: string = I18n.locale): string {
65+
return getIntlDateTimeFormatter(locale, {
66+
month: 'long',
67+
day: 'numeric',
68+
}).format(date);
8269
}
8370

8471
/**

0 commit comments

Comments
 (0)