Skip to content

Commit

Permalink
fix(l10n): fix date localization in sub management
Browse files Browse the repository at this point in the history
Because:

* Subscription management date is localized to `en` locale, regardless
  of browser language.
* SecurityEvent created date is not being localized.

This commit:

* Pass FluentDate to Fluent localization libraries to correctly
  localize dates.
* Add localization string for SecurityEvent created date.

Closes FXA-7795
  • Loading branch information
StaberindeZA committed Jun 16, 2023
1 parent 79bc2be commit be6538e
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ describe('CancelSubscriptionPanel', () => {
</LocalizationProvider>
);
expect(queryByText('$20.00 fooly')).toBeInTheDocument();
expect(queryByText('quuz 09/13/2019')).toBeInTheDocument();
expect(queryByText('quuz 13/09/2019')).toBeInTheDocument();
expect(queryByText('blee')).toBeInTheDocument();
});

Expand Down Expand Up @@ -311,7 +311,7 @@ describe('CancelSubscriptionPanel', () => {
'$20.00 + $3.00 tax fooly'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $5.00 + $1.23 taxes is due due 09/13/2019'
'Your next bill of $5.00 + $1.23 taxes is due due 13/09/2019'
);
expect(queryByText('blee')).toBeInTheDocument();
});
Expand Down Expand Up @@ -342,7 +342,7 @@ describe('CancelSubscriptionPanel', () => {
'$20.00 + $3.00 tax barly 8 24hrs'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $5.00 + $1.23 taxes is due due 08/14/2019'
'Your next bill of $5.00 + $1.23 taxes is due due 14/08/2019'
);
});

Expand Down Expand Up @@ -371,7 +371,7 @@ describe('CancelSubscriptionPanel', () => {
'$19.50 + $3.00 tax barly 8 24hrs'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $4.50 + $1.23 taxes is due due 08/14/2019'
'Your next bill of $4.50 + $1.23 taxes is due due 14/08/2019'
);
});

Expand All @@ -385,6 +385,7 @@ describe('CancelSubscriptionPanel', () => {
'payment-cancel-btn = blee',
`price-details-tax = { $priceAmount } + { $taxAmount } taxes`,
`sub-next-bill-tax = Your next bill of { $priceAmount } + { $taxAmount } taxes is due due <strong>{ $date }</strong>`,
`sub-next-bill-no-tax = Your next bill of { $priceAmount } prices is due due <strong>{ $date }</strong>`,
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
Expand All @@ -404,7 +405,7 @@ describe('CancelSubscriptionPanel', () => {
'$20.00 daily'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $5.00 is due 09/13/2019'
'Your next bill of $5.00 prices is due due 13/09/2019'
);
expect(queryByText('blee')).toBeInTheDocument();
});
Expand Down Expand Up @@ -437,7 +438,7 @@ describe('CancelSubscriptionPanel', () => {
'$20.00 fooly'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $5.00 prices is due due 09/13/2019'
'Your next bill of $5.00 prices is due due 13/09/2019'
);
expect(queryByText('blee')).toBeInTheDocument();
});
Expand Down Expand Up @@ -467,7 +468,7 @@ describe('CancelSubscriptionPanel', () => {
'$20.00 barly 8 24hrs'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $5.00 prices is due due 08/14/2019'
'Your next bill of $5.00 prices is due due 14/08/2019'
);
});

Expand Down Expand Up @@ -496,7 +497,7 @@ describe('CancelSubscriptionPanel', () => {
'$19.50 barly 8 24hrs'
);
expect(queryByTestId('sub-next-bill')).toHaveTextContent(
'Your next bill of $4.50 prices is due due 08/14/2019'
'Your next bill of $4.50 prices is due due 14/08/2019'
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const getNextBillData = (
const nextBillDate = getLocalizedDateString(subsequentInvoiceDate, true);
const nextBillL10nVarsDefault = {
priceAmount: getLocalizedCurrency(invoiceDisplayTotal, plan.currency),
date: nextBillDate,
date: getLocalizedDate(subsequentInvoiceDate, true),
};
const nextBillL10nVars = showInvoiceTax
? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from 'fxa-shared/subscriptions/type-guards';
import {
getIapSubscriptionManagementUrl,
getLocalizedDate,
getLocalizedDateString,
} from '../../../lib/formats';

Expand Down Expand Up @@ -48,6 +49,7 @@ const GooglePlaySubscriptionIapItem = (
const { auto_renewing, expiry_time_millis } = customerSubscription;

const nextBillDate = getLocalizedDateString(expiry_time_millis / 1000, true);
const nextBillDateL10n = getLocalizedDate(expiry_time_millis / 1000, true);
const nextBill = `Next billed on ${nextBillDate}`;
const expiresOn = `Expires on ${nextBillDate}`;

Expand All @@ -65,17 +67,11 @@ const GooglePlaySubscriptionIapItem = (
<div className="iap-type">Google: In-App purchase</div>
</Localized>
{auto_renewing ? (
<Localized
id="sub-next-bill"
vars={{ date: nextBillDate as string }}
>
<Localized id="sub-next-bill" vars={{ date: nextBillDateL10n }}>
<div>{nextBill}</div>
</Localized>
) : (
<Localized
id="sub-expires-on"
vars={{ date: nextBillDate as string }}
>
<Localized id="sub-expires-on" vars={{ date: nextBillDateL10n }}>
<div>{expiresOn}</div>
</Localized>
)}
Expand Down Expand Up @@ -103,10 +99,11 @@ const AppleSubscriptionIapItem = (
) => {
const { auto_renewing, expiry_time_millis } = customerSubscription;

let nextBill, expiresOn, nextBillDate;
let nextBill, expiresOn, nextBillDate, nextBillDateL10n;
// TODO - Remove expiry_time_millis check pending https://developer.apple.com/forums/thread/705730
if (expiry_time_millis) {
nextBillDate = getLocalizedDateString(expiry_time_millis / 1000, true);
nextBillDateL10n = getLocalizedDate(expiry_time_millis / 1000, true);
nextBill = `Next billed on ${nextBillDate}`;
expiresOn = `Expires on ${nextBillDate}`;
}
Expand All @@ -126,16 +123,13 @@ const AppleSubscriptionIapItem = (
</Localized>
{!!expiry_time_millis &&
(auto_renewing ? (
<Localized
id="sub-next-bill"
vars={{ date: nextBillDate as string }}
>
<Localized id="sub-next-bill" vars={{ date: nextBillDateL10n }}>
<div>{nextBill}</div>
</Localized>
) : (
<Localized
id="sub-expires-on"
vars={{ date: nextBillDate as string }}
vars={{ date: nextBillDateL10n }}
>
<div>{expiresOn}</div>
</Localized>
Expand Down
51 changes: 46 additions & 5 deletions packages/fxa-react/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { FluentBundle, FluentVariable } from '@fluent/bundle';
import { FluentBundle, FluentDateTime, FluentVariable } from '@fluent/bundle';
import { Message, Pattern } from '@fluent/bundle/esm/ast';
import { Localized, LocalizedProps, ReactLocalization } from '@fluent/react';
import React from 'react';

export type FtlMsgProps = {
children: React.ReactNode;
} & LocalizedProps;

// Going from react page to non-react page requires a hard navigate. This temporary
// function is an easy way to reference what needs updating when applicable flows have
// been fully converted - we should remove references to this function as we go and use
Expand All @@ -19,6 +15,51 @@ export function hardNavigateToContentServer(href: string) {
window.location.href = href;
}

export enum LocalizedDateOptions {
NumericDate,
NumericDateAndTime,
}

/**
* This method is used to provide Fluent with a localizable value that can be formatted per .ftl file based on localization requirements
*
* @param milliseconds
* @param numericDate
*/
export const getLocalizedDate = (
milliseconds: number,
dateOptions: LocalizedDateOptions
): FluentDateTime => {
let options: Intl.DateTimeFormatOptions | undefined;

switch (dateOptions) {
case LocalizedDateOptions.NumericDate:
options = {
day: 'numeric',
month: 'numeric',
year: 'numeric',
};
break;
case LocalizedDateOptions.NumericDateAndTime:
options = {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
};
break;
default:
options = undefined;
}

return new FluentDateTime(milliseconds, options);
};

export type FtlMsgProps = {
children: React.ReactNode;
} & LocalizedProps;

export const FtlMsg = (props: FtlMsgProps) => (
<Localized {...props}>{props.children}</Localized>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import React from 'react';
import { FtlMsg } from 'fxa-react/lib/utils';
import {
FtlMsg,
LocalizedDateOptions,
getLocalizedDate,
} from 'fxa-react/lib/utils';

enum SecurityEventName {
Create = 'account.create',
Expand Down Expand Up @@ -77,12 +81,22 @@ export function SecurityEvent({
minute: 'numeric',
}).format(new Date(createdAt));

const createdAtDateFluent = getLocalizedDate(
createdAt,
LocalizedDateOptions.NumericDateAndTime
);

const l10nName = getSecurityEventNameL10n(name);
return (
<li className="mt-5 ml-4" data-testid={l10nName.ftlId}>
<div className="absolute w-3 h-3 bg-green-600 rounded-full mt-1.5 -left-1.5 border border-green-700"></div>
<div className="text-grey-900 text-sm mobileLandscape:mt-3">
{createdAtDateText}
<FtlMsg
id="recent-activity-created-at"
vars={{ date: createdAtDateFluent }}
>
{createdAtDateText}
</FtlMsg>
</div>
<FtlMsg id={l10nName.ftlId}>
<p className="text-grey-400 text-xs mobileLandscape:mt-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ recent-activity-account-login = Account initiated login
recent-activity-account-reset = Account initiated password reset
recent-activity-emails-clearBounces = Account cleared email bounces
## $date (Date) - Date recent activity was created
recent-activity-created-at = { $date }
13 changes: 11 additions & 2 deletions packages/fxa-settings/src/components/Settings/Security/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import UnitRowRecoveryKey from '../UnitRowRecoveryKey';
import UnitRowTwoStepAuth from '../UnitRowTwoStepAuth';
import { UnitRow } from '../UnitRow';
import { useAccount } from '../../../models';
import { FtlMsg } from 'fxa-react/lib/utils';
import {
FtlMsg,
getLocalizedDate,
LocalizedDateOptions,
} from 'fxa-react/lib/utils';
import { Link } from '@reach/router';

const PwdDate = ({ passwordCreated }: { passwordCreated: number }) => {
Expand All @@ -18,8 +22,13 @@ const PwdDate = ({ passwordCreated }: { passwordCreated: number }) => {
day: 'numeric',
}).format(new Date(passwordCreated));

const pwdDateFluent = getLocalizedDate(
passwordCreated,
LocalizedDateOptions.NumericDate
);

return (
<FtlMsg id="security-password-created-date" vars={{ date: pwdDateText }}>
<FtlMsg id="security-password-created-date" vars={{ date: pwdDateFluent }}>
<p className="text-grey-400 text-xs mobileLandscape:mt-3">
Created {pwdDateText}
</p>
Expand Down

0 comments on commit be6538e

Please sign in to comment.