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
+ }
+}