diff --git a/components/bill/BillTracker.tsx b/components/bill/BillTracker.tsx index b774d6de2..4e6a8a003 100644 --- a/components/bill/BillTracker.tsx +++ b/components/bill/BillTracker.tsx @@ -3,6 +3,7 @@ import { Stage, useBillTracker } from "components/db/useBillStatus" import styled from "styled-components" import { BillProps, BillTracker } from "./types" import { Row } from "react-bootstrap" +import { useTranslation } from "next-i18next" export default function BillTrackerConnectedView({ bill, @@ -31,7 +32,13 @@ export const BillTrackerView = ({ ))} ) - return + return ( + + ) } export const BillStageStrip = ({ diff --git a/components/bill/Cosponsors.tsx b/components/bill/Cosponsors.tsx index 201d8f94c..e28fb958e 100644 --- a/components/bill/Cosponsors.tsx +++ b/components/bill/Cosponsors.tsx @@ -5,6 +5,7 @@ import { MemberReference, useMember } from "../db" import { memberLink } from "../links" import { FC } from "../types" import { BillProps } from "./types" +import { useTranslation } from "next-i18next" const CoSponsorRow = ({ court, @@ -13,9 +14,6 @@ const CoSponsorRow = ({ court: number coSponsor: MemberReference }) => { - const url = coSponsor - ? `https://malegislature.gov/Legislators/Profile/${coSponsor.Id}` - : "" const { member, loading } = useMember(court, coSponsor.Id) if (loading) { return null @@ -66,7 +64,6 @@ export const Cosponsors: FC> = ({ bill, children }) => { - const billNumber = bill.id const court = bill.court const coSponsors = bill.content.Cosponsors const numCoSponsors = coSponsors ? coSponsors.length : 0 @@ -93,7 +90,11 @@ export const Cosponsors: FC> = ({ size="lg" > - {billNumber + " CoSponsors"} + + {useTranslation("common").t("bill.bill_cosponsors", { + billId: bill.id + })} + <> diff --git a/components/bill/HistoryModal.tsx b/components/bill/HistoryModal.tsx index df7b98af7..ae137f938 100644 --- a/components/bill/HistoryModal.tsx +++ b/components/bill/HistoryModal.tsx @@ -3,20 +3,22 @@ import styled from "styled-components" import { Button, Modal } from "../bootstrap" import { HistoryTable } from "./HistoryTable" import { BillProps } from "./types" +import { useTranslation } from "next-i18next" export const HistoryModal = ({ bill }: BillProps) => { const [showBillHistory, setShowBillHistory] = useState(false) const handleShowBillHistory = () => setShowBillHistory(true) const handleCloseBillHistory = () => setShowBillHistory(false) + const { t } = useTranslation("common") return ( <> - Status & History + {t("bill.status_and_history")} {bill.id + " - " + bill.content.Title} diff --git a/components/bill/HistoryTable.tsx b/components/bill/HistoryTable.tsx index fc7ac442a..1959bd69e 100644 --- a/components/bill/HistoryTable.tsx +++ b/components/bill/HistoryTable.tsx @@ -3,6 +3,7 @@ import { useContext } from "react" import styled from "styled-components" import { BillHistory } from "../db" import { CourtContext } from "./Status" +import { useTranslation } from "next-i18next" export type HistoryProps = { billHistory: BillHistory } @@ -50,14 +51,15 @@ const BillHistoryActionRows = ({ billHistory }: HistoryProps) => { } export const HistoryTable = ({ billHistory }: HistoryProps) => { + const { t } = useTranslation("common") return (
- Status History - Branch + {t("bill.status_history")} + {t("bill.branch")} diff --git a/components/bill/LobbyingTable.tsx b/components/bill/LobbyingTable.tsx index 6e2b0be09..71e78f571 100644 --- a/components/bill/LobbyingTable.tsx +++ b/components/bill/LobbyingTable.tsx @@ -1,13 +1,15 @@ +import { useTranslation } from "next-i18next" +import { Table } from "react-bootstrap" import { Card, Container } from "../bootstrap" +import { Card as MapleCard } from "../Card" import { FC } from "../types" import { BillProps } from "./types" -import { Table } from "react-bootstrap" -import { Card as MapleCard } from "../Card" export const LobbyingTable: FC> = ({ bill, className }) => { + const { t } = useTranslation("common") const current = bill.currentCommittee if (!current) return null return ( @@ -16,7 +18,7 @@ export const LobbyingTable: FC> = ({ className={`${className} bg-white`} headerElement={ - Lobbying Parties + {t("bill.lobbying_parties")} } body={ @@ -24,21 +26,21 @@ export const LobbyingTable: FC> = ({ - - - + + + - - - + + + - - - + + +
Client NamePositionDisclosure Date{t("bill.client_name")}{t("bill.position")}{t("bill.disclosure_date")}
Example NameProApril 10, 2023{t("bill.example_name")}{t("bill.pro")}{t("date", { date: new Date("2023-04-15") })}
Example NameNeutralMarch 29, 2023{t("bill.example_name")}{t("bill.neutral")}{t("date", { date: new Date("2023-03-29") })}
diff --git a/components/bill/SponsorsAndCommittees.tsx b/components/bill/SponsorsAndCommittees.tsx index 2bc6a16ca..d65bd2d48 100644 --- a/components/bill/SponsorsAndCommittees.tsx +++ b/components/bill/SponsorsAndCommittees.tsx @@ -1,6 +1,7 @@ -import { format, fromUnixTime } from "date-fns" +import { fromUnixTime } from "date-fns" +import { useTranslation } from "next-i18next" import styled from "styled-components" -import { Card, Container, Row } from "../bootstrap" +import { Card, Container } from "../bootstrap" import { External } from "../links" import { LabeledIcon } from "../shared" import { FC } from "../types" @@ -29,6 +30,7 @@ export const Committees: FC> = ({ className }) => { const current = bill.currentCommittee + const { t } = useTranslation("common") if (!current) return null return ( @@ -36,7 +38,7 @@ export const Committees: FC> = ({ className={className} headerElement={ - Committee + {t("bill.committee")} } body={ @@ -64,13 +66,15 @@ export const Hearing: FC> = ({ bill, className }) => { + const { t } = useTranslation("common") return ( <> {bill.nextHearingAt && dateInFuture(bill.nextHearingAt) ? ( - Hearing Scheduled for{" "} - {format(fromUnixTime(bill.nextHearingAt?.seconds), "MMM d, y p")} + {t("bill.hearing_scheduled_for", { + date: fromUnixTime(bill.nextHearingAt?.seconds) + })} ) : null} @@ -86,7 +90,7 @@ export const Sponsors: FC> = ({ const cosponsors = bill.content.Cosponsors.filter(s => s.Id !== primary?.Id) const more = cosponsors.length > 2 const isMobile = useMediaQuery("(max-width: 768px)") - + const { t } = useTranslation("common") const countShowSponsors = isMobile ? 1 : 2 return ( @@ -95,7 +99,7 @@ export const Sponsors: FC> = ({ className={className} headerElement={ - Sponsors + {t("sponsors")} } body={ @@ -109,7 +113,7 @@ export const Sponsors: FC> = ({ {primary && ( > = ({ > = ({
{more && ( - See {bill.cosponsorCount} Sponsors + {t("bill.seeCosponsors", { count: bill.cosponsorCount })} )}
diff --git a/components/search/bills/BillHit.tsx b/components/search/bills/BillHit.tsx index 3e9c36a8a..e67604b40 100644 --- a/components/search/bills/BillHit.tsx +++ b/components/search/bills/BillHit.tsx @@ -6,7 +6,7 @@ import { } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { maple } from "components/links" -import { format, fromUnixTime } from "date-fns" +import { fromUnixTime } from "date-fns" import { Hit } from "instantsearch.js" import Link from "next/link" import styled from "styled-components" @@ -14,6 +14,7 @@ import { Card, Col } from "../../bootstrap" import { formatBillId } from "../../formatting" import { Timestamp } from "firebase/firestore" import { dateInFuture } from "components/db/events" +import { useTranslation } from "next-i18next" type BillRecord = { number: string @@ -132,6 +133,7 @@ export const DisplayUpcomingHearing = ({ export const BillHit = ({ hit }: { hit: Hit }) => { const url = maple.bill({ id: hit.number, court: hit.court }) const hearingDate = hit.nextHearingAt && hit.nextHearingAt / 1000 // convert to seconds + const { t } = useTranslation("common") return ( @@ -142,7 +144,9 @@ export const BillHit = ({ hit }: { hit: Hit }) => {
{hit.court && ( - Court {hit.court} + + {t("bill.court", { court: hit.court })} + )} {hit.city} @@ -154,16 +158,23 @@ export const BillHit = ({ hit }: { hit: Hit }) => {
- Sponsor: {hit.primarySponsor}{" "} - {hit.cosponsorCount > 0 - ? `and ${hit.cosponsorCount} other${ - hit.cosponsorCount > 1 ? "s" : "" - }` - : ""} + {(() => { + const count = hit.cosponsorCount + if (!hit.primarySponsor) { + return `${t("sponsor")}: ${t("bill.cosponsor_count", { + count + })}` + } + let title = `${t("sponsor")}: ${hit.primarySponsor}` + if (count) { + title += ` ${t("bill.and_others", { count })}` + } + return title + })()} {hit.currentCommittee && - `Committee: ${hit.currentCommittee}`} + `${t("bill.committee")}: ${hit.currentCommittee}`}
@@ -171,7 +182,9 @@ export const BillHit = ({ hit }: { hit: Hit }) => { {hit.nextHearingAt && dateInFuture(hit.nextHearingAt) ? ( - Hearing Scheduled {format(fromUnixTime(hearingDate!), "M/d/y p")} + {t("bill.hearing_scheduled_for", { + date: fromUnixTime(hearingDate!) + })} ) : null} diff --git a/public/locales/en/common.json b/public/locales/en/common.json index eab8fbaf4..0c2c6e0de 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -2,6 +2,26 @@ "about": "About", "back_to_bills": "back to list of bills", "bill": { + "and_others_one": "and {{count}} other", + "and_others_other": "and {{count}} others", + "bill_tracker": "Bill Tracker", + "bill_cosponsors": "{{billId}} Cosponsors", + "cosponsor_count_one": "{{count}} Cosponsor", + "cosponsor_count_other": "{{count}} Cosponsors", + "court": "Court", + "history": "History", + "status_and_history": "Status & History", + "status_history": "Status History", + "branch": "Branch", + "lobbying_parties": "Lobbying Parties", + "client_name": "Client Name", + "position": "Position", + "disclosure_date": "Disclosure Date", + "example_name": "Example Name", + "pro": "Pro", + "neutral": "Neutral", + "committee": "Committee", + "hearing_scheduled_for": "Hearing Scheduled for {{date, datetime(year: 'numeric'; month: 'long'; day: 'numeric'; hour: 'numeric'; minute: 'numeric')}}", "old_session": "this bill is from session {{billCourt}} - not the current session", "read_more": "Read More", "download_pdf": "Download PDF", @@ -29,6 +49,7 @@ "technology_and_communications": "Technology and Communications", "transportation_and_public_works": "Transportation and Public Works" }, + "seeCosponsors": "See {{count}} Cosponsors", "smart_tag": "AI Smart Tag", "smart_summary": "Smart Summary & Tags", "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. To report an inaccuracy or to suggest an improvement, please email admin@mapletestimony.org" @@ -42,7 +63,9 @@ }, "calendar": "Our Calendar", "hideAns": "Hide Answer", + "date": "{{date, datetime(year: 'numeric'; month: 'long'; day: 'numeric')}}", "joinTraining": "and join an upcoming training session!", + "leadSponsor": "Lead Sponsor", "learn": "Learn", "let_your_voice_be_heard": "Let your voice be heard!", "loading": { @@ -124,6 +147,8 @@ "bluesky": "BlueSky", "mastodon": "Mastodon" }, + "sponsor": "Sponsor", + "sponsors": "Sponsors", "table": { "page": "Page {{currentPage}}" },