diff --git a/lib/components/form/form.css b/lib/components/form/form.css index 4a24af022..854b6bfb1 100644 --- a/lib/components/form/form.css +++ b/lib/components/form/form.css @@ -126,6 +126,14 @@ font-size: 14px; line-height: 1.4; margin-top: -1px; + white-space: pre-wrap; +} + +.otp .settings-preview .summary.tall { + line-height: 2.6; +} + +.otp:not(.mobile) .settings-preview .summary { /* Prevent overflow from being multi-line. 36px is edit button width. */ width: calc(100% - 36px); } diff --git a/lib/components/form/settings-preview.js b/lib/components/form/settings-preview.js index 1cefa4d98..ebd0d35a1 100644 --- a/lib/components/form/settings-preview.js +++ b/lib/components/form/settings-preview.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import { Button } from 'react-bootstrap' import { connect } from 'react-redux' +import { mergeMessages } from '../../util/messages' import { isNotDefaultQuery } from '../../util/query' class SettingsPreview extends Component { @@ -22,11 +23,15 @@ class SettingsPreview extends Component { } static defaultProps = { - editButtonText: + editButtonText: , + messages: { + label: 'Transit Options\n& Preferences' + } } render () { - const { config, query, caret, editButtonText } = this.props + const { caret, config, query, editButtonText } = this.props + const messages = mergeMessages(SettingsPreview.defaultProps, this.props) // Show dot indicator if the current query differs from the default query. let showDot = isNotDefaultQuery(query, config) const button = ( @@ -37,10 +42,14 @@ class SettingsPreview extends Component { {showDot &&
}
) - + // Add tall class to account for vertical centering if there is only + // one line in the label (default is 2). + const addClass = messages.label.match(/\n/) ? '' : ' tall' return (
-
Transit Options
& Preferences
+
+ {messages.label} +
{button}
@@ -51,6 +60,7 @@ class SettingsPreview extends Component { const mapStateToProps = (state, ownProps) => { return { config: state.otp.config, + messages: state.otp.config.language.settingsPreview, query: state.otp.currentQuery } } diff --git a/lib/components/narrative/trip-details.js b/lib/components/narrative/trip-details.js index 0b7118d4f..ca7d3057d 100644 --- a/lib/components/narrative/trip-details.js +++ b/lib/components/narrative/trip-details.js @@ -5,11 +5,31 @@ import { VelocityTransitionGroup } from 'velocity-react' import moment from 'moment' import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary' +import { mergeMessages } from '../../util/messages' import { formatTime, getTimeFormat, getLongDateFormat } from '../../util/time' class TripDetails extends Component { + static defaultProps = { + // Note: messages with default null values included here for visibility. + // Overriding with a truthy string value will cause the expandable help + // message to appear in trip details. + messages: { + at: 'at', + caloriesBurned: 'Calories Burned', + // FIXME: Add templated string description. + caloriesBurnedDescription: null, + depart: 'Depart', + departDescription: null, + title: 'Trip Details', + fare: 'Fare', + transitFare: 'Transit Fare', + transitFareDescription: null + } + } + render () { const { itinerary, timeFormat, longDateFormat } = this.props + const messages = mergeMessages(TripDetails.defaultProps, this.props) const date = moment(itinerary.startTime) // process the transit fare @@ -25,7 +45,7 @@ class TripDetails extends Component { fare = ( {transitFare && ( - Transit Fare: {centsToString(transitFare)} + {messages.transitFare}: {centsToString(transitFare)} )} {minTNCFare !== 0 && ( @@ -33,7 +53,8 @@ class TripDetails extends Component { {companies.toLowerCase()} {' '} - Fare: {dollarsToString(minTNCFare)} - {dollarsToString(maxTNCFare)} + {messages.fare}:{' '} + {dollarsToString(minTNCFare)} - {dollarsToString(maxTNCFare)} )} @@ -50,19 +71,26 @@ class TripDetails extends Component { return (
-
Trip Details
+
{messages.title}
} summary={ - Depart {date.format(longDateFormat)} - {this.props.routingType === 'ITINERARY' && at {formatTime(itinerary.startTime, timeOptions)}} + {messages.depart} {date.format(longDateFormat)} + {this.props.routingType === 'ITINERARY' && + + {' '}{messages.at}{' '} + {formatTime(itinerary.startTime, timeOptions)} + + } } /> {fare && ( } summary={fare} /> @@ -70,7 +98,9 @@ class TripDetails extends Component { {caloriesBurned > 0 && ( } - summary={Calories Burned: {Math.round(caloriesBurned)}} + summary={{messages.caloriesBurned}: {Math.round(caloriesBurned)}} + // FIXME: Come up with a way to replace the caloriesBurnedDescription text with + // templated strings. description={ Calories burned is based on {Math.round(walkDuration / 60)} minute(s){' '} @@ -147,6 +177,7 @@ class TripDetail extends Component { const mapStateToProps = (state, ownProps) => { return { + messages: state.otp.config.language.tripDetails, routingType: state.otp.currentQuery.routingType, tnc: state.otp.tnc, timeFormat: getTimeFormat(state.otp.config), diff --git a/lib/util/messages.js b/lib/util/messages.js new file mode 100644 index 000000000..fa1340f68 --- /dev/null +++ b/lib/util/messages.js @@ -0,0 +1,15 @@ +/** + * Takes component's default props and its instance props and returns the + * merged messages props. The returned object will ensure that the default + * messages are substituted for any translation strings that were missing in the + * props. Note: this does not account for messages in nested objects (e.g., + * messages.header.description). + */ +export function mergeMessages (defaultProps, props) { + const defaultMessages = defaultProps.messages || {} + const propsMessages = props.messages || {} + return { + ...defaultMessages, + ...propsMessages + } +}