Add the missing en-US translations for classification labels (#7692)

Our classification labels were rendering their hardcoded English names
because the en-US locale file had no `classification` section at all, so
this adds the missing keys (labels and category names).

Also wires the category names through i18n, since those had no `t()`
call, and adds a test so a new label can't ship without its key.
This commit is contained in:
EthanHealy01
2026-08-27 14:10:56 +00:00
committed by GitHub
parent 897c72e9d9
commit a215c30068
5 changed files with 350 additions and 3 deletions
@@ -3001,6 +3001,293 @@ summary_one = "Ran 1 tool"
summary_other = "Ran {{count}} tools"
unknownTool = "Unknown tool"
[classification.families]
correspondence = "Correspondence"
education = "Education"
engineering = "Engineering"
finance = "Financial"
forms = "Forms"
government = "Government"
health = "Medical"
hr = "HR"
legal = "Legal"
operations = "Operations"
projects = "Projects"
property = "Property"
reports = "Reports"
sales = "Marketing"
travel = "Travel"
[classification.labels]
academic-record = "Academic record"
action-plan = "Action plan"
addendum = "Addendum"
advertisement = "Advertisement"
affidavit = "Affidavit"
agenda = "Agenda"
amendment = "Amendment"
analytics-report = "Analytics report"
announcement = "Announcement"
annual-report = "Annual report"
api-documentation = "API documentation"
application-form = "Application form"
appraisal-report = "Appraisal report"
architecture-document = "Architecture document"
articles-of-incorporation = "Articles of incorporation"
assignment-brief = "Assignment brief"
audit-report = "Audit report"
balance-sheet = "Balance sheet"
bank-statement = "Bank statement"
benefits-summary = "Benefits summary"
bill-of-lading = "Bill of lading"
bill-of-materials = "Bill of materials"
blueprint = "Blueprint"
board-report = "Board report"
board-resolution = "Board resolution"
booking-confirmation = "Booking confirmation"
brochure = "Brochure"
budget = "Budget"
business-plan = "Business plan"
business-proposal = "Business proposal"
bylaws = "Bylaws"
campaign-brief = "Campaign brief"
case-study = "Case study"
cash-flow-statement = "Cash flow statement"
catalog = "Catalog"
cease-and-desist = "Cease and desist"
certificate = "Certificate"
certificate-of-completion = "Certificate of completion"
change-log = "Change log"
checklist = "Checklist"
claim-form = "Claim form"
closing-statement = "Closing statement"
complaint-letter = "Complaint letter"
compliance-document = "Compliance document"
confirmation-letter = "Confirmation letter"
consent-form = "Consent form"
contract = "Contract"
course-syllabus = "Course syllabus"
court-filing = "Court filing"
cover-letter = "Cover letter"
credit-note = "Credit note"
customs-declaration = "Customs declaration"
customs-form = "Customs form"
cv = "CV"
datasheet = "Datasheet"
debit-note = "Debit note"
deed = "Deed"
delivery-note = "Delivery note"
demand-letter = "Demand letter"
design-document = "Design document"
diploma = "Diploma"
discharge-summary = "Discharge summary"
dissertation = "Dissertation"
donation-receipt = "Donation receipt"
dunning-letter = "Dunning letter"
email-thread = "Email thread"
employee-handbook = "Employee handbook"
employment-contract = "Employment contract"
estimate = "Estimate"
event-agenda = "Event agenda"
event-program = "Event program"
eviction-notice = "Eviction notice"
exam-paper = "Exam paper"
expense-report = "Expense report"
expense-summary = "Expense summary"
explanation-of-benefits = "Explanation of benefits"
fact-sheet = "Fact sheet"
faq-document = "FAQ document"
feasibility-study = "Feasibility study"
feedback-form = "Feedback form"
financial-forecast = "Financial forecast"
financial-statement = "Financial statement"
floor-plan = "Floor plan"
flyer = "Flyer"
form = "Form"
franchise-agreement = "Franchise agreement"
freight-document = "Freight document"
gift-certificate = "Gift certificate"
glossary = "Glossary"
government-notice = "Government notice"
grade-report = "Grade report"
grant-agreement = "Grant agreement"
grant-application = "Grant application"
hoa-document = "HOA document"
home-inspection-report = "Home inspection report"
hr-memo = "HR memo"
hr-policy = "HR policy"
immigration-document = "Immigration document"
immunization-record = "Immunization record"
incident-report = "Incident report"
income-statement = "Income statement"
index = "Index"
inspection-report = "Inspection report"
insurance-certificate = "Insurance certificate"
insurance-claim = "Insurance claim"
insurance-policy = "Insurance policy"
intake-form = "Intake form"
inventory-list = "Inventory list"
investment-summary = "Investment summary"
invitation = "Invitation"
invoice = "Invoice"
itinerary = "Itinerary"
job-application = "Job application"
job-description = "Job description"
lab-report = "Lab report"
lease-agreement = "Lease agreement"
leave-request = "Leave request"
legal-brief = "Legal brief"
legal-filing = "Legal filing"
legal-notice = "Legal notice"
legal-opinion = "Legal opinion"
lesson-plan = "Lesson plan"
letter = "Letter"
letter-of-intent = "Letter of intent"
license = "License"
license-agreement = "License agreement"
loan-agreement = "Loan agreement"
loan-document = "Loan document"
maintenance-log = "Maintenance log"
manual = "Manual"
market-research = "Market research"
marketing-plan = "Marketing plan"
media-kit = "Media kit"
medical-invoice = "Medical invoice"
medical-report = "Medical report"
meeting-agenda = "Meeting agenda"
meeting-minutes = "Meeting minutes"
meeting-notes = "Meeting notes"
membership-document = "Membership document"
memo = "Memo"
memorandum-of-understanding = "Memorandum of understanding"
mortgage-document = "Mortgage document"
nda = "NDA"
newsletter = "Newsletter"
non-compete-agreement = "Non-compete agreement"
notice = "Notice"
offer-letter = "Offer letter"
onboarding-document = "Onboarding document"
order-confirmation = "Order confirmation"
order-form = "Order form"
organization-chart = "Organization chart"
packing-slip = "Packing slip"
partnership-agreement = "Partnership agreement"
patent = "Patent"
pathology-report = "Pathology report"
payment-reminder = "Payment reminder"
payroll-document = "Payroll document"
payslip = "Payslip"
performance-review = "Performance review"
permit = "Permit"
petition = "Petition"
pitch-deck = "Pitch deck"
power-of-attorney = "Power of attorney"
prescription = "Prescription"
presentation = "Presentation"
press-release = "Press release"
price-list = "Price list"
pricing-sheet = "Pricing sheet"
privacy-policy = "Privacy policy"
product-sheet = "Product sheet"
proforma-invoice = "Proforma invoice"
progress-report = "Progress report"
project-charter = "Project charter"
project-plan = "Project plan"
promotional-material = "Promotional material"
property-listing = "Property listing"
proposal = "Proposal"
public-notice = "Public notice"
purchase-agreement = "Purchase agreement"
purchase-order = "Purchase order"
quality-report = "Quality report"
quarterly-report = "Quarterly report"
questionnaire = "Questionnaire"
quick-start-guide = "Quick start guide"
quote = "Quote"
radiology-report = "Radiology report"
receipt = "Receipt"
recommendation-letter = "Recommendation letter"
reference-letter = "Reference letter"
referral-letter = "Referral letter"
registration-confirmation = "Registration confirmation"
registration-form = "Registration form"
regulatory-filing = "Regulatory filing"
release-notes = "Release notes"
remittance-advice = "Remittance advice"
rental-agreement = "Rental agreement"
report = "Report"
request-for-proposal = "Request for proposal"
request-for-quotation = "Request for quotation"
requirements-document = "Requirements document"
research-abstract = "Research abstract"
research-paper = "Research paper"
reservation = "Reservation"
resignation-letter = "Resignation letter"
resume = "Resume"
retrospective = "Retrospective"
return-authorization = "Return authorization"
risk-assessment = "Risk assessment"
roadmap = "Roadmap"
safety-data-sheet = "Safety data sheet"
safety-procedure = "Safety procedure"
sales-proposal = "Sales proposal"
sales-report = "Sales report"
schematic = "Schematic"
scope-of-work = "Scope of work"
service-agreement = "Service agreement"
service-report = "Service report"
settlement-agreement = "Settlement agreement"
shareholder-agreement = "Shareholder agreement"
shipping-confirmation = "Shipping confirmation"
specification = "Specification"
sponsorship-agreement = "Sponsorship agreement"
standard-operating-procedure = "Standard operating procedure"
statement-of-account = "Statement of account"
statement-of-work = "Statement of work"
status-report = "Status report"
stock-report = "Stock report"
study-guide = "Study guide"
subpoena = "Subpoena"
subscription-confirmation = "Subscription confirmation"
supply-order = "Supply order"
survey-form = "Survey form"
survey-results = "Survey results"
sustainability-report = "Sustainability report"
table-of-contents = "Table of contents"
tax-form = "Tax form"
tax-return = "Tax return"
tax-statement = "Tax statement"
technical-drawing = "Technical drawing"
technical-specification = "Technical specification"
tenancy-agreement = "Tenancy agreement"
tender-document = "Tender document"
termination-letter = "Termination letter"
terms-and-conditions = "Terms and conditions"
terms-of-service = "Terms of service"
test-plan = "Test plan"
test-report = "Test report"
thesis = "Thesis"
ticket = "Ticket"
timeline = "Timeline"
timesheet = "Timesheet"
title-document = "Title document"
training-material = "Training material"
transcript = "Transcript"
travel-itinerary = "Travel itinerary"
trust-document = "Trust document"
user-guide = "User guide"
utility-bill = "Utility bill"
vendor-agreement = "Vendor agreement"
visa-document = "Visa document"
waiver = "Waiver"
warehouse-receipt = "Warehouse receipt"
warranty-document = "Warranty document"
waybill = "Waybill"
white-paper = "White paper"
will = "Will"
work-instruction = "Work instruction"
work-order = "Work order"
[cloudBadge]
tooltip = "This operation will use your cloud credits"
@@ -61,7 +61,7 @@ export function ClassificationLabelsSection() {
)}
<LocalIcon icon={family.icon} width="1.1rem" />
<span className="classification-category-name">
{family.name}
{t(`classification.families.${family.id}`, family.name)}
</span>
</span>
}
@@ -40,7 +40,9 @@ export function ClassificationCategoryManager({
<span className="category-visibility-icon">
<LocalIcon icon={category.icon} width="1.1rem" />
</span>
<span className="category-visibility-name">{category.name}</span>
<span className="category-visibility-name">
{t(`classification.families.${category.id}`, category.name)}
</span>
{count !== undefined && (
<span className="category-visibility-count">{count}</span>
)}
@@ -56,7 +56,7 @@ export function buildLabelGroups(
if (members.length === 0) continue;
visible.push({
id: `category:${category.id}`,
label: category.name,
label: t(`classification.families.${category.id}`, category.name),
icon: category.icon,
stubs: members,
defaultExpanded: false,
@@ -0,0 +1,58 @@
import fs from "fs";
import path from "path";
import { describe, it, expect } from "vitest";
import { parse } from "smol-toml";
import {
DEFAULT_CLASSIFICATION_LABELS,
LABEL_FAMILIES,
} from "@app/data/classificationLabels";
// Label and family names are rendered through `classification.labels.<id>` /
// `classification.families.<id>`, with the built-in English name as the fallback.
// A fallback renders fine but is never translated (and logs a missingKey), so a
// label added to classificationLabels.json without its en-US key silently ships
// English to every locale. This guards that the en-US vocabulary stays complete.
const EN_US_TRANSLATIONS = path.join(
__dirname,
"../../../public/locales/en-US/translation.toml",
);
function readSection(section: string): Record<string, unknown> {
const parsed = parse(fs.readFileSync(EN_US_TRANSLATIONS, "utf8")) as {
classification?: Record<string, Record<string, unknown>>;
};
return parsed.classification?.[section] ?? {};
}
describe("classification vocabulary (en-US coverage)", () => {
const labels = readSection("labels");
const families = readSection("families");
it("has an en-US key for every label id, matching its built-in name", () => {
for (const label of DEFAULT_CLASSIFICATION_LABELS) {
expect(
labels[label.id],
`en-US missing classification.labels.${label.id}`,
).toBe(label.name);
}
});
it("has an en-US key for every family id, matching its built-in name", () => {
for (const family of LABEL_FAMILIES) {
expect(
families[family.id],
`en-US missing classification.families.${family.id}`,
).toBe(family.name);
}
});
it("has no en-US keys for labels or families that no longer exist", () => {
const labelIds = new Set(DEFAULT_CLASSIFICATION_LABELS.map((l) => l.id));
const familyIds = new Set(LABEL_FAMILIES.map((f) => f.id));
expect(Object.keys(labels).filter((id) => !labelIds.has(id))).toEqual([]);
expect(Object.keys(families).filter((id) => !familyIds.has(id))).toEqual(
[],
);
});
});