feat(lab): complete EN translations for blog, docs and homepage#69
Conversation
Apply the same two-level pattern as Projects: compact SkillCard (icon + summary + first 3 skill pills + hover overlay with usage bars) opens a SkillModal with full skill metadata (description, years, projects, keywords). Adds `leadership` id to SkillCategory type and fixes the missing icon. 263 tests pass.
|
Deployment failed with the following error: |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the internationalization capabilities of the Lab documentation and blog by adding full English translations across various content types and UI elements. Concurrently, it refines the user experience of the portfolio application's skills section through a modular component redesign, making skill details more accessible and interactive. These changes collectively improve content reach and maintainability while ensuring the stability of new features through updated testing. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request is a significant step forward, successfully adding English translations across the lab's blog and documentation, and refactoring the portfolio's skills section into more modular and interactive components. The changes are well-structured and improve both internationalization and user experience. I've identified a few minor areas for improvement within the newly added documentation files to ensure the examples follow best practices for Docusaurus i18n and test mocking, enhancing maintainability and correctness.
| - **Portfolio**: Next.js 15, React 19, Tailwind, Framer Motion | ||
| - **Lab**: Docusaurus 2.4 for documentation and this blog | ||
|
|
||
| Feel free to explore the [Guides](/en/docs/intro) for more structured documentation on architecture, i18n, and testing. |
There was a problem hiding this comment.
The link to the guides includes a hardcoded /en locale prefix. While this works, the recommended practice in Docusaurus is to use root-relative links (e.g., /docs/intro). Docusaurus's i18n system is designed to automatically prepend the correct locale prefix based on the current language context. Relying on this automatic behavior improves maintainability and makes the content more robust against future changes to URL structures or locale configurations.
| Feel free to explore the [Guides](/en/docs/intro) for more structured documentation on architecture, i18n, and testing. | |
| Feel free to explore the [Guides](/docs/intro) for more structured documentation on architecture, i18n, and testing. |
| ```typescript | ||
| // src/shared/components/layout/LocaleSwitcher.tsx | ||
| 'use client'; | ||
|
|
||
| import { useRouter, usePathname } from 'next/navigation'; | ||
| import { useLocale } from 'next-intl'; | ||
|
|
||
| export function LocaleSwitcher() { | ||
| const router = useRouter(); | ||
| const pathname = usePathname(); | ||
| const currentLocale = useLocale(); | ||
|
|
||
| const switchLocale = (newLocale: string) => { | ||
| // Remove current locale prefix from pathname | ||
| const pathWithoutLocale = pathname.replace(`/${currentLocale}`, ''); | ||
|
|
||
| // Add new locale prefix (except for default locale 'es') | ||
| const newPath = newLocale === 'es' | ||
| ? pathWithoutLocale || '/' | ||
| : `/${newLocale}${pathWithoutLocale || ''}`; | ||
|
|
||
| router.push(newPath); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="flex gap-2"> | ||
| <button | ||
| onClick={() => switchLocale('es')} | ||
| className={currentLocale === 'es' ? 'font-bold' : ''} | ||
| > | ||
| ES | ||
| </button> | ||
| <button | ||
| onClick={() => switchLocale('en')} | ||
| className={currentLocale === 'en' ? 'font-bold' : ''} | ||
| > | ||
| EN | ||
| </button> | ||
| </div> | ||
| ); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
The documented example for the LocaleSwitcher component uses manual path manipulation to switch locales. This approach is fragile and not recommended. next-intl provides its own useRouter and usePathname hooks (from next-intl/client) that offer a much cleaner and more robust API for this purpose. Using router.replace(pathname, {locale: newLocale}) simplifies the logic, reduces the chance of errors, and correctly handles various routing scenarios, such as preserving search parameters.
// src/shared/components/layout/LocaleSwitcher.tsx
'use client';
import { usePathname, useRouter } from 'next-intl/client';
import { useLocale } from 'next-intl';
export function LocaleSwitcher() {
const router = useRouter();
const pathname = usePathname();
const currentLocale = useLocale();
const switchLocale = (newLocale: string) => {
// Use the router to switch the locale, preserving the current path.
router.replace(pathname, {locale: newLocale});
};
return (
<div className="flex gap-2">
<button
onClick={() => switchLocale('es')}
className={currentLocale === 'es' ? 'font-bold' : ''}
>
ES
</button>
<button
onClick={() => switchLocale('en')}
className={currentLocale === 'en' ? 'font-bold' : ''}
>
EN
</button>
</div>
);
}| vi.mock('framer-motion', () => ({ | ||
| motion: { | ||
| div: 'div', | ||
| button: 'button', | ||
| section: 'section', | ||
| }, | ||
| AnimatePresence: ({ children }: { children: React.ReactNode }) => children, | ||
| })); |
There was a problem hiding this comment.
The documented mock for framer-motion is incorrect. Assigning a string like 'div' to motion.div will cause React to throw an error during testing, as it expects a component function, not a string. The mock should provide a valid React component that renders the underlying DOM element and passes through its props.
| vi.mock('framer-motion', () => ({ | |
| motion: { | |
| div: 'div', | |
| button: 'button', | |
| section: 'section', | |
| }, | |
| AnimatePresence: ({ children }: { children: React.ReactNode }) => children, | |
| })); | |
| vi.mock('framer-motion', () => ({ | |
| motion: { | |
| div: (props) => <div {...props} />, | |
| button: (props) => <button {...props} />, | |
| section: (props) => <section {...props} />, | |
| }, | |
| AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>, | |
| })); |
|
✅ CI Pipeline passed! All checks completed successfully. |
|
✅ CI Pipeline passed! All checks completed successfully. |
Summary
_category_.jsonEN override for sidebar label ("Architecture Guides")/docs/intro→/en/docs/intro)<Translate>to homepage components (buttons, tagline, feature cards) and register EN strings incode.jsonTest plan
npx docusaurus start --locale en --port 3002— home shows EN text/en/blog/bienvenida-al-blog-labshows EN content/en/docs/introlocalhost:3001) unaffected