From bc6a0b31f0fe62edb700d2589f5487e9c5aa5335 Mon Sep 17 00:00:00 2001 From: Phil Cline Date: Wed, 21 Jul 2021 09:58:34 -0700 Subject: [PATCH 01/64] improvement(active-trip-renderer): Localize component --- i18n/en-US.yml | 10 +++++-- .../active-trip-renderer.js | 29 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index c27abfd18..65b84313a 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -26,13 +26,18 @@ _name: English # Component-specific messages (e.g. button captions) # are defined for each component under the 'components' category. components: + ActiveTripRenderer: + bodyText: "Trip is due to arrive at the destination at {arrivalTime}." + delayedHeading: "Trip is in progress and is delayed {deviationHumanDuration}!" + earlyHeading: "Trip is in progress and is arriving {deviationHumanDuration} earlier than expected!" + noDataHeading: Trip is in progress (no realtime updates available). + onTimeHeading: Trip is in progress and is about on time. DefaultItinerary: clickDetails: Click to view details # Use ordered placeholders when multiple modes are involved # (this will accommodate right-to-left languages by swapping the order/separator in this string). multiModeSummary: "{accessMode} to {transitMode}" ItinerarySummary: - calories: Cal #{minTotalFare} - {maxTotalFare} //other { {minTotalFare}} fareCost: "{useMaxFare, select, true {{minTotalFare} - {maxTotalFare}} other {{minTotalFare}} @@ -123,7 +128,8 @@ common: micromobility: E-Scooter micromobilityRent: Rental E-Scooter walk: Walk - + + # Shared itinerary description messages itineraryDescriptions: calories: Cal transfers: "{transfers, plural, =0 {} one {{transfers} Transfer} other {{transfers} Transfers}}" diff --git a/lib/components/user/monitored-trip/trip-status-rendering-strategies/active-trip-renderer.js b/lib/components/user/monitored-trip/trip-status-rendering-strategies/active-trip-renderer.js index 3dda64721..e69cc4afd 100644 --- a/lib/components/user/monitored-trip/trip-status-rendering-strategies/active-trip-renderer.js +++ b/lib/components/user/monitored-trip/trip-status-rendering-strategies/active-trip-renderer.js @@ -1,9 +1,11 @@ import moment from 'moment' +import { useIntl } from 'react-intl' import { formatDuration } from '@opentripplanner/core-utils/lib/time' -import baseRenderer from './base-renderer' import { getTripStatus, REALTIME_STATUS } from '../../../../util/viewer' +import baseRenderer from './base-renderer' + /** * Calculates various data for monitored trips that are currently active. */ @@ -12,6 +14,7 @@ export default function activeTripRenderer ({ onTimeThresholdSeconds, timeFormat }) { + const intl = useIntl() const data = baseRenderer(monitoredTrip) const tripEndMoment = moment(data.matchingItinerary.endTime) @@ -38,23 +41,35 @@ export default function activeTripRenderer ({ if (tripStatus === REALTIME_STATUS.ON_TIME) { // about on time data.panelBsStyle = 'success' - data.headingText = 'Trip is in progress and is about on time.' + data.headingText = intl.formatMessage({ + id: 'components.ActiveTripRenderer.onTimeHeading' + }) } else if (tripStatus === REALTIME_STATUS.LATE) { // delayed data.panelBsStyle = 'warning' - data.headingText = `Trip is in progress and is delayed ${deviationHumanDuration}!` + data.headingText = intl.formatMessage({ + id: 'components.ActiveTripRenderer.delayedHeading', + values: {deviationHumanDuration} + }) } else { // early data.panelBsStyle = 'warning' - data.headingText = `Trip is in progress and is arriving ${deviationHumanDuration} earlier than expected!` + data.headingText = intl.formatMessage({ + id: 'components.ActiveTripRenderer.earlyHeading', + values: {deviationHumanDuration} + }) } } else { data.panelBsStyle = 'info' - data.headingText = 'Trip is in progress (no realtime updates available).' + data.headingText = intl.formatMessage({ + id: 'components.ActiveTripRenderer.noDataHeading' + }) } - data.bodyText = - `Trip is due to arrive at the destination at ${tripEndMoment.format(timeFormat)}.` + data.bodyText = intl.formatMessage({ + id: 'components.ActiveTripRenderer.bodyText', + values: {arrivalTime: tripEndMoment.format(timeFormat)} + }) data.shouldRenderTogglePauseTripButton = true data.shouldRenderToggleSnoozeTripButton = true From b66a0fde3a7ebfd05ea4e2dde7a85f3affb3ccf2 Mon Sep 17 00:00:00 2001 From: Phil Cline Date: Wed, 21 Jul 2021 10:47:20 -0700 Subject: [PATCH 02/64] improvement(base-renderer): Localize Base Renderer --- i18n/en-US.yml | 9 ++++++ .../base-renderer.js | 31 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index 2378d77f0..186abf4ba 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -32,6 +32,15 @@ components: earlyHeading: "Trip is in progress and is arriving {deviationHumanDuration} earlier than expected!" noDataHeading: Trip is in progress (no realtime updates available). onTimeHeading: Trip is in progress and is about on time. + BaseRenderer: + bodyDefault: Unknown Trip State + lastCheckedDefaultText: Last checked time unknown + LastCheckedText: "Last checked: {formattedDuration} ago" + toggleSnoozeTripButtonText: "{tripIsSnoozed, select, + true {Unsnooze trip analysis} + other {Snooze for rest of today} + }" + DefaultItinerary: clickDetails: Click to view details # Use ordered placeholders when multiple modes are involved diff --git a/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js b/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js index 99963cbe1..faccd39a1 100644 --- a/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js +++ b/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js @@ -1,17 +1,20 @@ import moment from 'moment' -import { formatDuration } from '@opentripplanner/core-utils/lib/time' +import { useIntl } from 'react-intl' + +import { FormattedDuration } from '../../../default/format-duration' /** * Calculate commonly-used pieces of data used to render the trip status * component. The monitoredTrip param can be undefined. */ export default function baseRenderer (monitoredTrip) { + const intl = useIntl() const data = { // create some default display values in case another renderer doesn't // calculate these values - body: 'Unknown trip state', - headingText: 'Unknown trip state', - lastCheckedText: 'Last checked time unknown', + body: intl.formatMessage({id: 'components.BaseRenderer.bodyDefault'}), + headingText: intl.formatMessage({id: 'components.BaseRenderer.bodyDefault'}), // same default msg as body + lastCheckedText: intl.formatMessage({id: 'components.BaseRenderer.lastCheckedDefaultText'}), monitoredTrip: monitoredTrip, journeyState: monitoredTrip && monitoredTrip.journeyState, tripIsActive: monitoredTrip && monitoredTrip.isActive, @@ -26,8 +29,14 @@ export default function baseRenderer (monitoredTrip) { moment(data.journeyState.lastCheckedEpochMillis), 'seconds' ) - data.lastCheckedText = - `Last checked: ${formatDuration(secondsSinceLastCheck)} ago` + data.lastCheckedText = intl.formatMessage({ + id: 'components.BaseRenderer.lastCheckedText', + values: { + formattedDuration: + } + }) } // set some alert data if the matching itinerary exists @@ -51,10 +60,16 @@ export default function baseRenderer (monitoredTrip) { if (data.tripIsSnoozed) { data.toggleSnoozeTripButtonGlyphIcon = 'play' - data.toggleSnoozeTripButtonText = 'Unsnooze trip analysis' + data.toggleSnoozeTripButtonText = intl.formatMessage({ + id: 'components.BaseRenderer.toggleSnoozeTripButtonText', + values: {tripIsSnoozed: data.tripIsSnoozed ? 'true' : 'false'} + }) } else { data.toggleSnoozeTripButtonGlyphIcon = 'pause' - data.toggleSnoozeTripButtonText = 'Snooze for rest of today' + data.toggleSnoozeTripButtonText = intl.formatMessage({ + id: 'components.BaseRenderer.toggleSnoozeTripButtonText', + values: {tripIsSnoozed: data.tripIsSnoozed ? 'true' : 'false'} + }) } return data From 34de0167297bdea7c9bee703a9c7ea569392163f Mon Sep 17 00:00:00 2001 From: Phil Cline Date: Wed, 21 Jul 2021 11:54:01 -0700 Subject: [PATCH 03/64] style(narrative-itinerary-header): order keys properly --- lib/components/narrative/narrative-itineraries-header.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/components/narrative/narrative-itineraries-header.js b/lib/components/narrative/narrative-itineraries-header.js index b95853270..23075da0d 100644 --- a/lib/components/narrative/narrative-itineraries-header.js +++ b/lib/components/narrative/narrative-itineraries-header.js @@ -62,9 +62,9 @@ export default function NarrativeItinerariesHeader ({ title={intl.formatMessage({ id: 'components.NarrativeItinerariesHeader.titleText', values: { - pending: pending ? 'true' : 'false', + issueNum: errors.length, itineraryNum: itineraries.length, - issueNum: errors.length + pending: pending ? 'true' : 'false' } })} > @@ -72,8 +72,8 @@ export default function NarrativeItinerariesHeader ({ From 32becd7393dcf61832bb40c5a7e246f76461954f Mon Sep 17 00:00:00 2001 From: Phil Cline Date: Wed, 21 Jul 2021 12:02:55 -0700 Subject: [PATCH 04/64] style(linting): --- .../narrative/default/format-time.js | 6 ++--- .../narrative/line-itin/itin-summary.js | 27 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/components/narrative/default/format-time.js b/lib/components/narrative/default/format-time.js index dd61ca0d9..91606a85b 100644 --- a/lib/components/narrative/default/format-time.js +++ b/lib/components/narrative/default/format-time.js @@ -1,13 +1,13 @@ import moment from 'moment-timezone' import { FormattedMessage } from 'react-intl' -export function FormattedTime ({startTime, endTime, timeFormat}) { +export function FormattedTime ({endTime, startTime, timeFormat}) { return ( ) diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index bd1d0d7db..3a27a02cc 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -87,7 +87,7 @@ export class ItinerarySummary extends Component { } render () { - const { itinerary, use24HourFormat, currency } = this.props + const { currency, itinerary, use24HourFormat } = this.props const { LegIcon } = this.context const timeFormat = use24HourFormat ? 'H:mm' : 'h:mm a' @@ -124,23 +124,23 @@ export class ItinerarySummary extends Component { ), - maxTotalFare: ( + minTotalFare: ( - ) + ), + useMaxFare: minTotalFare !== maxTotalFare ? 'true' : 'false' }} /> @@ -209,8 +209,9 @@ function getRouteColorForBadge (leg) { const mapStateToProps = (state, ownProps) => { return { - use24HourFormat: state.user.loggedInUser?.use24HourFormat ?? false, - currency: state.otp.config.localization?.currency || 'USD' + currency: state.otp.config.localization?.currency || 'USD', + use24HourFormat: state.user.loggedInUser?.use24HourFormat ?? false } } + export default connect(mapStateToProps)(ItinerarySummary) From e5732d9eeb99fd605834646f6a5efa2aba642ca9 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:03:25 -0400 Subject: [PATCH 05/64] refactor(lint): Fix lint errors --- .../narrative/default/format-time.js | 6 ++--- .../narrative/line-itin/itin-summary.js | 26 +++++++++---------- .../base-renderer.js | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/components/narrative/default/format-time.js b/lib/components/narrative/default/format-time.js index dd61ca0d9..91606a85b 100644 --- a/lib/components/narrative/default/format-time.js +++ b/lib/components/narrative/default/format-time.js @@ -1,13 +1,13 @@ import moment from 'moment-timezone' import { FormattedMessage } from 'react-intl' -export function FormattedTime ({startTime, endTime, timeFormat}) { +export function FormattedTime ({endTime, startTime, timeFormat}) { return ( ) diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index bd1d0d7db..26f037dc1 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -87,7 +87,7 @@ export class ItinerarySummary extends Component { } render () { - const { itinerary, use24HourFormat, currency } = this.props + const { currency, itinerary, use24HourFormat } = this.props const { LegIcon } = this.context const timeFormat = use24HourFormat ? 'H:mm' : 'h:mm a' @@ -124,23 +124,23 @@ export class ItinerarySummary extends Component { ), - maxTotalFare: ( + minTotalFare: ( - ) + ), + useMaxFare: minTotalFare !== maxTotalFare ? 'true' : 'false' }} /> @@ -209,8 +209,8 @@ function getRouteColorForBadge (leg) { const mapStateToProps = (state, ownProps) => { return { - use24HourFormat: state.user.loggedInUser?.use24HourFormat ?? false, - currency: state.otp.config.localization?.currency || 'USD' + currency: state.otp.config.localization?.currency || 'USD', + use24HourFormat: state.user.loggedInUser?.use24HourFormat ?? false } } export default connect(mapStateToProps)(ItinerarySummary) diff --git a/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js b/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js index b7e4ce12c..77dd36e60 100644 --- a/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js +++ b/lib/components/user/monitored-trip/trip-status-rendering-strategies/base-renderer.js @@ -1,7 +1,7 @@ import moment from 'moment' import { useIntl } from 'react-intl' -import { FormattedDuration } from '../../../default/format-duration' +import { FormattedDuration } from '../../../narrative/default/format-duration' /** * Calculate commonly-used pieces of data used to render the trip status From 60a16bcf7c72f4a788a1dd258168775c7b8b1220 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 30 Jul 2021 17:45:15 -0400 Subject: [PATCH 06/64] refactor(components/user): Localize more user components. --- i18n/en-US.yml | 23 ++++++++++++++++ i18n/fr-FR.yml | 23 +++++++++++++++- lib/components/user/sub-nav.js | 20 ++++++++++---- lib/components/user/terms-of-use-pane.js | 31 ++++++++++++++-------- lib/components/user/user-account-screen.js | 9 ++++--- lib/components/user/verify-email-pane.js | 12 ++++----- 6 files changed, 92 insertions(+), 26 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index 186abf4ba..cddfd4dfa 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -105,12 +105,26 @@ components: signInTooltip: Please sign in to save trip. SimpleRealtimeAnnotation: usingRealtimeInfo: This trip uses real-time traffic and delay information + SubNav: + myAccount: My account + settings: Settings + trips: Trips TabbedItineraries: optionNumber: "Option {optionNum,number}" fareCost: "{useMaxFare, select, true {{minTotalFare}+} other {{minTotalFare}} }" + TermsOfUsePane: + mustAgreeToTerms: You must agree to the terms of service to continue. + # For termsOfServiceStatement and termsOfStorageStatement, + # use the special ... markup to insert the + # corresponding link in the text. + termsOfServiceStatement: "I confirm that I am at least 18 years old, and I have read and + consent to the Terms of service for using the Trip Planner." + termsOfStorageStatement: "Optional: I consent to the Trip Planner storing my historical planned trips in order to + improve transit services in my area. More info..." + TripTools: # Note to translator: copyLink, linkCopied, print, reportIssue, # and startOver are width-constrained. @@ -125,6 +139,15 @@ components: Please add any additional feedback for this trip under the 'Additional Comments' section below and send using your regular email program." startOver: Start Over # TODO: move to other category (common with hamburger 'Start Over' item) + UserAccountScreen: + confirmDelete: Are you sure you would like to delete your user account? Once you do so, it cannot be recovered. + VerifyEmailPane: + emailIsVerified: My email is verified! + instructions1: "Please check your email inbox and follow the link in the message + to verify your email address before finishing your account setup." + instructions2: Once you're verified, click the button below to continue. + resendVerification: Resend verification email + # Common messages that appear in multiple components and modules # are grouped below by topic. diff --git a/i18n/fr-FR.yml b/i18n/fr-FR.yml index 9e7d1d50e..5645faca6 100644 --- a/i18n/fr-FR.yml +++ b/i18n/fr-FR.yml @@ -64,12 +64,25 @@ components: signInTooltip: Veuillez vous connecter pour enregistrer ce trajet. SimpleRealtimeAnnotation: usingRealtimeInfo: Ce trajet utilise les informations en temps réel sur le trafic et les retards + SubNav: + myAccount: Mon compte + settings: Préférences + trips: Trajets TabbedItineraries: optionNumber: "Option {optionNum,number}" fareCost: "{useMaxFare, select, true {À partir de {minTotalFare}} other {{minTotalFare}} }" + TermsOfUsePane: + mustAgreeToTerms: Vous devez accepter les conditions d'utilisation avant de continuer. + # For termsOfServiceStatement and termsOfStorageStatement, + # use the special ... markup to insert the + # corresponding link in the text. + termsOfServiceStatement: "J'atteste avoir au moins 18 ans et j'ai lu et consens aux + Conditions de service pour utiliser the Plannificateur de trajets." + termsOfStorageStatement: "Facultatif: Je consens à ce que le Plannificateur de trajets sauvegarde mes recherches effectuées + afin d'améliorer les transports publics dans ma region. Plus d'informations..." TripTools: # Note to translator: copyLink, linkCopied, print, reportIssue, # and startOver are width-constrained. @@ -79,11 +92,19 @@ components: print: Imprimer reportIssue: Un problème ? # "Signaler un problème" does not fit. reportEmailSubject: Signaler un problème avec OpenTripPlanner - reportEmailTemplate: " *** A L'ATTENTION DE L'UTILISATEUR *** + reportEmailTemplate: " *** À L'ATTENTION DE L'UTILISATEUR *** Vous pouvez communiquer votre problème en détail aux administrateurs de ce site, par courriel. Veuillez ajouter toute remarque sur cet itinéraire dans la section 'Additional Comments' ci-dessous, puis envoyez depuis votre logiciel de messagerie usuel." startOver: Recommencer + UserAccountScreen: + confirmDelete: "Êtes-vous sûr·e de vouloir supprimer votre compte ? Cette action est irréversible." + VerifyEmailPane: + emailIsVerified: Mon email est vérifié ! + instructions1: "Vous devriez recevoir un message par courriel. Cliquez le lien dans le message + pour verifier votre adresse email. Vous pourrez ensuite finir de créer votre compte." + instructions2: Une fois votre adresse est vérifiée, cliquez le bouton ci-dessous pour continuer. + resendVerification: Envoyer de nouveau le message de vérification common: accessModes: diff --git a/lib/components/user/sub-nav.js b/lib/components/user/sub-nav.js index 8d17c03a0..7eee08ac2 100644 --- a/lib/components/user/sub-nav.js +++ b/lib/components/user/sub-nav.js @@ -1,23 +1,33 @@ import React from 'react' import { Button } from 'react-bootstrap' +import { FormattedMessage } from 'react-intl' import { LinkContainerWithQuery } from '../form/connected-links' -import { SubNavContainer, SubNavLinks } from './styled' import { ACCOUNT_SETTINGS_PATH, TRIPS_PATH } from '../../util/constants' +import { SubNavContainer, SubNavLinks } from './styled' + /** * This component renders the sub navigation elements for Account pages. */ -const SubNav = ({title = 'My account'}) => ( +const SubNav = ({ title }) => (
-

{title}

+

+ {title || ( + + )} +

- + - +
diff --git a/lib/components/user/terms-of-use-pane.js b/lib/components/user/terms-of-use-pane.js index e7c0e94fd..1aa76692f 100644 --- a/lib/components/user/terms-of-use-pane.js +++ b/lib/components/user/terms-of-use-pane.js @@ -1,5 +1,6 @@ import React from 'react' import { Checkbox, ControlLabel, FormGroup } from 'react-bootstrap' +import { FormattedMessage } from 'react-intl' import { TERMS_OF_SERVICE_PATH, TERMS_OF_STORAGE_PATH } from '../../util/constants' @@ -19,8 +20,9 @@ const TermsOfUsePane = ({ return (
- You must agree to the terms of service to continue. - + + + - I confirm that I am at least 18 years old, and I have read and{' '} - consent to the{' '} - - Terms of Service - for using the Trip Planner. + ( + {chunks} + ) + }} + /> - - {/* TODO: Implement the link */} - Optional: I consent to the Trip Planner storing my historical planned trips in order to - improve transit services in my area. More info... + ( + {chunks} + ) + }} + />
diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index 3d7f16abf..d6ea186c6 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -1,6 +1,7 @@ import { withAuthenticationRequired } from '@auth0/auth0-react' import { Form, Formik } from 'formik' import React, { Component } from 'react' +import { injectIntl } from 'react-intl' import { connect } from 'react-redux' import * as yup from 'yup' @@ -15,6 +16,8 @@ import NewAccountWizard from './new-account-wizard' import withLoggedInUserSupport from './with-logged-in-user-support' // The validation schema for the form fields. +// FIXME: validationSchema is not really directly used, so the text below is never shown. +// Also, this may be removed depending on fate of the Save button on this screen. const validationSchema = yup.object({ email: yup.string().email(), hasConsentedToTerms: yup.boolean().oneOf([true], 'You must agree to the terms to continue.'), @@ -52,11 +55,11 @@ class UserAccountScreen extends Component { } _handleDeleteUser = (evt) => { - const {auth0, deleteUser, loggedInUser} = this.props + const {auth0, deleteUser, intl, loggedInUser} = this.props // Avoid triggering onsubmit with formik (which would result in a save user // call). evt.preventDefault() - if (confirm('Are you sure you would like to delete your user account? Once you do so, it cannot be recovered.')) { + if (confirm(intl.formatMessage({id: 'components.UserAccountScreen.confirmDelete'}))) { deleteUser(loggedInUser, auth0) } } @@ -159,7 +162,7 @@ const mapDispatchToProps = { export default withLoggedInUserSupport( withAuthenticationRequired( - connect(mapStateToProps, mapDispatchToProps)(UserAccountScreen), + connect(mapStateToProps, mapDispatchToProps)(injectIntl(UserAccountScreen)), RETURN_TO_CURRENT_ROUTE ), true diff --git a/lib/components/user/verify-email-pane.js b/lib/components/user/verify-email-pane.js index a66b8f011..8b907a056 100644 --- a/lib/components/user/verify-email-pane.js +++ b/lib/components/user/verify-email-pane.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import { FormattedMessage, injectIntl } from 'react-intl' import { connect } from 'react-redux' import styled from 'styled-components' @@ -36,11 +37,10 @@ class VerifyEmailPane extends Component { return (

- Please check your email inbox and follow the link in the message - to verify your email address before finishing your account setup. +

- Once you're verified, click the button below to continue. +

@@ -58,7 +58,7 @@ class VerifyEmailPane extends Component { onClick={resendVerificationEmail} style={{padding: '0px'}} > - Resend verification email +
@@ -77,4 +77,4 @@ const mapDispatchToProps = { routeTo: uiActions.routeTo } -export default connect(mapStateToProps, mapDispatchToProps)(VerifyEmailPane) +export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(VerifyEmailPane)) From c6c131f2f6aeaf2563f51b367fe805f58394aba6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 2 Aug 2021 12:03:39 -0400 Subject: [PATCH 07/64] refactor(i18n,NotificationPrefsPane): Localize components --- i18n/en-US.yml | 32 ++++++ i18n/fr-FR.yml | 31 +++++ .../user/form-navigation-buttons.js | 4 +- .../user/notification-prefs-pane.js | 106 +++++++++++------- .../user/sequential-pane-display.js | 6 +- lib/components/user/stacked-pane-display.js | 5 +- 6 files changed, 136 insertions(+), 48 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index cddfd4dfa..9d0a604bd 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -82,6 +82,31 @@ components: } }" viewAll: View all options + NotificationPrefsPane: + description: You can receive notifications about trips you frequently take. + notificationChannelPrompt: How would you like to receive notifications? + notificationEmailDetail: "Notification emails will be sent to:" + emailSelect: Email + noneSelect: Don't notify me + smsSelect: SMS + PhoneNumberEditor: + cancel: Cancel + changeNumber: Change number + invalidCode: Please enter 6 digits for the validation code. + invalidPhone: Please enter a valid phone number. + pending: Pending + # Note to translator: placeholder is width-constrained. + placeholder: "Enter your phone number" + prompt: "Enter your phone number for SMS notifications:" + requestNewCode: Request a new code + sendVerificationText: Send verification text + smsDetail: "SMS notifications will be sent to:" + verified: Verified + verificationCode: "Verification code:" + verificationInstructions: "Please check the SMS messaging app on your mobile phone + for a text message with a verification code, and enter the code below + (code expires after 10 minutes)." + verify: Verify PlanFirstLastButtons: # Note to translator: these values are width-constrained. first: First @@ -103,8 +128,15 @@ components: saveTripText: Save trip signInText: Sign in to save trip signInTooltip: Please sign in to save trip. + SequentialPaneDisplay: + back: Back + finish: Finish + next: Next SimpleRealtimeAnnotation: usingRealtimeInfo: This trip uses real-time traffic and delay information + StackedPaneDisplay: + cancel: Cancel + save: Save preferences SubNav: myAccount: My account settings: Settings diff --git a/i18n/fr-FR.yml b/i18n/fr-FR.yml index 5645faca6..127a0af50 100644 --- a/i18n/fr-FR.yml +++ b/i18n/fr-FR.yml @@ -41,6 +41,30 @@ components: } }" viewAll: Voir toutes les options + NotificationPrefsPane: + description: Vous pouvez recevoir des notifications sur les trajets que vous effectuez fréquemment. + notificationChannelPrompt: Comment voulez-vous recevoir vos notifications ? + notificationEmailDetail: "Les courriers de notification seront envoyés à :" + emailSelect: Courriel + noneSelect: Ne pas me notifier + smsSelect: SMS + PhoneNumberEditor: + cancel: Annuler + changeNumber: Changer de numéro + invalidCode: Le code de vérification doit comporter 6 chiffres. + invalidPhone: Veuillez entrer un numéro de téléphone valable. + pending: Non vérifié + # Note to translator: placeholder is width-constrained. + placeholder: "Entrez votre numéro" + prompt: "Entrez votre numéro de téléphone pour les SMS de notification :" + requestNewCode: Envoyer un nouveau code + sendVerificationText: Envoyer le SMS de vérification + smsDetail: "Les SMS de notification seront envoyés au :" + verified: Verifié + verificationCode: "Code de vérification :" + verificationInstructions: "Un SMS vous a été envoyé avec un code de vérification. + Veuillez taper ce code ci-dessous (le code expire après 10 minutes)." + verify: Verifier PlanFirstLastButtons: # Note to translator: these values are width-constrained. first: Premier @@ -62,8 +86,15 @@ components: saveTripText: Enregistrer signInText: Connectez-vous pour enregistrer signInTooltip: Veuillez vous connecter pour enregistrer ce trajet. + SequentialPaneDisplay: + back: Retour + finish: Terminer + next: Suivant SimpleRealtimeAnnotation: usingRealtimeInfo: Ce trajet utilise les informations en temps réel sur le trafic et les retards + StackedPaneDisplay: + cancel: Annuler + save: Enregistrer mes préferences SubNav: myAccount: Mon compte settings: Préférences diff --git a/lib/components/user/form-navigation-buttons.js b/lib/components/user/form-navigation-buttons.js index f20cf9b5c..5bac1ee86 100644 --- a/lib/components/user/form-navigation-buttons.js +++ b/lib/components/user/form-navigation-buttons.js @@ -51,8 +51,8 @@ const buttonType = PropTypes.shape({ disabled: PropTypes.bool, /** Triggered when the button is clicked. */ onClick: PropTypes.func, - /** The text to display on the button. */ - text: PropTypes.string, + /** The text to display on the button (JSX elements accepted). */ + text: PropTypes.element, /** The HTML type of the button ('button', 'reset', 'submit'). */ type: PropTypes.string }) diff --git a/lib/components/user/notification-prefs-pane.js b/lib/components/user/notification-prefs-pane.js index 70af59bb4..78695d830 100644 --- a/lib/components/user/notification-prefs-pane.js +++ b/lib/components/user/notification-prefs-pane.js @@ -11,6 +11,7 @@ import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap' +import { FormattedMessage, injectIntl } from 'react-intl' import { formatPhoneNumber, isPossiblePhoneNumber } from 'react-phone-number-input' import Input from 'react-phone-number-input/input' import styled, { css } from 'styled-components' @@ -18,20 +19,7 @@ import * as yup from 'yup' import { getErrorStates, isBlank } from '../../util/ui' -const allowedNotificationChannels = [ - { - text: 'Email', - type: 'email' - }, - { - text: 'SMS', - type: 'sms' - }, - { - text: 'Don\'t notify me', - type: 'none' - } -] +const allowedNotificationChannels = ['email', 'sms', 'none'] // Styles // HACK: Preserve container height. @@ -64,12 +52,13 @@ const FlushLink = styled(Button)` padding-right: 0; ` -const INVALID_PHONE_MSG = 'Please enter a valid phone number.' -const VALIDATION_CODE_MSG = 'Please enter 6 digits for the validation code.' +// Because we show the same message for the two validation conditions below, +// there is no need to pass that message here, +// that is done in the corresponding `` in PhoneNumberEditor. const codeValidationSchema = yup.object({ validationCode: yup.string() - .required(VALIDATION_CODE_MSG) - .matches(/^\d{6}$/, VALIDATION_CODE_MSG) // 6-digit string + .required() + .matches(/^\d{6}$/) // 6-digit string }) /** @@ -101,17 +90,19 @@ const NotificationPrefsPane = ({ return (

- You can receive notifications about trips you frequently take. +

- How would you like to receive notifications? + + + - {allowedNotificationChannels.map(({ text, type }, index) => ( + {allowedNotificationChannels.map((type, index) => ( // TODO: If removing the Save/Cancel buttons on the account screen, // persist changes immediately when onChange is triggered. - {text} + ))} @@ -132,7 +123,9 @@ const NotificationPrefsPane = ({
{notificationChannel === 'email' && ( - Notification emails will be sent to: + + + {email} )} @@ -147,7 +140,7 @@ const NotificationPrefsPane = ({ // (The validation for this component is independent of the validation set in UserAccountScreen.) innerProps => { return ( - a
and remove onKeyDown handler. - Enter your phone number for SMS notifications: + + + @@ -321,28 +317,48 @@ class PhoneNumberEditor extends Component { disabled={isPhoneInvalid} onClick={this._handleRequestCode} > - Send verification text + { // Show cancel button only if a phone number is already recorded. - initialPhoneNumber && } - {showPhoneError && {INVALID_PHONE_MSG}} + initialPhoneNumber && ( + + )} + {showPhoneError && ( + + + + )} ) : ( - SMS notifications will be sent to: + + + {formatPhoneNumber(initialPhoneNumber)} {' '} {isPending - // eslint-disable-next-line jsx-a11y/label-has-for - ? - // eslint-disable-next-line jsx-a11y/label-has-for - : + ? ( + // eslint-disable-next-line jsx-a11y/label-has-for + + ) + : ( + // eslint-disable-next-line jsx-a11y/label-has-for + + ) } - + )} @@ -352,11 +368,11 @@ class PhoneNumberEditor extends Component { // make this a and remove onKeyDown handler.

- Please check the SMS messaging app on your mobile phone - for a text message with a verification code, and enter the code below - (code expires after 10 minutes). +

- Verification code: + + + - Verify + {touched.validationCode && errors.validationCode && ( - {errors.validationCode} + + + )} - Request a new code + + +
)} ) } } + +const PhoneNumberEditorWithIntl = injectIntl(PhoneNumberEditor) diff --git a/lib/components/user/sequential-pane-display.js b/lib/components/user/sequential-pane-display.js index 4229ffc6a..df8d6d59d 100644 --- a/lib/components/user/sequential-pane-display.js +++ b/lib/components/user/sequential-pane-display.js @@ -1,8 +1,10 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' +import { FormattedMessage } from 'react-intl' import { connect } from 'react-redux' import * as uiActions from '../../actions/ui' + import FormNavigationButtons from './form-navigation-buttons' import { SequentialPaneContainer } from './styled' @@ -61,12 +63,12 @@ class SequentialPaneDisplay extends Component { }} okayButton={{ disabled: disableNext, onClick: this._handleToNextPane, - text: nextId ? 'Next' : 'Finish', + text: , type: 'submit' }} /> diff --git a/lib/components/user/stacked-pane-display.js b/lib/components/user/stacked-pane-display.js index 8db7380b9..5b42b6410 100644 --- a/lib/components/user/stacked-pane-display.js +++ b/lib/components/user/stacked-pane-display.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types' import React, {useState} from 'react' +import { FormattedMessage } from 'react-intl' import FormNavigationButtons from './form-navigation-buttons' import { PageHeading, StackedPaneContainer } from './styled' @@ -29,10 +30,10 @@ const StackedPaneDisplay = ({ onCancel, paneSequence, title }) => { updateBeingCanceled(true) onCancel() }, - text: 'Cancel' + text: }} okayButton={{ - text: 'Save Preferences', + text: , type: 'submit' }} /> From f135b161d6c7bab1a5d7c00b1230705c01abfc45 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 2 Aug 2021 15:28:32 -0400 Subject: [PATCH 08/64] refactor(i18n, user): Localize more user settings components. --- i18n/en-US.yml | 17 ++++++++++ i18n/fr-FR.yml | 17 ++++++++++ .../user/account-setup-finish-pane.js | 5 ++- lib/components/user/delete-user.js | 5 +-- .../user/existing-account-display.js | 9 +++--- .../user/form-navigation-buttons.js | 6 ++-- lib/components/user/new-account-wizard.js | 31 +++++++++---------- 7 files changed, 64 insertions(+), 26 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index 9d0a604bd..597a484ed 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -26,6 +26,8 @@ _name: English # Component-specific messages (e.g. button captions) # are defined for each component under the 'components' category. components: + AccountSetupFinishPane: + message: You are ready to start planning your trips. ActiveTripRenderer: bodyText: "Trip is due to arrive at the destination at {arrivalTime}." delayedHeading: "Trip is in progress and is delayed {deviationHumanDuration}!" @@ -46,6 +48,15 @@ components: # Use ordered placeholders when multiple modes are involved # (this will accommodate right-to-left languages by swapping the order/separator in this string). multiModeSummary: "{accessMode} to {transitMode}" + DeleteUser: + deleteMyAccount: Delete my account + ExistingAccountDisplay: + mainTitle: My settings + notifications: Notifications + places: Favorite places + terms: Terms + FormNavigationButtons: + ariaLabel: Form navigation ItinerarySummary: fareCost: "{useMaxFare, select, true {{minTotalFare} - {maxTotalFare}} @@ -82,6 +93,12 @@ components: } }" viewAll: View all options + NewAccountWizard: + finish: Account setup complete! + notifications: Notification preferences + places: Add your locations + terms: Create a new account + verify: Verify your email address NotificationPrefsPane: description: You can receive notifications about trips you frequently take. notificationChannelPrompt: How would you like to receive notifications? diff --git a/i18n/fr-FR.yml b/i18n/fr-FR.yml index 127a0af50..7e3e1cd18 100644 --- a/i18n/fr-FR.yml +++ b/i18n/fr-FR.yml @@ -2,9 +2,20 @@ _id: fr-FR _name: Unofficial French Translations! components: + AccountSetupFinishPane: + message: Vous pouvez maintenant commencer à plannifer vos trajets. DefaultItinerary: clickDetails: Cliquez pour afficher les détails multiModeSummary: "{accessMode} + {transitMode}" + DeleteUser: + deleteMyAccount: Supprimer mon compte + ExistingAccountDisplay: + mainTitle: Mes préférences + notifications: Notifications + places: Destinations favorites + terms: Conditions d'utilisation + FormNavigationButtons: + ariaLabel: Navigation du formulaire ItinerarySummary: fareCost: "{useMaxFare, select, true {{minTotalFare} - {maxTotalFare}} @@ -41,6 +52,12 @@ components: } }" viewAll: Voir toutes les options + NewAccountWizard: + finish: Votre nouveau compte est prêt ! + notifications: Recevez vos notifications + places: Ajoutez vos destinations + terms: Créez votre nouveau compte + verify: Vérifiez votre adresse de courriel NotificationPrefsPane: description: Vous pouvez recevoir des notifications sur les trajets que vous effectuez fréquemment. notificationChannelPrompt: Comment voulez-vous recevoir vos notifications ? diff --git a/lib/components/user/account-setup-finish-pane.js b/lib/components/user/account-setup-finish-pane.js index 60f4b71cf..468b8fa0f 100644 --- a/lib/components/user/account-setup-finish-pane.js +++ b/lib/components/user/account-setup-finish-pane.js @@ -1,8 +1,11 @@ import React from 'react' +import { FormattedMessage } from 'react-intl' const AccountSetupFinishPane = () => (
-

You are ready to start planning your trips.

+

+ +

) diff --git a/lib/components/user/delete-user.js b/lib/components/user/delete-user.js index 7aaaec235..e6d2c5851 100644 --- a/lib/components/user/delete-user.js +++ b/lib/components/user/delete-user.js @@ -1,5 +1,6 @@ import React from 'react' import { Button } from 'react-bootstrap' +import { FormattedMessage } from 'react-intl' import styled from 'styled-components' const DeleteButton = styled(Button)` @@ -14,9 +15,9 @@ const DeleteButton = styled(Button)` /** * Renders a delete user button for the account settings page. */ -const DeleteUser = ({onDelete}) => ( +const DeleteUser = ({ onDelete }) => ( - Delete my account + ) diff --git a/lib/components/user/existing-account-display.js b/lib/components/user/existing-account-display.js index 78ceabe8a..9d291136b 100644 --- a/lib/components/user/existing-account-display.js +++ b/lib/components/user/existing-account-display.js @@ -1,4 +1,5 @@ import React from 'react' +import { FormattedMessage } from 'react-intl' import BackToTripPlanner from './back-to-trip-planner' import DeleteUser from './delete-user' @@ -20,17 +21,17 @@ const ExistingAccountDisplay = props => { { pane: FavoritePlacesList, props, - title: 'My locations' + title: }, { pane: NotificationPrefsPane, props, - title: 'Notifications' + title: }, { pane: TermsOfUsePane, props: { ...props, disableCheckTerms: true }, - title: 'Terms' + title: }, { pane: DeleteUser, @@ -43,7 +44,7 @@ const ExistingAccountDisplay = props => { } />
) diff --git a/lib/components/user/form-navigation-buttons.js b/lib/components/user/form-navigation-buttons.js index 5bac1ee86..45e0ffe5b 100644 --- a/lib/components/user/form-navigation-buttons.js +++ b/lib/components/user/form-navigation-buttons.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types' import React from 'react' import { Button, FormGroup } from 'react-bootstrap' +import { injectIntl } from 'react-intl' import styled from 'styled-components' // Styles @@ -20,10 +21,11 @@ const RightButton = styled(Button)` */ const FormNavigationButtons = ({ backButton, + intl, okayButton }) => ( -