Skip to content

Commit 8a51f70

Browse files
authored
extract text to translation ready files -- Bill Detail Page (#1860)
* extract text to translation ready files -- Bill Detail Page * create components/i18n.ts to prevent SSR build errors * fix useTranslation-related build error * prettier formatting * adjust date formats * update components/search/bills/BillHit.tsx date formatting * update lead sponsor terminology in Sponsors component and add translation key * add translation keys for court and sponsor details in BillHit component * reorder translation keys * address bug in billHit sponsor d isplay when there is only one co-sponsor * use built-in i18next date formatting utils in lieu of wrapper
1 parent 2e68244 commit 8a51f70

File tree

8 files changed

+98
-42
lines changed

8 files changed

+98
-42
lines changed

components/bill/BillTracker.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Stage, useBillTracker } from "components/db/useBillStatus"
33
import styled from "styled-components"
44
import { BillProps, BillTracker } from "./types"
55
import { Row } from "react-bootstrap"
6+
import { useTranslation } from "next-i18next"
67

78
export default function BillTrackerConnectedView({
89
bill,
@@ -31,7 +32,13 @@ export const BillTrackerView = ({
3132
))}
3233
</Row>
3334
)
34-
return <MapleCard className={className} header="Bill Tracker" body={body} />
35+
return (
36+
<MapleCard
37+
className={className}
38+
header={useTranslation("common").t("bill.bill_tracker")}
39+
body={body}
40+
/>
41+
)
3542
}
3643

3744
export const BillStageStrip = ({

components/bill/Cosponsors.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { MemberReference, useMember } from "../db"
55
import { memberLink } from "../links"
66
import { FC } from "../types"
77
import { BillProps } from "./types"
8+
import { useTranslation } from "next-i18next"
89

910
const CoSponsorRow = ({
1011
court,
@@ -13,9 +14,6 @@ const CoSponsorRow = ({
1314
court: number
1415
coSponsor: MemberReference
1516
}) => {
16-
const url = coSponsor
17-
? `https://malegislature.gov/Legislators/Profile/${coSponsor.Id}`
18-
: ""
1917
const { member, loading } = useMember(court, coSponsor.Id)
2018
if (loading) {
2119
return null
@@ -66,7 +64,6 @@ export const Cosponsors: FC<React.PropsWithChildren<BillProps>> = ({
6664
bill,
6765
children
6866
}) => {
69-
const billNumber = bill.id
7067
const court = bill.court
7168
const coSponsors = bill.content.Cosponsors
7269
const numCoSponsors = coSponsors ? coSponsors.length : 0
@@ -93,7 +90,11 @@ export const Cosponsors: FC<React.PropsWithChildren<BillProps>> = ({
9390
size="lg"
9491
>
9592
<Modal.Header closeButton onClick={handleCloseBillCosponsors}>
96-
<Modal.Title>{billNumber + " CoSponsors"}</Modal.Title>
93+
<Modal.Title>
94+
{useTranslation("common").t("bill.bill_cosponsors", {
95+
billId: bill.id
96+
})}
97+
</Modal.Title>
9798
</Modal.Header>
9899
<Modal.Body>
99100
<>

components/bill/HistoryModal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ import styled from "styled-components"
33
import { Button, Modal } from "../bootstrap"
44
import { HistoryTable } from "./HistoryTable"
55
import { BillProps } from "./types"
6+
import { useTranslation } from "next-i18next"
67

78
export const HistoryModal = ({ bill }: BillProps) => {
89
const [showBillHistory, setShowBillHistory] = useState(false)
910
const handleShowBillHistory = () => setShowBillHistory(true)
1011
const handleCloseBillHistory = () => setShowBillHistory(false)
12+
const { t } = useTranslation("common")
1113

1214
return (
1315
<>
1416
<Button variant="primary" className="m-1" onClick={handleShowBillHistory}>
15-
History
17+
{t("bill.history")}
1618
</Button>
1719
<Modal show={showBillHistory} onHide={handleCloseBillHistory} size="lg">
1820
<Modal.Header closeButton onClick={handleCloseBillHistory}>
19-
<StyledModalTitle>Status & History</StyledModalTitle>
21+
<StyledModalTitle>{t("bill.status_and_history")}</StyledModalTitle>
2022
</Modal.Header>
2123
<StyledBillTitle>
2224
{bill.id + " - " + bill.content.Title}

components/bill/HistoryTable.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useContext } from "react"
33
import styled from "styled-components"
44
import { BillHistory } from "../db"
55
import { CourtContext } from "./Status"
6+
import { useTranslation } from "next-i18next"
67

78
export type HistoryProps = { billHistory: BillHistory }
89

@@ -50,14 +51,15 @@ const BillHistoryActionRows = ({ billHistory }: HistoryProps) => {
5051
}
5152

5253
export const HistoryTable = ({ billHistory }: HistoryProps) => {
54+
const { t } = useTranslation("common")
5355
return (
5456
<div className="text-center">
5557
<StyledTable>
5658
<thead>
5759
<tr>
5860
<th></th>
59-
<th>Status History</th>
60-
<th>Branch</th>
61+
<th>{t("bill.status_history")}</th>
62+
<th>{t("bill.branch")}</th>
6163
</tr>
6264
</thead>
6365
<tbody>

components/bill/LobbyingTable.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import { useTranslation } from "next-i18next"
2+
import { Table } from "react-bootstrap"
13
import { Card, Container } from "../bootstrap"
4+
import { Card as MapleCard } from "../Card"
25
import { FC } from "../types"
36
import { BillProps } from "./types"
4-
import { Table } from "react-bootstrap"
5-
import { Card as MapleCard } from "../Card"
67

78
export const LobbyingTable: FC<React.PropsWithChildren<BillProps>> = ({
89
bill,
910
className
1011
}) => {
12+
const { t } = useTranslation("common")
1113
const current = bill.currentCommittee
1214
if (!current) return null
1315
return (
@@ -16,29 +18,29 @@ export const LobbyingTable: FC<React.PropsWithChildren<BillProps>> = ({
1618
className={`${className} bg-white`}
1719
headerElement={
1820
<Card.Header className="h4 bg-secondary text-light">
19-
Lobbying Parties
21+
{t("bill.lobbying_parties")}
2022
</Card.Header>
2123
}
2224
body={
2325
<Card.Body>
2426
<Table>
2527
<thead>
2628
<tr>
27-
<th>Client Name</th>
28-
<th>Position</th>
29-
<th>Disclosure Date</th>
29+
<th>{t("bill.client_name")}</th>
30+
<th>{t("bill.position")}</th>
31+
<th>{t("bill.disclosure_date")}</th>
3032
</tr>
3133
</thead>
3234
<tbody>
3335
<tr>
34-
<td>Example Name</td>
35-
<td>Pro</td>
36-
<td>April 10, 2023</td>
36+
<td>{t("bill.example_name")}</td>
37+
<td>{t("bill.pro")}</td>
38+
<td>{t("date", { date: new Date("2023-04-15") })}</td>
3739
</tr>
3840
<tr>
39-
<td>Example Name</td>
40-
<td>Neutral</td>
41-
<td>March 29, 2023</td>
41+
<td>{t("bill.example_name")}</td>
42+
<td>{t("bill.neutral")}</td>
43+
<td>{t("date", { date: new Date("2023-03-29") })}</td>
4244
</tr>
4345
</tbody>
4446
</Table>

components/bill/SponsorsAndCommittees.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { format, fromUnixTime } from "date-fns"
1+
import { fromUnixTime } from "date-fns"
2+
import { useTranslation } from "next-i18next"
23
import styled from "styled-components"
3-
import { Card, Container, Row } from "../bootstrap"
4+
import { Card, Container } from "../bootstrap"
45
import { External } from "../links"
56
import { LabeledIcon } from "../shared"
67
import { FC } from "../types"
@@ -29,14 +30,15 @@ export const Committees: FC<React.PropsWithChildren<BillProps>> = ({
2930
className
3031
}) => {
3132
const current = bill.currentCommittee
33+
const { t } = useTranslation("common")
3234
if (!current) return null
3335
return (
3436
<Container className={`${className} p-0`}>
3537
<MapleCard
3638
className={className}
3739
headerElement={
3840
<Card.Header className="h4 bg-secondary text-light">
39-
Committee
41+
{t("bill.committee")}
4042
</Card.Header>
4143
}
4244
body={
@@ -64,13 +66,15 @@ export const Hearing: FC<React.PropsWithChildren<BillProps>> = ({
6466
bill,
6567
className
6668
}) => {
69+
const { t } = useTranslation("common")
6770
return (
6871
<>
6972
{bill.nextHearingAt && dateInFuture(bill.nextHearingAt) ? (
7073
<LabeledContainer className={className}>
7174
<HearingDate>
72-
Hearing Scheduled for{" "}
73-
{format(fromUnixTime(bill.nextHearingAt?.seconds), "MMM d, y p")}
75+
{t("bill.hearing_scheduled_for", {
76+
date: fromUnixTime(bill.nextHearingAt?.seconds)
77+
})}
7478
</HearingDate>
7579
</LabeledContainer>
7680
) : null}
@@ -86,7 +90,7 @@ export const Sponsors: FC<React.PropsWithChildren<BillProps>> = ({
8690
const cosponsors = bill.content.Cosponsors.filter(s => s.Id !== primary?.Id)
8791
const more = cosponsors.length > 2
8892
const isMobile = useMediaQuery("(max-width: 768px)")
89-
93+
const { t } = useTranslation("common")
9094
const countShowSponsors = isMobile ? 1 : 2
9195

9296
return (
@@ -95,7 +99,7 @@ export const Sponsors: FC<React.PropsWithChildren<BillProps>> = ({
9599
className={className}
96100
headerElement={
97101
<Card.Header className="h4 bg-secondary text-light">
98-
Sponsors
102+
{t("sponsors")}
99103
</Card.Header>
100104
}
101105
body={
@@ -109,7 +113,7 @@ export const Sponsors: FC<React.PropsWithChildren<BillProps>> = ({
109113
{primary && (
110114
<LabeledIcon
111115
idImage={`https://malegislature.gov/Legislators/Profile/170/${primary.Id}.jpg`}
112-
mainText="Lead Sponsor"
116+
mainText={t("leadSponsor")}
113117
subText={
114118
<External
115119
href={`https://malegislature.gov/Legislators/Profile/${primary.Id}`}
@@ -126,7 +130,7 @@ export const Sponsors: FC<React.PropsWithChildren<BillProps>> = ({
126130
<LabeledIcon
127131
key={s.Id}
128132
idImage={`https://malegislature.gov/Legislators/Profile/170/${s.Id}.jpg`}
129-
mainText="Sponsor"
133+
mainText={t("sponsor")}
130134
subText={
131135
<External
132136
href={`https://malegislature.gov/Legislators/Profile/${s.Id}`}
@@ -140,7 +144,7 @@ export const Sponsors: FC<React.PropsWithChildren<BillProps>> = ({
140144
<div className="d-flex justify-content-center">
141145
{more && (
142146
<Cosponsors bill={bill}>
143-
See {bill.cosponsorCount} Sponsors
147+
{t("bill.seeCosponsors", { count: bill.cosponsorCount })}
144148
</Cosponsors>
145149
)}
146150
</div>

components/search/bills/BillHit.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import {
66
} from "@fortawesome/free-solid-svg-icons"
77
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
88
import { maple } from "components/links"
9-
import { format, fromUnixTime } from "date-fns"
9+
import { fromUnixTime } from "date-fns"
1010
import { Hit } from "instantsearch.js"
1111
import Link from "next/link"
1212
import styled from "styled-components"
1313
import { Card, Col } from "../../bootstrap"
1414
import { formatBillId } from "../../formatting"
1515
import { Timestamp } from "firebase/firestore"
1616
import { dateInFuture } from "components/db/events"
17+
import { useTranslation } from "next-i18next"
1718

1819
type BillRecord = {
1920
number: string
@@ -132,6 +133,7 @@ export const DisplayUpcomingHearing = ({
132133
export const BillHit = ({ hit }: { hit: Hit<BillRecord> }) => {
133134
const url = maple.bill({ id: hit.number, court: hit.court })
134135
const hearingDate = hit.nextHearingAt && hit.nextHearingAt / 1000 // convert to seconds
136+
const { t } = useTranslation("common")
135137

136138
return (
137139
<Link href={url} legacyBehavior>
@@ -142,7 +144,9 @@ export const BillHit = ({ hit }: { hit: Hit<BillRecord> }) => {
142144
<Col className="left">
143145
<div className="d-flex justify-content-between">
144146
{hit.court && (
145-
<span className="blurb me-2">Court {hit.court}</span>
147+
<span className="blurb me-2">
148+
{t("bill.court", { court: hit.court })}
149+
</span>
146150
)}
147151
<span className="blurb">{hit.city}</span>
148152
<span style={{ flex: "1" }} />
@@ -154,24 +158,33 @@ export const BillHit = ({ hit }: { hit: Hit<BillRecord> }) => {
154158
</Card.Title>
155159
<div className="d-flex justify-content-between flex-column">
156160
<span className="blurb">
157-
Sponsor: {hit.primarySponsor}{" "}
158-
{hit.cosponsorCount > 0
159-
? `and ${hit.cosponsorCount} other${
160-
hit.cosponsorCount > 1 ? "s" : ""
161-
}`
162-
: ""}
161+
{(() => {
162+
const count = hit.cosponsorCount
163+
if (!hit.primarySponsor) {
164+
return `${t("sponsor")}: ${t("bill.cosponsor_count", {
165+
count
166+
})}`
167+
}
168+
let title = `${t("sponsor")}: ${hit.primarySponsor}`
169+
if (count) {
170+
title += ` ${t("bill.and_others", { count })}`
171+
}
172+
return title
173+
})()}
163174
</span>
164175
<span className="blurb">
165176
{hit.currentCommittee &&
166-
`Committee: ${hit.currentCommittee}`}
177+
`${t("bill.committee")}: ${hit.currentCommittee}`}
167178
</span>
168179
</div>
169180
</Col>
170181
</div>
171182
</Card.Body>
172183
{hit.nextHearingAt && dateInFuture(hit.nextHearingAt) ? (
173184
<Card.Footer className="card-footer">
174-
Hearing Scheduled {format(fromUnixTime(hearingDate!), "M/d/y p")}
185+
{t("bill.hearing_scheduled_for", {
186+
date: fromUnixTime(hearingDate!)
187+
})}
175188
</Card.Footer>
176189
) : null}
177190
</StyledCard>

public/locales/en/common.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22
"about": "About",
33
"back_to_bills": "back to list of bills",
44
"bill": {
5+
"and_others_one": "and {{count}} other",
6+
"and_others_other": "and {{count}} others",
7+
"bill_tracker": "Bill Tracker",
8+
"bill_cosponsors": "{{billId}} Cosponsors",
9+
"cosponsor_count_one": "{{count}} Cosponsor",
10+
"cosponsor_count_other": "{{count}} Cosponsors",
11+
"court": "Court",
12+
"history": "History",
13+
"status_and_history": "Status & History",
14+
"status_history": "Status History",
15+
"branch": "Branch",
16+
"lobbying_parties": "Lobbying Parties",
17+
"client_name": "Client Name",
18+
"position": "Position",
19+
"disclosure_date": "Disclosure Date",
20+
"example_name": "Example Name",
21+
"pro": "Pro",
22+
"neutral": "Neutral",
23+
"committee": "Committee",
24+
"hearing_scheduled_for": "Hearing Scheduled for {{date, datetime(year: 'numeric'; month: 'long'; day: 'numeric'; hour: 'numeric'; minute: 'numeric')}}",
525
"old_session": "this bill is from session {{billCourt}} - not the current session",
626
"read_more": "Read More",
727
"download_pdf": "Download PDF",
@@ -29,6 +49,7 @@
2949
"technology_and_communications": "Technology and Communications",
3050
"transportation_and_public_works": "Transportation and Public Works"
3151
},
52+
"seeCosponsors": "See {{count}} Cosponsors",
3253
"smart_tag": "AI Smart Tag",
3354
"smart_summary": "Smart Summary & Tags",
3455
"smart_disclaimer": "This content has been generated using artificial intelligence and may not accurately reflect the details of the legislation. Learn more about <0>how MAPLE uses AI</0>. To report an inaccuracy or to suggest an improvement, please email [email protected]"
@@ -42,7 +63,9 @@
4263
},
4364
"calendar": "Our Calendar",
4465
"hideAns": "Hide Answer",
66+
"date": "{{date, datetime(year: 'numeric'; month: 'long'; day: 'numeric')}}",
4567
"joinTraining": "and join an upcoming training session!",
68+
"leadSponsor": "Lead Sponsor",
4669
"learn": "Learn",
4770
"let_your_voice_be_heard": "Let your voice be heard!",
4871
"loading": {
@@ -115,6 +138,8 @@
115138
"bluesky": "BlueSky",
116139
"mastodon": "Mastodon"
117140
},
141+
"sponsor": "Sponsor",
142+
"sponsors": "Sponsors",
118143
"table": {
119144
"page": "Page {{currentPage}}"
120145
},

0 commit comments

Comments
 (0)