A multilingual Nuxt 4 portfolio with prerendered SEO, dual industry/research resume views, and a local-LLM translation pipeline.
Live: ajbarea.github.io Languages: English · Español · 日本語 · 中文
The personal portfolio of AJ Barea, software engineer, federated-learning researcher at RIT, and incoming PhD student in Computing and Information Sciences. The site ships in four languages with prerendered hreflang SEO, a filterable project gallery, dual industry/research resume views, a Nuxt Content-backed blog, and a contact form with clipboard fallback.
The full content surface (~280 strings) lives in i18n/locales/*.json. Adding a new language is a one-line config edit plus a translation file.
- Four locales: English (canonical), Spanish (hand-authored), Japanese, Simplified Chinese
- Project gallery: 12 curated projects, filterable by AI/ML, Federated Learning, Full-Stack, Robotics, Hackathon
- Dual resume views: toggle between Industry and Research with separate experience streams
- Blog: Nuxt Content with code highlighting
- Photo gallery: PhotoSwipe lightbox over Cloudinary CDN
- Contact form: Web3Forms backend with clipboard-copy fallback when no API key is set
- Dark mode: system-aware with manual toggle
- 64 prerendered routes: 16 per locale via static-site generation
- SEO:
hreflangon every page, self-pointing canonicals,og:localealternates - Reduced-motion respected by the typewriter and text-scroller
- Local-LLM translation pipeline:
scripts/auto-translate.mjscalls Qwen2.5-7B via Ollama, validates placeholders, writes a reviewable draft
| Layer | Choice |
|---|---|
| Framework | Nuxt 4 (static-site generation) |
| UI | Vue 3 + Tailwind CSS v4 |
| Language | TypeScript (strict) |
| i18n | @nuxtjs/i18n v10 with prefix_except_default strategy |
| Content | @nuxt/content for the blog |
| Images | @nuxt/image over Cloudinary |
| Gallery | PhotoSwipe v5 with dynamic captions |
| State | Pinia |
| Forms | Web3Forms |
| Translation | Local Qwen2.5-7B-Instruct via Ollama |
| Testing | Vitest (unit) + Playwright (e2e) |
| Lint / format | ESLint + Prettier |
| Deploy | GitHub Actions → GitHub Pages on Node 24 |
git clone https://github.com/ajbarea/ajbarea.github.io.git
cd ajbarea.github.io
npm installnpm run dev # dev server with HMR at http://localhost:3000npm run generate # static-site generation → .output/public/
npm run preview # serve the built site locallySet NUXT_PUBLIC_WEB3FORMS_KEY in .env to enable real form submissions. Without it the form falls back to copying the email address to clipboard.
ajbarea.github.io/
├── app/
│ ├── assets/styles/ # global CSS + design tokens
│ ├── components/
│ │ ├── home/ # AuthorCard, ExperienceTimeline, ActivityHighlights, ExpertiseGrid, ContactSection, ...
│ │ ├── projects/ # ProjectCard, ProjectFilter
│ │ ├── resume/ # ExperienceList, EducationCard, PublicationList, SkillsGrid, ResumeViewToggle
│ │ ├── layout/ # TheNavigation, LanguageSelector, ThemeToggle
│ │ └── ui/ # ContactForm, ToastContainer
│ ├── composables/ # useTypewriter, useClipboard, useToast
│ ├── data/ # profile, projects, timeline, publications, hackathons, conferences, workshops, highlights
│ ├── pages/ # index, projects, resume, blog, gallery
│ ├── stores/ # Pinia stores
│ └── types/ # TypeScript interfaces
├── content/articles/ # Nuxt Content blog markdown
├── i18n/locales/ # en.json, es.json, ja.json, zh.json
├── public/ # static assets, PDFs, favicons
├── scripts/ # audit, hero-card composition, auto-translate
├── e2e/ # Playwright specs
├── nuxt.config.ts # i18n locales, image CDN, SEO base URL
└── IMPL.md # implementation spec (language-selector phases)
The portfolio ships in four locales with bidirectional hreflang linking and self-pointing canonicals on every page. Adding a new locale is a three-step recipe.
Copy i18n/locales/en.json to i18n/locales/{code}.json and translate every value. Preserve placeholders like {name}, {title}, {category} exactly, and keep the vue-i18n escape {'@'} intact (it renders as a literal @ in email-like strings).
Requires Ollama installed and qwen2.5:7b-instruct pulled.
node scripts/auto-translate.mjs --target {es|ja|zh}The script walks en.json recursively, calls Qwen for each string, validates that every placeholder survived the round-trip, retries up to 3× on failure, and writes a gitignored {target}.draft.json. Diff the draft against your committed locale before promoting it. Quality varies by language: Spanish is hand-authored; Mandarin needed ~15 surgical fixes on top of the draft; Japanese needed substantial rewrites because Qwen-7B mixed CJK scripts and leaked tokens from other languages.
// nuxt.config.ts
i18n: {
locales: [
{ code: 'en', language: 'en', file: 'en.json', name: 'English' },
{ code: 'es', language: 'es', file: 'es.json', name: 'Español' },
{ code: 'ja', language: 'ja', file: 'ja.json', name: '日本語' },
{ code: 'zh', language: 'zh-CN', file: 'zh.json', name: '中文' }
// add your locale here
]
}The LanguageSelector component picks up the new locale automatically from the dynamic locale list. hreflang, canonical, and og:locale tags update via useLocaleHead({ seo: true }) on each page. No code changes needed.
For the full phased rollout (Phases 1a → 1b → 2 → 3) and the per-language quality observations, see IMPL.md.
npm run dev # dev server with HMR
npm run generate # static-site generation
npm run preview # serve the built site locally
npm run lint # ESLint over the full repo
npm run format # Prettier auto-format
npm run test:unit # Vitest (component + composable unit tests)
npm run test:e2e # Playwright end-to-end suiteFor full pre-push validation, ./lint.sh runs format, lint, npm audit, unit tests, build, and e2e in sequence.
Pushes to main trigger .github/workflows/deploy.yml, which runs npm ci and npm run generate on Node 24, then deploys .output/public/ to GitHub Pages via the official Pages actions. The first run after a new locale lands prerenders the additional routes (16 per locale) automatically.
Part of a family of repos exploring agentic AI and federated learning from complementary angles. ajbarea.github.io is the Visibility surface; the others occupy different roles.
- kourai-khryseai — Innovation. Multi-agent software-development forge: maidens-as-specialists over A2A, MCP sidecars, transparent human-on-the-loop.
- phalanx-fl — Research. Federated-learning reference platform on Flower + Ray. Eight aggregation strategies with the attack vocabulary.
- vFL — Performance. Same FL strategies as Rust kernels via PyO3 + FastMCP + Prefect Horizon.
- ldqis — Lab identity. Public website for the Laboratory of Data Quality and Intelligent Security at RIT.
- techne — Governance. Claude Code skills plugin: audits, lint/test gates, cross-repo drift detection.