feat(client): upgrades i18next to v25.5.2, turns on type-checking#61970
feat(client): upgrades i18next to v25.5.2, turns on type-checking#61970ahrjarrett wants to merge 22 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
|
Hi @ahrjarrett, we appreciate your great work here ❤️ We're juggling quite a few things at the moment, so it might take us a little while to review this PR thoroughly. But really excited for this feature - it's going to be a big boost to the DX!
Thanks for creating the issue to track those. We will take a look and work through them. |
Sounds good! Let me know when you're ready to start reviewing so I can pull, migrate the i18n work that's been done in the meantime, merge, push. That way the team only has to review it once :) |
|
We would love to be able to merge your changes but it looks like there is an error with the CI build. Once you resolve these issues, we will be able to review your PR and merge it. 😊 Feel free to reference the contributing guidelines for instructions on running the CI build locally. ✅ |
|
Hi @ahrjarrett 👋 Just a quick update: we've got a high-priority PR (#61906) coming in with a bunch of new translation strings. I think it makes sense to wait for that to be merged before bringing this one in. Once #61906 is merged, what workflow would you recommend for handling the update? Should I merge |
There is a codemod, but you probably won't want to use it in this case because it will "undo" all of the places where I left the string-based selector in tact. Probably the easiest path forward if you'd like to move forward on your own is to:
// TODO: convert to selector #61969That way you keep the risk of making a mistake low, start enjoying the benefit of having strongly typed translations now, and handle the actual migration when it's convenient / makes sense for your roadmap. Let me know if you need a hand while you do this! Large migrations like this can be tricky as I'm sure you're aware, so sometimes having an extra set of 👀 can be helpful :) Excited to get this working for the team! |
This comment was marked as off-topic.
This comment was marked as off-topic.
|
Let's hold off on updating this PR (because there will be more merge conflicts). We'll get the blocking PRs in first, and I'll help resolve the conflicts. |
There was a problem hiding this comment.
Pull request overview
Upgrades the client’s i18n setup to i18next v25.5.2 and migrates translation usage to selector-based keys to enable TypeScript type-checking/autocomplete across translations.
Changes:
- Bump
i18nextand configureCustomTypeOptionsresources + selector support. - Migrate many
t('key.path')calls tot($ => $.key.path)(and add a few runtime helpers for dynamic keys). - Update unit tests/mocks to support selector-based translation keys.
Reviewed changes
Copilot reviewed 198 out of 200 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| client/utils/tags.tsx | Use selector-based i18next.t for meta tag strings |
| client/tools/generate-search-placeholder.ts | Use selector-based t/i18n.t for placeholders |
| client/src/templates/Introduction/super-block-intro.tsx | Selector-based intro translations + safer structure typing |
| client/src/templates/Introduction/components/super-block-map.tsx | Selector-based intro translations + user type narrowing |
| client/src/templates/Introduction/components/super-block-intro.tsx | Selector-based translations and intro data access |
| client/src/templates/Introduction/components/super-block-accordion.tsx | Use namespace resource access for intro chapter/module labels |
| client/src/templates/Introduction/components/legacy-links.tsx | Selector-based intro + misc translations |
| client/src/templates/Introduction/components/help-translate.tsx | Selector-based learn/links translations |
| client/src/templates/Introduction/components/challenges.tsx | Selector-based aria/buttons/icons translations |
| client/src/templates/Introduction/components/cert-challenge.tsx | Selector-based certification titles + safer state typing |
| client/src/templates/Introduction/components/block.test.tsx | Update test translation mocking for selectors |
| client/src/templates/Introduction/components/block-label.tsx | Selector-based block label translation |
| client/src/templates/Challenges/utils/index.ts | Selector-based aria/learn strings in prism helpers |
| client/src/templates/Challenges/utils/frame.ts | Selector-based iframe title translation |
| client/src/templates/Challenges/redux/execute-challenge-saga.test.js | Update i18next mock to support selector keys |
| client/src/templates/Challenges/redux/execute-challenge-saga.js | Selector-based learn strings in challenge execution |
| client/src/templates/Challenges/redux/create-question-epic.js | Selector-based forum-help/intro translations |
| client/src/templates/Challenges/quiz/show.tsx | Selector-based quiz/learn/buttons strings + TODO casts |
| client/src/templates/Challenges/quiz/finish-quiz-modal.tsx | Selector-based quiz modal strings |
| client/src/templates/Challenges/quiz/exit-quiz-modal.tsx | Selector-based quiz modal strings |
| client/src/templates/Challenges/projects/tool-panel.tsx | Selector-based button strings |
| client/src/templates/Challenges/projects/solution-form.tsx | Selector-based learn strings for labels/buttons |
| client/src/templates/Challenges/projects/frontend/show.tsx | Selector-based learn title + TODO casts |
| client/src/templates/Challenges/projects/backend/show.tsx | Selector-based learn title/test-output + TODO casts |
| client/src/templates/Challenges/ms-trophy/show.tsx | Selector-based learn title/buttons + TODO casts |
| client/src/templates/Challenges/ms-trophy/link-ms-user.tsx | Selector-based MS learn/buttons strings |
| client/src/templates/Challenges/generic/show.tsx | Selector-based learn/buttons + intro object access |
| client/src/templates/Challenges/fill-in-the-blank/show.tsx | Selector-based learn/buttons + intro title helper usage |
| client/src/templates/Challenges/exam/show.tsx | Selector-based exam/learn/buttons + intro object access |
| client/src/templates/Challenges/exam/components/missing-prerequisites.tsx | Selector-based caution + exam strings |
| client/src/templates/Challenges/exam/components/foundational-c-sharp-survey-alert.tsx | Selector-based survey strings |
| client/src/templates/Challenges/exam/components/finish-exam-modal.tsx | Selector-based exam modal strings |
| client/src/templates/Challenges/exam/components/exit-exam-modal.tsx | Selector-based exam modal strings |
| client/src/templates/Challenges/exam/components/exam-results.tsx | Selector-based exam results strings/interpolation |
| client/src/templates/Challenges/exam-download/show.tsx | Selector-based exam download page strings |
| client/src/templates/Challenges/exam-download/exam-token-controls.tsx | Selector-based exam-token strings |
| client/src/templates/Challenges/exam-download/attempts.tsx | Selector-based exam/flash strings |
| client/src/templates/Challenges/components/tool-panel.tsx | Selector-based buttons/aria strings |
| client/src/templates/Challenges/components/test-suite.tsx | Selector-based learn/icons strings |
| client/src/templates/Challenges/components/scene/scene.tsx | Selector-based a11y button labels |
| client/src/templates/Challenges/components/reset-modal.tsx | Selector-based learn/buttons strings |
| client/src/templates/Challenges/components/preview.tsx | Selector-based iframe title |
| client/src/templates/Challenges/components/preview-portal.tsx | Selector-based editor tab title |
| client/src/templates/Challenges/components/output.tsx | Selector-based aria label |
| client/src/templates/Challenges/components/multiple-choice-questions.tsx | Selector-based learn/quiz/speaking-modal strings |
| client/src/templates/Challenges/components/fill-in-the-blanks.tsx | Selector-based learn/aria strings |
| client/src/templates/Challenges/components/daily-challenge-bread-crumb.tsx | Selector-based aria + intro title (daily challenge) |
| client/src/templates/Challenges/components/completion-modal.tsx | Selector-based completion modal strings |
| client/src/templates/Challenges/components/challenge-transcript.tsx | Selector-based learn transcript string |
| client/src/templates/Challenges/components/challenge-title.tsx | Selector-based links/misc strings |
| client/src/templates/Challenges/components/challenge-heading.tsx | Temporary cast to keep string-key usage compiling |
| client/src/templates/Challenges/components/challenge-explanation.tsx | Selector-based learn explanation string |
| client/src/templates/Challenges/components/bread-crumb.tsx | Selector-based aria + TODO casts for intro keys |
| client/src/templates/Challenges/components/assignments.tsx | Use plural-form keys explicitly for assignments text |
| client/src/templates/Challenges/codeally/show.tsx | Selector-based learn strings + intro object access |
| client/src/templates/Challenges/codeally/rdb-step-2-instructions.tsx | Selector-based learn step strings |
| client/src/templates/Challenges/codeally/rdb-step-1-instructions.tsx | Selector-based learn step strings |
| client/src/templates/Challenges/codeally/rdb-ona-logout-alert.tsx | Selector-based caution + learn strings |
| client/src/templates/Challenges/codeally/rdb-ona-continue-alert.tsx | Selector-based misc note label |
| client/src/templates/Challenges/codeally/rdb-local-logout-alert.tsx | Selector-based caution + learn strings |
| client/src/templates/Challenges/codeally/ona-instructions.tsx | Selector-based learn local/ona strings |
| client/src/templates/Challenges/codeally/local-instructions.tsx | Selector-based learn local strings |
| client/src/templates/Challenges/codeally/codespaces-instructions.tsx | Selector-based learn codespaces/local strings |
| client/src/templates/Challenges/classic/xterm.tsx | Selector-based aria string |
| client/src/templates/Challenges/classic/show.tsx | Selector-based learn/buttons strings + TODO casts |
| client/src/templates/Challenges/classic/mobile-layout.tsx | Selector-based editor-tabs/aria strings |
| client/src/templates/Challenges/classic/lower-jaw.tsx | Selector-based learn/buttons/aria strings + dynamic learn key |
| client/src/templates/Challenges/classic/editor.tsx | Selector-based aria/editor-alerts/icons + TODO casts |
| client/src/templates/Challenges/classic/editor-tabs.tsx | Selector-based editor tab sr-only text |
| client/src/templates/Challenges/classic/action-row.tsx | Selector-based aria/editor-tabs/buttons strings |
| client/src/redux/donation-saga.js | Selector-based donate error strings |
| client/src/pages/update-stripe-card.tsx | Selector-based learn/buttons/misc strings |
| client/src/pages/supporters.tsx | Selector-based learn/buttons/misc strings |
| client/src/pages/learn/daily-coding-challenge/archive.tsx | Selector-based daily-coding-challenges title |
| client/src/pages/learn/archive.tsx | Selector-based metaTags/learn/landing strings |
| client/src/pages/learn.tsx | Selector-based metaTags title |
| client/src/pages/index.tsx | Selector-based metaTags title for SEO |
| client/src/pages/email-sign-up.tsx | Selector-based misc title |
| client/src/pages/donate.tsx | Selector-based donate title |
| client/src/pages/catalog.tsx | Selector-based curriculum catalog strings |
| client/src/i18next.d.ts | Add typed resources + selector config in CustomTypeOptions |
| client/src/components/staging-warning-modal/index.tsx | Selector-based staging warning strings |
| client/src/components/signout-modal/index.tsx | Selector-based signout strings |
| client/src/components/share/use-share.tsx | TODO cast for intro key string |
| client/src/components/share/use-share.test.tsx | Adjust test expectations for selector-based translation behavior |
| client/src/components/share/share-template.tsx | Selector-based share/aria strings |
| client/src/components/settings/user-token.tsx | Selector-based user-token strings |
| client/src/components/settings/sound.tsx | Selector-based settings/buttons strings |
| client/src/components/settings/settings-sidebar-nav.tsx | Selector-based headings + helper for certification titles |
| client/src/components/settings/scrollbar-width.tsx | Selector-based settings string |
| client/src/components/settings/reset-modal.tsx | Selector-based settings danger-zone strings |
| client/src/components/settings/keyboard-shortcuts.tsx | Selector-based settings/buttons strings |
| client/src/components/settings/honesty.tsx | Selector-based settings/buttons strings |
| client/src/components/settings/exam-token.tsx | Selector-based exam-token/buttons strings |
| client/src/components/settings/email.tsx | Selector-based validation/settings/buttons strings |
| client/src/components/settings/delete-modal.tsx | Selector-based settings danger-zone strings |
| client/src/components/settings/danger-zone.tsx | Selector-based settings danger-zone strings |
| client/src/components/settings/account.tsx | Selector-based settings strings |
| client/src/components/seo/index.tsx | Selector-based intro object retrieval |
| client/src/components/search/searchBar/search-hits.tsx | Selector-based search aria strings |
| client/src/components/search/searchBar/search-bar.tsx | Selector-based search/search-bar strings + TODO cast |
| client/src/components/search/searchBar/search-bar-optimized.tsx | Selector-based search/search-bar strings + TODO cast |
| client/src/components/search/searchBar/search-bar-footer.tsx | Selector-based search strings with interpolation |
| client/src/components/profile/profile.tsx | Selector-based profile/buttons/misc strings |
| client/src/components/profile/components/utils/utils.ts | Selector-based profile joined string |
| client/src/components/profile/components/username.tsx | Selector-based settings strings + TODO cast |
| client/src/components/profile/components/timeline-pagination.tsx | Selector-based aria/profile strings |
| client/src/components/profile/components/stats.tsx | Selector-based profile strings |
| client/src/components/profile/components/social-icons.tsx | Selector-based aria social labels with interpolation |
| client/src/components/profile/components/profile-completeness.tsx | Precompute translated labels via selectors |
| client/src/components/profile/components/portfolio-projects.tsx | Selector-based profile/aria strings |
| client/src/components/profile/components/internet.tsx | Selector-based validation/settings strings |
| client/src/components/profile/components/heat-map.tsx | Selector-based profile/aria strings + plural handling |
| client/src/components/profile/components/experience-display.tsx | Selector-based profile experience strings |
| client/src/components/profile/components/certifications.tsx | Selector-based profile/settings strings + TODO cast |
| client/src/components/profile/components/camper.tsx | Selector-based profile strings |
| client/src/components/profile/components/bio.tsx | Selector-based aria/profile strings |
| client/src/components/profile/components/about.tsx | Selector-based settings/validation/buttons strings |
| client/src/components/layouts/default.tsx | Selector-based metaTags/learn strings + TODO cast |
| client/src/components/landing/components/two-button-cta.tsx | Selector-based buttons/misc strings |
| client/src/components/landing/components/testimonials.tsx | Selector-based landing strings |
| client/src/components/landing/components/landing-top.tsx | Selector-based landing strings |
| client/src/components/landing/components/landing-catalog.tsx | Selector-based landing catalog strings |
| client/src/components/landing/components/faq.tsx | Selector-based landing/learn strings + object return |
| client/src/components/landing/components/campers-image.tsx | Selector-based landing image alt/caption |
| client/src/components/landing/components/big-call-to-action.tsx | Selector-based default CTA string |
| client/src/components/landing/components/benefits.tsx | Selector-based landing benefits strings + object return |
| client/src/components/landing/components/as-seen-in.tsx | Selector-based landing string |
| client/src/components/helpers/loader.tsx | Selector-based misc slow-load string |
| client/src/components/helpers/form/block-save-button.tsx | Selector-based default save label |
| client/src/components/helpers/avatar-renderer.tsx | Selector-based profile sr-only label |
| client/src/components/growth-book/ona-note.tsx | Selector-based intro/misc-text string |
| client/src/components/growth-book/codeally-down.tsx | Selector-based misc labels + intro Trans ns usage |
| client/src/components/growth-book/codeally-button.tsx | Selector-based buttons/aria strings (layout tweak) |
| client/src/components/email-options.tsx | Selector-based buttons/misc strings |
| client/src/components/daily-coding-challenge/widget.tsx | Selector-based daily-coding-challenges/buttons strings |
| client/src/components/daily-coding-challenge/not-found.tsx | Selector-based 404/dcc/buttons strings |
| client/src/components/daily-coding-challenge/calendar.tsx | Selector-based weekdays/aria/buttons strings |
| client/src/components/daily-coding-challenge/calendar-day.tsx | Selector-based aria not-available string |
| client/src/components/catalog-item.tsx | Selector-based intro/curriculum catalog strings |
| client/src/components/archived-warning/index.tsx | Selector-based misc note label |
| client/src/components/SolutionViewer/project-modal.tsx | Selector-based settings/buttons strings |
| client/src/components/SolutionViewer/exam-results-modal.tsx | Selector-based exam results/buttons strings |
| client/src/components/Progress/progress.tsx | Selector-based intro object access + learn strings |
| client/src/components/OfflineWarning/offline-warning.tsx | Selector-based misc offline string |
| client/src/components/Map/index.tsx | Selector-based landing headings + intro titles |
| client/src/components/Intro/learn-alert.tsx | Selector-based learn/donate/buttons strings |
| client/src/components/Intro/index.tsx | Selector-based welcome/heading/login strings |
| client/src/components/Header/components/universal-nav.tsx | Selector-based aria labels |
| client/src/components/Header/components/menu-button.tsx | Selector-based menu label |
| client/src/components/Header/components/login.tsx | Selector-based sign-in default text |
| client/src/components/Header/components/language-list.tsx | Selector-based change-language label |
| client/src/components/Header/components/exam-nav.tsx | Selector-based primary nav aria label |
| client/src/components/Header/components/auth-or-profile.tsx | Selector-based sign-in label |
| client/src/components/FourOhFour/index.tsx | Selector-based 404/buttons strings |
| client/src/components/Flash/index.tsx | Replace t(message, vars) with custom flash translation lookup |
| client/src/components/Donation/wallets-button.tsx | Selector-based donate error strings |
| client/src/components/Donation/stripe-card-form.tsx | Use TFunction and selector-based donate/buttons strings |
| client/src/components/Donation/multi-tier-donation-form.tsx | Selector-based donate/buttons strings |
| client/src/components/Donation/donate-form.tsx | Selector-based donate strings |
| client/src/components/Donation/donate-completion.tsx | Selector-based donate/buttons strings |
| client/src/components/Donation/card-update-alert-handler.tsx | Selector-based donate/buttons strings |
| client/src/client-only-routes/show-user.tsx | Selector-based report/buttons strings |
| client/src/client-only-routes/show-user.test.tsx | Update test translation mocking for selectors |
| client/src/client-only-routes/show-update-email.tsx | Selector-based misc/buttons strings |
| client/src/client-only-routes/show-unsubscribed.tsx | Selector-based metaTags/misc/buttons strings |
| client/src/client-only-routes/show-settings.tsx | Selector-based settings/misc/buttons strings |
| client/src/client-only-routes/show-project-links.tsx | Selector-based certification/settings strings + selector-heading helper |
| client/src/assets/icons/magnifier.tsx | Selector-based icons sr-only label |
| client/src/assets/icons/lightbulb.tsx | Selector-based icon title |
| client/src/assets/icons/input-reset.tsx | Selector-based icons sr-only label |
| client/src/assets/icons/initial.tsx | Selector-based icon title |
| client/src/assets/icons/green-pass.tsx | Selector-based icon label/title |
| client/src/assets/icons/green-not-completed.tsx | Selector-based icon sr-only/title |
| client/src/assets/icons/fail.tsx | Selector-based icon title |
| client/src/assets/icons/default-avatar.tsx | Selector-based icon title/desc |
| client/package.json | Upgrade i18next to 25.5.2 |
| client/i18n/locales/english/translations.json | Add aria.bluesky translation |
| client/config/growthbook-features-default.json | JSON formatting normalization |
| client/mocks/react-i18next.js | Update react-i18next test mock to support selector keys |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
You can also share your feedback on Copilot code review. Take the survey.
|
Hi @ahrjarrett, thank you so much for your patience and the effort you've put into this PR. After reviewing it more carefully, we've decided not to move forward with this migration at this time. Given our current priorities, we don't think the cost/benefit trade-off works in our favor right now. We really appreciate your contribution and the time you invested in this. We sincerely apologize that we can't take it further. |
Closes #61818
Closes #60543
Closes #49621
Supersedes #61820
Fast-follow: #61969
Intro
i18next@25.4.2 has been released, and includes a new feature that allows freeCodeCamp to turn on type-checking for all translations.
This PR performs that migration.
To prevent merge conflicts and to avoid having to make decisions that I don't have the context to make on my own, I created a fast-follow ticket (#61969) as a catch-all for all the places where type-checking did not work because certain translations were missing.
Where that was necessary, I added the following comment:
// TODO: convert to selector #61969That way whoever picks up #61969 could
grepthe codebase to find all the TODOs related to that piece of work.Demo
To make the change easier to understand, I created a quick demo showing a before and after.
Before
Impact
This makes the system more difficult to change without accidentally introducing bugs.
For example, if the path
icons.avatarchanges, the user needs to remember to grepicons.avatarto find all the places where its referenced, or risk breaking the translationAfter
Impact
This makes the system easier to change, because if the location of a translation changes, the user will get a type error when running
pnpm lint:ts.It also reduces the ramp-up time for new users who want to contribute. They don't (necessarily) need to know where translations are located to use a translation. This makes the process of translating freeCodeCamp into different languages more accessible overall.
Let me know if I can answer any questions about the change, and of course please don't hesitate to share any feedback about the change you might have.
Checklist:
mainbranch of freeCodeCamp.