From bd512a9bd4adc46ff17a21e94b174393c28aa39f Mon Sep 17 00:00:00 2001 From: Collin Beczak Date: Tue, 21 Jul 2026 16:40:51 -0500 Subject: [PATCH 1/3] add translations --- src/components/AppLayout/BetaBanner.tsx | 19 +- .../AppLayout/DismissibleBanner.test.tsx | 15 +- .../AppLayout/DismissibleBanner.tsx | 4 +- .../AppLayout/Header/DesktopNav.tsx | 13 +- .../Header/DropdownMenuNotifications.tsx | 62 +- .../AppLayout/Header/DropdownMenuUser.tsx | 31 +- .../GlobalSearchDropdown/FindById/index.tsx | 95 +- .../FindChallenge/index.tsx | 50 +- .../FindComments/index.tsx | 91 +- .../FindFeatureByName/index.tsx | 113 +- .../FindProject/index.tsx | 50 +- .../GlobalSearchDropdown/FindTask/index.tsx | 86 +- .../UnifiedSearchList.tsx | 43 +- .../AppLayout/Header/GlobalSearch/index.tsx | 15 +- .../Header/GlobalSearch/shared/searchTypes.ts | 81 +- src/components/AppLayout/Header/MobileNav.tsx | 13 +- src/components/AppLayout/Header/index.tsx | 8 +- src/components/AppLayout/index.tsx | 59 +- .../FeatureStyleLegend/FeatureStyleLegend.tsx | 5 +- .../CustomLayers/CustomLayerForm.tsx | 48 +- .../CustomLayers/CustomLayerRow.tsx | 6 +- .../CustomLayers/CustomLayersPanel.tsx | 11 +- .../Map/LayerControl/LayerControl.tsx | 4 +- src/components/Map/MapControls.tsx | 22 +- src/components/Map/ScaleBar/ScaleBar.tsx | 30 +- .../Map/StatusLegend/StatusLegend.tsx | 9 +- .../Map/TaskMarkers/ClusterToggle.tsx | 28 +- .../BrowseChallengeMap.tsx | 8 +- .../ChallengePanel/ChallengeActionButtons.tsx | 44 +- .../ChallengePanel/ChallengeFooter.tsx | 34 +- .../ChallengePanel/ChallengeHeader.tsx | 146 +- .../ChallengeModals/ActionsModal.tsx | 95 +- .../ChallengeModals/ChallengeComments.tsx | 86 +- .../ChallengeModals/CloneChallengeModal.tsx | 99 +- .../ChallengeModals/CommentsModal.tsx | 10 +- .../ChallengeModals/OverpassModal.tsx | 10 +- .../ChallengeModals/ReportModal.tsx | 140 +- .../ChallengePanel/ChallengeProgress.tsx | 8 +- .../ChallengePanel/ScrollIndicator.tsx | 7 +- .../ChallengePanel/index.tsx | 8 +- .../BrowsedProjectPage/ChallengesList.tsx | 68 +- .../BrowsedProjectPage/ProjectDetail.tsx | 60 +- .../DashboardPage/ContributionsSection.tsx | 44 +- .../Pages/DashboardPage/LevelModal.tsx | 66 +- .../DashboardPage/LockedTasksSection.tsx | 26 +- .../DashboardPage/SavedChallengesSection.tsx | 28 +- .../Pages/DashboardPage/TeamsSection.tsx | 55 +- .../DashboardPage/UserProfileSection.tsx | 38 +- .../ErrorBoundary/RouteErrorBoundary.tsx | 333 ++-- .../ChallengeList/ChallengeActionMenu.tsx | 20 +- .../ChallengeList/ChallengesTableView.tsx | 80 +- .../ChallengeList/ListFooter.tsx | 18 +- .../ChallengeList/index.tsx | 33 +- .../SearchThisAreaButton.tsx | 4 +- .../ExploreChallengesResults.tsx | 6 +- .../FilterBar/ClearFiltersButton.tsx | 4 +- .../FilterBar/DifficultyFilter.tsx | 22 +- .../FilterBar/GlobalMapToggles.tsx | 6 +- .../FilterBar/LocationSearchFilter.tsx | 61 +- .../FilterBar/SortByFilter.tsx | 24 +- .../FilterBar/ViewModeToggle.tsx | 23 +- .../FilterBar/WorkOnFilter.tsx | 40 +- .../ChallengeRecentActivity.tsx | 33 +- .../ChallengeStatusIndicator.tsx | 79 +- .../ChallengeTasksExplorer.tsx | 152 +- .../ManageChallengeDetailContent.tsx | 198 +- .../MiniChallengeMap.tsx | 8 +- .../ManageChallengeEdit/index.tsx | 10 +- .../ManageChallengeNew/ChallengeForm.tsx | 422 ++++- .../ManageChallengeNew/index.tsx | 11 +- .../ManageChallengesContent.tsx | 170 +- .../ManagementPages/ManageHome/index.tsx | 38 +- .../ChallengesTableView.tsx | 157 +- .../ManageProjectDetailContent.tsx | 261 ++- .../ManageProjectEdit/index.tsx | 10 +- .../ManageProjectNew/ProjectForm.tsx | 183 +- .../ManageProjectNew/index.tsx | 10 +- .../ManageProjects/ManageProjectsContent.tsx | 181 +- .../ManageProjects/ProjectsTableView.tsx | 74 +- .../ManageTaskDetail/index.tsx | 81 +- .../ManageTaskEdit/TaskForm.tsx | 90 +- .../ManagementPages/ManageTaskEdit/index.tsx | 30 +- .../ManagementPages/ManageTaskNew/index.tsx | 18 +- .../ManagementPages/ManageTasksOpen/index.tsx | 45 +- .../ManagementPages/ManagementLayout.tsx | 43 +- .../MoveChallengeModal/index.tsx | 27 +- .../Editor/BoundsDrawControl.tsx | 91 +- .../Editor/DefaultPrioritySelect.tsx | 6 +- .../Editor/TierWarningBadges.tsx | 6 +- .../Preview/PreviewMap.tsx | 4 +- .../PrioritizationContent.tsx | 29 +- .../TaskPrioritizationPage/index.tsx | 6 +- .../BulkActionsToolbar/BulkActionsToolbar.tsx | 125 +- .../BulkClearLockDialog.tsx | 65 +- .../BulkActionsToolbar/BulkDeleteDialog.tsx | 65 +- .../BulkActionsToolbar/BulkReassignDialog.tsx | 31 +- .../BulkActionsToolbar/BulkStatusDialog.tsx | 24 +- .../BulkActionsToolbar/BulkTagDialog.tsx | 14 +- .../shared/ChallengeStatusBanner.tsx | 10 +- .../shared/RebuildTasksDialog.tsx | 123 +- .../shared/VisibilityToggle.tsx | 9 +- .../NotificationsPage/NotificationFilters.tsx | 45 +- .../NotificationsPage/NotificationItem.tsx | 73 +- .../NotificationSelectAll.tsx | 16 +- .../NotificationThreadDialog.tsx | 78 +- .../NotificationsPage/NotificationToolbar.tsx | 34 +- .../NotificationsPageContent.tsx | 37 +- .../NotificationsPage/SavedViewsMenu.tsx | 82 +- .../Pages/ProfilePage/ProfileHeader.tsx | 10 +- .../Pages/ProfilePage/TimeRangeSelector.tsx | 20 +- .../ProfilePage/blocks/ReviewStatsBlock.tsx | 29 +- .../ProfilePage/blocks/ReviewerStatsBlock.tsx | 25 +- .../ProfilePage/blocks/TaskStatsBlock.tsx | 13 +- src/components/Pages/ProfilePage/index.tsx | 6 +- .../sections/AchievementsSection.tsx | 10 +- .../ProfilePage/sections/MetricsSection.tsx | 4 +- .../sections/TopChallengesSection.tsx | 4 +- .../SuperAdminAnalytics/index.tsx | 165 +- .../SuperAdminChallenges/index.tsx | 73 +- .../SuperAdminPages/SuperAdminHome/index.tsx | 102 +- .../SuperAdminPages/SuperAdminLayout.tsx | 30 +- .../SuperAdminPlugins/index.tsx | 87 +- .../SuperAdminProjects/index.tsx | 78 +- .../SuperAdminSettings/index.tsx | 266 ++- .../SuperAdminPages/SuperAdminUsers/index.tsx | 105 +- .../Pages/TaskEditPage/IdEditorView.tsx | 52 +- .../TaskEditPage/KeyboardShortcutsModal.tsx | 54 +- .../Pages/TaskEditPage/TaskActionModal.tsx | 141 +- .../TaskEditPage/TaskActions/EditorButton.tsx | 107 +- .../TaskEditPage/TaskActions/LockButton.tsx | 27 +- .../TaskActions/NavigationActions.tsx | 48 +- .../TaskEditPage/TaskActions/SkipButton.tsx | 22 +- .../TaskActions/StartMappingActions.tsx | 26 +- .../TaskEditPage/TaskActions/TaskActions.tsx | 52 +- .../Pages/TaskEditPage/TaskInfoHeader.tsx | 60 +- src/components/Pages/TaskEditPage/TaskMap.tsx | 12 +- .../TaskMap/ClearBundleDialog.tsx | 17 +- .../TaskEditPage/TaskMap/MultiTaskPanel.tsx | 33 +- .../TaskMap/useMapControlButtons.ts | 25 +- .../TaskMap/useTaskMapShortcuts.ts | 31 +- .../Pages/TaskEditPage/TaskNearbyMap.tsx | 16 +- .../TaskEditPage/contexts/OSMDataContext.tsx | 35 +- .../contexts/TaskBundleContext.tsx | 6 +- .../Pages/TeamsPage/EditTeamPage.tsx | 13 +- .../Pages/TeamsPage/InviteMemberDialog.tsx | 36 +- .../Pages/TeamsPage/NewTeamPage.tsx | 18 +- .../Pages/TeamsPage/PendingInvitesSection.tsx | 37 +- src/components/Pages/TeamsPage/TeamCard.tsx | 8 +- .../Pages/TeamsPage/TeamDetailPage.tsx | 70 +- src/components/Pages/TeamsPage/TeamForm.tsx | 24 +- src/components/Pages/TeamsPage/TeamsList.tsx | 17 +- .../TaskInfoPanel/CommentsHistoryTab.tsx | 48 +- .../OSMHistoryTab/AreaHistoryCard.tsx | 37 +- .../OSMHistoryTab/ElementHistoryCard.tsx | 25 +- .../OSMHistoryTab/LinkedChangesetCard.tsx | 43 +- .../OSMHistoryTab/OSMFeatureCard.tsx | 11 +- .../OSMHistoryTab/OSMHistoryTab.tsx | 21 +- .../OSMHistoryTab/TaskTimelineCard.tsx | 29 +- .../PropertiesTab/PropertiesTab.tsx | 4 +- .../TaskInfoPanel/TaskInfoDrawer.tsx | 54 +- .../TaskTab/BundleStateIndicator.tsx | 15 +- .../TaskInfoPanel/TaskTab/BundleTaskList.tsx | 26 +- .../TaskTab/InstructionPanel.tsx | 18 +- src/components/TaskInfoPanel/TaskTabsList.tsx | 56 +- .../AchievementBadge/AchievementBadge.tsx | 12 +- src/components/shared/ChallengeCard.tsx | 16 +- .../shared/ClearManageFiltersButton.tsx | 4 +- .../CommentComposer/CommentComposer.tsx | 30 +- .../CommentComposer/CommentMentionInput.tsx | 8 +- .../shared/CommentList/CommentItem.tsx | 13 +- .../shared/CommentList/CommentList.tsx | 10 +- .../ConfigureColumnsDialog.tsx | 28 +- .../variants/AchievementEarned.tsx | 10 +- .../variants/ChallengeCompleted.tsx | 58 +- .../CongratulateModal/variants/LevelUp.tsx | 30 +- .../variants/StreakMilestone.tsx | 28 +- src/components/shared/DigitDisplay.tsx | 6 +- .../shared/EmailRequirementNotice.tsx | 20 +- src/components/shared/MapLoadingIndicator.tsx | 4 +- src/components/shared/NotFound.tsx | 42 +- src/components/shared/PointsTicker.tsx | 8 +- src/components/shared/ProjectCard.tsx | 32 +- src/components/shared/ProjectPickerModal.tsx | 44 +- src/components/shared/SearchBar.tsx | 11 +- src/components/shared/SectionHeader.tsx | 54 +- src/components/shared/ShareLink/ShareLink.tsx | 9 +- .../shared/ShareLink/SharePopoverContent.tsx | 27 +- src/components/shared/SignIn.tsx | 14 +- src/components/shared/StatusBadge.tsx | 8 +- .../StatusBreakdownBar/StatusBreakdownBar.tsx | 6 +- .../StatusBreakdownBar/useActionSummary.ts | 5 +- .../TaskPropertyQueryBuilder/RuleGroup.tsx | 23 +- .../TaskPropertyQueryBuilder/RuleLeaf.tsx | 20 +- src/components/shared/TaskTags/TagChip.tsx | 49 +- src/components/shared/TaskTags/TagInput.tsx | 12 +- src/components/shared/TaskTags/TaskTags.tsx | 11 +- .../shared/TaskTags/TaskTagsDialog.tsx | 14 +- .../TopChallengesList/TopChallengeRow.tsx | 8 +- .../TopChallengesList/TopChallengesList.tsx | 14 +- src/components/shared/ViewModeToggle.tsx | 16 +- src/components/ui/Dialog.tsx | 45 +- src/components/ui/Loader.tsx | 34 +- src/components/ui/Spinner.tsx | 21 +- src/components/ui/Table.tsx | 4 +- src/contexts/ChallengeFormContext.tsx | 12 +- src/contexts/NotificationsContext.tsx | 58 +- src/contexts/PluginContext.tsx | 73 +- src/contexts/ThemeContext.tsx | 48 +- src/i18n/messages/en-US.json | 1641 ++++++++++++++++- src/lib/SuperAdminGuard.tsx | 12 +- src/main.tsx | 13 +- .../RapidEditorPlugin/RapidEditorView.tsx | 28 +- src/routes/_app/$.tsx | 39 +- 213 files changed, 8943 insertions(+), 2259 deletions(-) diff --git a/src/components/AppLayout/BetaBanner.tsx b/src/components/AppLayout/BetaBanner.tsx index a47775c5b..07c7d3b2e 100644 --- a/src/components/AppLayout/BetaBanner.tsx +++ b/src/components/AppLayout/BetaBanner.tsx @@ -1,8 +1,15 @@ +import { useIntl } from '@/i18n' import { DismissibleBanner } from './DismissibleBanner' -export const BetaBanner = () => ( - - This is a beta version of MapRoulette and is actively being worked on. Some features may be - incomplete or unavailable. - -) +export const BetaBanner = () => { + const { t } = useIntl() + return ( + + {t( + 'appLayout.betaBanner.message', + undefined, + 'This is a beta version of MapRoulette and is actively being worked on. Some features may be incomplete or unavailable.' + )} + + ) +} diff --git a/src/components/AppLayout/DismissibleBanner.test.tsx b/src/components/AppLayout/DismissibleBanner.test.tsx index fdbceed4a..683659e4e 100644 --- a/src/components/AppLayout/DismissibleBanner.test.tsx +++ b/src/components/AppLayout/DismissibleBanner.test.tsx @@ -1,6 +1,7 @@ import { cleanup, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { afterEach, describe, expect, it } from 'vitest' +import { IntlProvider } from '@/i18n' import { DismissibleBanner } from './DismissibleBanner.tsx' afterEach(() => { @@ -8,15 +9,17 @@ afterEach(() => { localStorage.clear() }) +const renderBanner = (ui: React.ReactElement) => render({ui}) + describe('DismissibleBanner', () => { it('renders its children on first visit', () => { - render(Hello) + renderBanner(Hello) expect(screen.getByText('Hello')).toBeDefined() }) it('disappears after the dismiss button is clicked', async () => { const user = userEvent.setup() - render(Hello) + renderBanner(Hello) await user.click(screen.getByRole('button', { name: 'Dismiss banner' })) @@ -25,25 +28,25 @@ describe('DismissibleBanner', () => { it('stays dismissed across remounts (persistence by storage key)', async () => { const user = userEvent.setup() - const { unmount } = render( + const { unmount } = renderBanner( Hello ) await user.click(screen.getByRole('button', { name: 'Dismiss banner' })) unmount() - render(Hello) + renderBanner(Hello) expect(screen.queryByText('Hello')).toBeNull() }) it('does not affect a banner with a different storage key', async () => { const user = userEvent.setup() - const { unmount } = render( + const { unmount } = renderBanner( Hello ) await user.click(screen.getByRole('button', { name: 'Dismiss banner' })) unmount() - render(Hi) + renderBanner(Hi) expect(screen.getByText('Hi')).toBeDefined() }) }) diff --git a/src/components/AppLayout/DismissibleBanner.tsx b/src/components/AppLayout/DismissibleBanner.tsx index 4f3af960b..c983cb7e2 100644 --- a/src/components/AppLayout/DismissibleBanner.tsx +++ b/src/components/AppLayout/DismissibleBanner.tsx @@ -1,5 +1,6 @@ import { X } from 'lucide-react' import { type ReactNode, useState } from 'react' +import { useIntl } from '@/i18n' interface Props { /** localStorage key used to persist the dismissed state across reloads. */ @@ -8,6 +9,7 @@ interface Props { } export const DismissibleBanner = ({ storageKey, children }: Props) => { + const { t } = useIntl() const [dismissed, setDismissed] = useState(() => localStorage.getItem(storageKey) === 'true') if (dismissed) return null @@ -24,7 +26,7 @@ export const DismissibleBanner = ({ storageKey, children }: Props) => { type="button" onClick={handleDismiss} className="-translate-y-1/2 absolute top-1/2 right-3 rounded p-1 hover:bg-slate-800 dark:hover:bg-slate-200" - aria-label="Dismiss banner" + aria-label={t('appLayout.dismissibleBanner.dismiss', undefined, 'Dismiss banner')} > diff --git a/src/components/AppLayout/Header/DesktopNav.tsx b/src/components/AppLayout/Header/DesktopNav.tsx index 8d0f19810..5e9d85401 100644 --- a/src/components/AppLayout/Header/DesktopNav.tsx +++ b/src/components/AppLayout/Header/DesktopNav.tsx @@ -1,12 +1,17 @@ import { Link } from '@tanstack/react-router' import { ExternalLink } from 'lucide-react' import { useNavigationContext } from '@/contexts/NavigationContext' +import { useIntl } from '@/i18n' export const DesktopNav = () => { const { allNavigationItems } = useNavigationContext() + const { t } = useIntl() return ( -