[{"data":1,"prerenderedAt":1049},["ShallowReactive",2],{"navigation":3,"/blog/nuxt-i18n-routing":154,"surround-nuxt-i18n-routing":1036},[4],{"title":5,"path":6,"stem":7,"children":8,"page":153},"Blog","/blog","blog",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149],{"title":10,"path":11,"stem":12},"How to Increase Free Tier Memory on AWS EC2","/blog/aws-ec2-ram","blog/aws-ec2-ram",{"title":14,"path":15,"stem":16},"Blooper: The Disappearing Cloudflare Variables","/blog/blooper-cloudflare-env-vars","blog/blooper-cloudflare-env-vars",{"title":18,"path":19,"stem":20},"Blooper: Hardcoded Content Links cause 404s","/blog/blooper-hardcoded-nuxt-content-links","blog/blooper-hardcoded-nuxt-content-links",{"title":22,"path":23,"stem":24},"Blooper: The Invisible SEO","/blog/blooper-nuxt-seo-env","blog/blooper-nuxt-seo-env",{"title":26,"path":27,"stem":28},"Blooper: The SSR Crash of Doom","/blog/blooper-ssr-feedback-form","blog/blooper-ssr-feedback-form",{"title":30,"path":31,"stem":32},"Blooper: The YouTube Crawl Budget Killer","/blog/blooper-video-crawl-budget","blog/blooper-video-crawl-budget",{"title":34,"path":35,"stem":36},"Blooper: PWA Redirecting SPA Routes","/blog/blooper-vite-pwa-ssr-redirects","blog/blooper-vite-pwa-ssr-redirects",{"title":38,"path":39,"stem":40},"Git Branching in 2026: Code State, Environment Isolation, and Modern CD","/blog/branch-theater-staging-branches","blog/branch-theater-staging-branches",{"title":42,"path":43,"stem":44},"The Developer's Guide to Avoiding Cloud Billing Traps in 2026","/blog/cloud-billing-traps-2026","blog/cloud-billing-traps-2026",{"title":46,"path":47,"stem":48},"Disk Management in Linux","/blog/disk-management-in-linux","blog/disk-management-in-linux",{"title":50,"path":51,"stem":52},"Docker Devtools Stage | Powerlevel, OhMyZSH, tmux, fuzzy finder, autocomplete etc","/blog/docker-devtools","blog/docker-devtools",{"title":54,"path":55,"stem":56},"Docker container | Resolving Permission Denied Woes: A Decade-Long Struggle Unraveled","/blog/docker-non-root-user-perms","blog/docker-non-root-user-perms",{"title":58,"path":59,"stem":60},"The Ultimate Guide to Free Tier Hosting Platforms (Serverless & Containers)","/blog/free-hosting-platforms-guide","blog/free-hosting-platforms-guide",{"title":62,"path":63,"stem":64},"Git commands | underrated","/blog/git-commands-underrated","blog/git-commands-underrated",{"title":66,"path":67,"stem":68},"Journey: Taming Edge Caching and KV Storage","/blog/journey-github-streak-caching","blog/journey-github-streak-caching",{"title":70,"path":71,"stem":72},"Kubernetes Cluster Setup Guide","/blog/k8s-setup","blog/k8s-setup",{"title":74,"path":75,"stem":76},"Linux WiFi Drivers","/blog/linux-wifi-drivers","blog/linux-wifi-drivers",{"title":78,"path":79,"stem":80},"4 Common Routing Traps When Migrating to Nuxt i18n","/blog/nuxt-i18n-routing","blog/nuxt-i18n-routing",{"title":82,"path":83,"stem":84},"Patch the CVE, Don't Trigger an Accidental Refactor: Surviving PHP2Shell on Docker","/blog/patching-php2shell-docker-playbook","blog/patching-php2shell-docker-playbook",{"title":86,"path":87,"stem":88},"pfSense basic firewall setup","/blog/pfsense-basic-firewall-setup","blog/pfsense-basic-firewall-setup",{"title":90,"path":91,"stem":92},"pfSense Quickstart","/blog/pfsense-quickstart","blog/pfsense-quickstart",{"title":94,"path":95,"stem":96},"Postmortem: Cloudflare Worker Missing Dependency on Deploy","/blog/postmortem-cloudflare-worker-dependency-deploy","blog/postmortem-cloudflare-worker-dependency-deploy",{"title":98,"path":99,"stem":100},"Postmortem: Serverless HTTP Driver Transaction Failures","/blog/postmortem-neon-http-transactions","blog/postmortem-neon-http-transactions",{"title":102,"path":103,"stem":104},"Postmortem: The Ghost in the Chrome Cache (Nuxt & Cloudflare Pages)","/blog/postmortem-nuxt-cloudflare-chunk-error","blog/postmortem-nuxt-cloudflare-chunk-error",{"title":106,"path":107,"stem":108},"Postmortem: Nuxt Content 404s on Staging Deployments","/blog/postmortem-nuxt-content-404-staging","blog/postmortem-nuxt-content-404-staging",{"title":110,"path":111,"stem":112},"Postmortem: 500 Internal Server Error on ISR Routes","/blog/postmortem-nuxt-content-payload-extraction","blog/postmortem-nuxt-content-payload-extraction",{"title":114,"path":115,"stem":116},"Post-Mortem: Intrusive Database Driver Refactoring","/blog/postmortem-proxy-re-architecting","blog/postmortem-proxy-re-architecting",{"title":118,"path":119,"stem":120},"Post-Mortem: Cloudflare Worker Connection Hang & Resilient DB Fallbacks","/blog/postmortem-worker-db-connection-hang","blog/postmortem-worker-db-connection-hang",{"title":122,"path":123,"stem":124},"Pro Tip: Sandboxing Cloudflare Workers for AI Agents","/blog/pro-tip-wrangler-local-dev-trick","blog/pro-tip-wrangler-local-dev-trick",{"title":126,"path":127,"stem":128},"Proxmox DNS Management","/blog/proxmox-dns","blog/proxmox-dns",{"title":130,"path":131,"stem":132},"Proxmox Recovery","/blog/proxmox-recovery","blog/proxmox-recovery",{"title":134,"path":135,"stem":136},"Proxmox WiFi Connection Story","/blog/proxmox-wifi","blog/proxmox-wifi",{"title":138,"path":139,"stem":140},"roadauth-rails api jwt cors","/blog/rodauth-jwt","blog/rodauth-jwt",{"title":142,"path":143,"stem":144},"SOCKS5 Proxy Quickstart","/blog/sock5-proxy","blog/sock5-proxy",{"title":146,"path":147,"stem":148},"tmux status bar toggle","/blog/tmux-status","blog/tmux-status",{"title":150,"path":151,"stem":152},"UFW - Cheatsheet","/blog/ufw-cheatsheet","blog/ufw-cheatsheet",false,{"id":155,"title":78,"authors":156,"badge":162,"body":164,"date":1027,"description":1028,"extension":1029,"image":1030,"imageComponent":1030,"meta":1031,"navigation":256,"path":79,"pinned":153,"seo":1032,"seoImage":1033,"stem":80,"__hash__":1035},"posts/blog/nuxt-i18n-routing.md",[157],{"name":158,"to":159,"avatar":160},"Rahul Dhole","/",{"src":161},"/profile.png",{"label":163},"Nuxt",{"type":165,"value":166,"toc":1018},"minimark",[167,184,211,214,217,220,225,228,234,293,316,321,328,331,366,369,388,390,394,397,401,435,438,450,464,497,511,565,567,571,574,579,594,609,614,627,642,665,667,671,674,678,717,730,737,745,872,876,882,915,918,920,925,933,1011,1014],[168,169,170,171,175,176,183],"p",{},"If you're adding translations to your NuxtJS app using ",[172,173,174],"code",{},"@nuxtjs/i18n",", you probably expect it to be a simple config change. But there is a massive gotcha that caught me totally off guard while translating ",[177,178,182],"a",{"href":179,"rel":180},"https://neoncv.ai",[181],"nofollow","neoncv.ai",": your URLs are no longer static.",[168,185,186,187,190,191,194,195,198,199,202,203,206,207,210],{},"Depending on your i18n routing strategy (like ",[172,188,189],{},"prefix_except_default"," or ",[172,192,193],{},"prefix","), that predictable ",[172,196,197],{},"/dashboard"," route dynamically shifts to ",[172,200,201],{},"/fr/dashboard",", ",[172,204,205],{},"/ja/dashboard",", or ",[172,208,209],{},"/es/dashboard",".",[168,212,213],{},"This one detail shatters standard Vue routing logic relying on exact path strings. After untangling broken links and missing transitions, I realized most tutorials skip the painful part: retrofitting a large, existing codebase.",[168,215,216],{},"Here are 4 architectural traps you will hit, and the senior-level patterns you should use instead.",[218,219],"hr",{},[221,222,224],"h2",{"id":223},"trap-1-brittle-path-checking","Trap 1: Brittle Path Checking",[168,226,227],{},"It's common to check if a user is inside a specific section of the app to trigger middleware, animations, or load third-party scripts (like a support chat widget only on marketing pages).",[168,229,230],{},[231,232,233],"strong",{},"The Mistake:",[235,236,241],"pre",{"className":237,"code":238,"language":239,"meta":240,"style":240},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const route = useRoute()\n\n// ❌ Breaks instantly on localized paths like /fr/tracker\nconst isTrackerChild = route.path.startsWith('/tracker/')\n\n// ❌ A fragile string-sniffing workaround\nconst routeName = String(route.name || '')\nconst isTrackerChildWorkaround = routeName.startsWith('tracker-')\n","javascript","",[172,242,243,251,258,264,270,275,281,287],{"__ignoreMap":240},[244,245,248],"span",{"class":246,"line":247},"line",1,[244,249,250],{},"const route = useRoute()\n",[244,252,254],{"class":246,"line":253},2,[244,255,257],{"emptyLinePlaceholder":256},true,"\n",[244,259,261],{"class":246,"line":260},3,[244,262,263],{},"// ❌ Breaks instantly on localized paths like /fr/tracker\n",[244,265,267],{"class":246,"line":266},4,[244,268,269],{},"const isTrackerChild = route.path.startsWith('/tracker/')\n",[244,271,273],{"class":246,"line":272},5,[244,274,257],{"emptyLinePlaceholder":256},[244,276,278],{"class":246,"line":277},6,[244,279,280],{},"// ❌ A fragile string-sniffing workaround\n",[244,282,284],{"class":246,"line":283},7,[244,285,286],{},"const routeName = String(route.name || '')\n",[244,288,290],{"class":246,"line":289},8,[244,291,292],{},"const isTrackerChildWorkaround = routeName.startsWith('tracker-')\n",[168,294,295,296,299,300,303,304,307,308,311,312,315],{},"Relying on ",[172,297,298],{},"route.path"," fails because the path changes with locales. Switching to ",[172,301,302],{},"route.name.startsWith()"," is slightly better, but replaces one brittle hack with another. What if someone renames ",[172,305,306],{},"tracker/[id].vue"," to ",[172,309,310],{},"tracker/detail-[id].vue","? Or what if ",[172,313,314],{},"tracker-settings.vue"," accidentally triggers it?",[168,317,318],{},[231,319,320],{},"The Senior Fix: Route Meta",[168,322,323,324,327],{},"Use Vue Router's native ",[172,325,326],{},"route.meta",". It's immune to locale prefixes, renaming, and string sniffing.",[168,329,330],{},"Define it in your pages:",[235,332,334],{"className":237,"code":333,"language":239,"meta":240,"style":240},"// pages/tracker/[id].vue\ndefinePageMeta({\n  section: 'tracker',\n  requiresAuth: true,\n  loadSupportChat: false\n})\n",[172,335,336,341,346,351,356,361],{"__ignoreMap":240},[244,337,338],{"class":246,"line":247},[244,339,340],{},"// pages/tracker/[id].vue\n",[244,342,343],{"class":246,"line":253},[244,344,345],{},"definePageMeta({\n",[244,347,348],{"class":246,"line":260},[244,349,350],{},"  section: 'tracker',\n",[244,352,353],{"class":246,"line":266},[244,354,355],{},"  requiresAuth: true,\n",[244,357,358],{"class":246,"line":272},[244,359,360],{},"  loadSupportChat: false\n",[244,362,363],{"class":246,"line":277},[244,364,365],{},"})\n",[168,367,368],{},"Check it in your middleware or computed properties:",[235,370,372],{"className":237,"code":371,"language":239,"meta":240,"style":240},"// middleware/auth.ts or chat widget logic\nconst route = useRoute()\nconst isTrackerChild = route.meta.section === 'tracker'\n",[172,373,374,379,383],{"__ignoreMap":240},[244,375,376],{"class":246,"line":247},[244,377,378],{},"// middleware/auth.ts or chat widget logic\n",[244,380,381],{"class":246,"line":253},[244,382,250],{},[244,384,385],{"class":246,"line":260},[244,386,387],{},"const isTrackerChild = route.meta.section === 'tracker'\n",[218,389],{},[221,391,393],{"id":392},"trap-2-context-blind-navigation","Trap 2: Context-Blind Navigation",[168,395,396],{},"If you have custom \"go back\" buttons or logic that redirects users, you likely have navigation calls scattered everywhere.",[168,398,399],{},[231,400,233],{},[235,402,404],{"className":237,"code":403,"language":239,"meta":240,"style":240},"const router = useRouter()\n\n// ❌ Kicks the user out of their localized environment\nfunction goBack() {\n  router.push('/pricing')\n}\n",[172,405,406,411,415,420,425,430],{"__ignoreMap":240},[244,407,408],{"class":246,"line":247},[244,409,410],{},"const router = useRouter()\n",[244,412,413],{"class":246,"line":253},[244,414,257],{"emptyLinePlaceholder":256},[244,416,417],{"class":246,"line":260},[244,418,419],{},"// ❌ Kicks the user out of their localized environment\n",[244,421,422],{"class":246,"line":266},[244,423,424],{},"function goBack() {\n",[244,426,427],{"class":246,"line":272},[244,428,429],{},"  router.push('/pricing')\n",[244,431,432],{"class":246,"line":277},[244,433,434],{},"}\n",[168,436,437],{},"If a user from Japan reading your pricing page triggers this, they are instantly kicked back to the default English route.",[168,439,440],{},[231,441,442,443,446,447],{},"The Fix: ",[172,444,445],{},"navigateTo"," and ",[172,448,449],{},"\u003CNuxtLinkLocale>",[168,451,452,453,456,457,459,460,463],{},"For programmatic navigation, wrap the path in ",[172,454,455],{},"useLocalePath()"," and use Nuxt's ",[172,458,445],{}," (which safely handles SSR context and cross-environment navigation, unlike raw ",[172,461,462],{},"router.push",").",[235,465,467],{"className":237,"code":466,"language":239,"meta":240,"style":240},"const localePath = useLocalePath()\n\n// ✅ Safely navigates while preserving locale and SSR context\nasync function goBack() {\n  await navigateTo(localePath('/pricing'))\n}\n",[172,468,469,474,478,483,488,493],{"__ignoreMap":240},[244,470,471],{"class":246,"line":247},[244,472,473],{},"const localePath = useLocalePath()\n",[244,475,476],{"class":246,"line":253},[244,477,257],{"emptyLinePlaceholder":256},[244,479,480],{"class":246,"line":260},[244,481,482],{},"// ✅ Safely navigates while preserving locale and SSR context\n",[244,484,485],{"class":246,"line":266},[244,486,487],{},"async function goBack() {\n",[244,489,490],{"class":246,"line":272},[244,491,492],{},"  await navigateTo(localePath('/pricing'))\n",[244,494,495],{"class":246,"line":277},[244,496,434],{},[168,498,499,500,503,504,506,507,510],{},"For template navigation, stop hardcoding ",[172,501,502],{},"\u003CNuxtLink to=\"/pricing\">",". Use the built-in ",[172,505,449],{}," component instead. It automatically wraps your paths in ",[172,508,509],{},"localePath()"," under the hood.",[235,512,516],{"className":513,"code":514,"language":515,"meta":240,"style":240},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- ✅ Automatically resolves to /fr/pricing for French users -->\n\u003CNuxtLinkLocale to=\"/pricing\">Pricing\u003C/NuxtLinkLocale>\n","vue",[172,517,518,524],{"__ignoreMap":240},[244,519,520],{"class":246,"line":247},[244,521,523],{"class":522},"sHwdD","\u003C!-- ✅ Automatically resolves to /fr/pricing for French users -->\n",[244,525,526,530,534,538,541,544,548,550,553,557,560,562],{"class":246,"line":253},[244,527,529],{"class":528},"sMK4o","\u003C",[244,531,533],{"class":532},"swJcz","NuxtLinkLocale",[244,535,537],{"class":536},"spNyl"," to",[244,539,540],{"class":528},"=",[244,542,543],{"class":528},"\"",[244,545,547],{"class":546},"sfazB","/pricing",[244,549,543],{"class":528},[244,551,552],{"class":528},">",[244,554,556],{"class":555},"sTEyZ","Pricing",[244,558,559],{"class":528},"\u003C/",[244,561,533],{"class":532},[244,563,564],{"class":528},">\n",[218,566],{},[221,568,570],{"id":569},"trap-3-reinventing-active-link-states","Trap 3: Reinventing Active Link States",[168,572,573],{},"When building a custom sidebar or navigation menu, you need to highlight the active route.",[168,575,576,578],{},[231,577,233],{},"\nManually hacking active link detection by comparing strings.",[235,580,582],{"className":237,"code":581,"language":239,"meta":240,"style":240},"// ❌ Custom logic that fails on nested routes and edge cases\nconst isActive = (path) => route.path.startsWith(localePath(path))\n",[172,583,584,589],{"__ignoreMap":240},[244,585,586],{"class":246,"line":247},[244,587,588],{},"// ❌ Custom logic that fails on nested routes and edge cases\n",[244,590,591],{"class":246,"line":253},[244,592,593],{},"const isActive = (path) => route.path.startsWith(localePath(path))\n",[168,595,596,597,600,601,604,605,608],{},"This gets messy incredibly fast, and edge cases (like ",[172,598,599],{},"/tracker"," matching ",[172,602,603],{},"/tracker-archive"," due to ",[172,606,607],{},".startsWith()",") will inevitably cause bugs.",[168,610,611],{},[231,612,613],{},"The Fix: Native Router Link Resolution",[168,615,616,617,619,620,446,623,626],{},"Don't reinvent the wheel. ",[172,618,449],{}," natively applies standard ",[172,621,622],{},"router-link-active",[172,624,625],{},"router-link-exact-active"," classes automatically.",[168,628,629,630,634,635,637,638,641],{},"If you ",[631,632,633],"em",{},"must"," build a highly custom wrapper component that can't use ",[172,636,449],{}," directly, leverage Vue Router's ",[172,639,640],{},"useLink"," with the localized path to tap into its robust resolution engine.",[235,643,645],{"className":237,"code":644,"language":239,"meta":240,"style":240},"const localePath = useLocalePath()\n\n// Leverage Vue Router's bulletproof logic\nconst { isActive, isExactActive } = useLink({ to: localePath('/dashboard') })\n",[172,646,647,651,655,660],{"__ignoreMap":240},[244,648,649],{"class":246,"line":247},[244,650,473],{},[244,652,653],{"class":246,"line":253},[244,654,257],{"emptyLinePlaceholder":256},[244,656,657],{"class":246,"line":260},[244,658,659],{},"// Leverage Vue Router's bulletproof logic\n",[244,661,662],{"class":246,"line":266},[244,663,664],{},"const { isActive, isExactActive } = useLink({ to: localePath('/dashboard') })\n",[218,666],{},[221,668,670],{"id":669},"trap-4-locale-switching-query-preservation","Trap 4: Locale Switching & Query Preservation",[168,672,673],{},"Adding a language toggle seems easy until your users try it mid-session on a dynamic route or a page with active filters.",[168,675,676],{},[231,677,233],{},[235,679,681],{"className":513,"code":680,"language":515,"meta":240,"style":240},"\u003C!-- ❌ Loses route parameters and query strings -->\n\u003Ca href=\"/fr/search\">Français\u003C/a>\n",[172,682,683,688],{"__ignoreMap":240},[244,684,685],{"class":246,"line":247},[244,686,687],{"class":522},"\u003C!-- ❌ Loses route parameters and query strings -->\n",[244,689,690,692,694,697,699,701,704,706,708,711,713,715],{"class":246,"line":253},[244,691,529],{"class":528},[244,693,177],{"class":532},[244,695,696],{"class":536}," href",[244,698,540],{"class":528},[244,700,543],{"class":528},[244,702,703],{"class":546},"/fr/search",[244,705,543],{"class":528},[244,707,552],{"class":528},[244,709,710],{"class":555},"Français",[244,712,559],{"class":528},[244,714,177],{"class":532},[244,716,564],{"class":528},[168,718,719,720,723,724,726,727,210],{},"If a user is on ",[172,721,722],{},"/search?q=nuxt"," and clicks your language toggle, redirecting them strictly to ",[172,725,703],{}," wipes out their search query. The same happens on dynamic routes like ",[172,728,729],{},"/users/123",[168,731,732],{},[231,733,442,734],{},[172,735,736],{},"switchLocalePath()",[168,738,739,741,742,744],{},[172,740,174],{}," provides a ",[172,743,736],{}," composable specifically for this. It takes the current route context (including dynamic parameters and query strings) and safely translates it to the target language.",[235,746,748],{"className":513,"code":747,"language":515,"meta":240,"style":240},"\u003Cscript setup>\nconst switchLocalePath = useSwitchLocalePath()\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003C!-- ✅ Preserves /search?q=nuxt -> /fr/search?q=nuxt -->\n    \u003CNuxtLink :to=\"switchLocalePath('fr')\">\n      Français\n    \u003C/NuxtLink>\n  \u003C/div>\n\u003C/template>\n",[172,749,750,762,779,787,791,800,810,815,837,843,853,863],{"__ignoreMap":240},[244,751,752,754,757,760],{"class":246,"line":247},[244,753,529],{"class":528},[244,755,756],{"class":532},"script",[244,758,759],{"class":536}," setup",[244,761,564],{"class":528},[244,763,764,767,770,772,776],{"class":246,"line":253},[244,765,766],{"class":536},"const",[244,768,769],{"class":555}," switchLocalePath ",[244,771,540],{"class":528},[244,773,775],{"class":774},"s2Zo4"," useSwitchLocalePath",[244,777,778],{"class":555},"()\n",[244,780,781,783,785],{"class":246,"line":260},[244,782,559],{"class":528},[244,784,756],{"class":532},[244,786,564],{"class":528},[244,788,789],{"class":246,"line":266},[244,790,257],{"emptyLinePlaceholder":256},[244,792,793,795,798],{"class":246,"line":272},[244,794,529],{"class":528},[244,796,797],{"class":532},"template",[244,799,564],{"class":528},[244,801,802,805,808],{"class":246,"line":277},[244,803,804],{"class":528},"  \u003C",[244,806,807],{"class":532},"div",[244,809,564],{"class":528},[244,811,812],{"class":246,"line":283},[244,813,814],{"class":522},"    \u003C!-- ✅ Preserves /search?q=nuxt -> /fr/search?q=nuxt -->\n",[244,816,817,820,823,826,828,830,833,835],{"class":246,"line":289},[244,818,819],{"class":528},"    \u003C",[244,821,822],{"class":532},"NuxtLink",[244,824,825],{"class":536}," :to",[244,827,540],{"class":528},[244,829,543],{"class":528},[244,831,832],{"class":546},"switchLocalePath('fr')",[244,834,543],{"class":528},[244,836,564],{"class":528},[244,838,840],{"class":246,"line":839},9,[244,841,842],{"class":555},"      Français\n",[244,844,846,849,851],{"class":246,"line":845},10,[244,847,848],{"class":528},"    \u003C/",[244,850,822],{"class":532},[244,852,564],{"class":528},[244,854,856,859,861],{"class":246,"line":855},11,[244,857,858],{"class":528},"  \u003C/",[244,860,807],{"class":532},[244,862,564],{"class":528},[244,864,866,868,870],{"class":246,"line":865},12,[244,867,559],{"class":528},[244,869,797],{"class":532},[244,871,564],{"class":528},[221,873,875],{"id":874},"wrapping-up","Wrapping up",[168,877,878,879,881],{},"Integrating ",[172,880,174],{}," into a production codebase taught me these vital rules:",[883,884,885,894,906,909],"ol",{},[886,887,888,889,891,892,210],"li",{},"Ditch ",[172,890,298],{}," checking in favor of ",[172,893,326],{},[886,895,896,897,900,901,446,904,210],{},"Replace hardcoded ",[172,898,899],{},"router.push()"," with ",[172,902,903],{},"navigateTo(localePath())",[172,905,449],{},[886,907,908],{},"Let Vue Router handle active link states.",[886,910,911,912,914],{},"Always use ",[172,913,736],{}," for language toggles.",[168,916,917],{},"Adopt these architectural patterns early, and you'll save yourself massive headaches taking your app global.",[218,919],{},[921,922,924],"h3",{"id":923},"bonus-the-ai-refactoring-prompt","Bonus: The AI Refactoring Prompt",[168,926,927,928,446,930,932],{},"If you are retrofitting a large, existing codebase and don't want to hunt down every single ",[172,929,462],{},[172,931,298],{}," manually, drop this prompt into your AI coding assistant (like GitHub Copilot, Cursor, or Gemini):",[934,935,936,953,956,982,985],"blockquote",{},[168,937,938,939,941,942,202,945,948,949,952],{},"\"I am migrating my Nuxt 3 application to ",[172,940,174],{},". Scan my ",[172,943,944],{},"pages",[172,946,947],{},"components",", and ",[172,950,951],{},"middleware"," directories for Nuxt routing anti-patterns.",[168,954,955],{},"Specifically, look for:",[883,957,958,966,975],{},[886,959,960,961,190,963,210],{},"Hardcoded string checks against ",[172,962,298],{},[172,964,965],{},"route.name",[886,967,968,969,190,971,974],{},"Direct usage of ",[172,970,899],{},[172,972,973],{},"navigateTo()"," with static strings.",[886,976,977,978,981],{},"Standard ",[172,979,980],{},"\u003CNuxtLink>"," components or manual active link logic.",[168,983,984],{},"Refactor them using Nuxt i18n best practices:",[986,987,988,993,998,1005],"ul",{},[886,989,990,991,210],{},"Replace route path checking with ",[172,992,326],{},[886,994,995,996,210],{},"Wrap programmatic navigation paths in ",[172,997,509],{},[886,999,1000,1001,900,1003,210],{},"Replace standard ",[172,1002,980],{},[172,1004,449],{},[886,1006,1007,1008,1010],{},"Use ",[172,1009,736],{}," for language toggles.\"",[168,1012,1013],{},"Let the AI do the heavy lifting, and enjoy your newly localized app!",[1015,1016,1017],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":240,"searchDepth":253,"depth":253,"links":1019},[1020,1021,1022,1023,1024],{"id":223,"depth":253,"text":224},{"id":392,"depth":253,"text":393},{"id":569,"depth":253,"text":570},{"id":669,"depth":253,"text":670},{"id":874,"depth":253,"text":875,"children":1025},[1026],{"id":923,"depth":260,"text":924},"2026-09-28","How to fix broken navigation, missing widgets, and active links when migrating a NuxtJS app to @nuxtjs/i18n.","md",null,{},{"title":78,"description":1028},{"src":1034},"https://placehold.co/800x400/0f172a/3b82f6?text=Nuxt+i18n+Routing","RLyEVtMsks4KOCaCnvkKnQo3JtJN8Zui_dbSAWJ_smY",[1037,1043],{"title":74,"path":75,"stem":76,"description":1038,"imageComponent":1039,"seoImage":1041,"children":-1},"Linux WiFi Drivers - Dive into the details of linux wifi drivers with this quick guide.",{"type":1040},"Linux",{"src":1042},"https://placehold.co/800x400/0f172a/3b82f6?text=Linux+WiFi+Drivers",{"title":82,"path":83,"stem":84,"description":1044,"imageComponent":1045,"seoImage":1047,"children":-1},"An SRE incident playbook for PHP2Shell (CVE-2026-109618 & CVE-2026-109619) following national cyber alerts. How to patch containerized PHP stacks without breaking production.",{"type":1046},"Security",{"src":1048},"https://placehold.co/800x400/0f172a/3b82f6?text=Patching+PHP2Shell+on+Docker",1792752997593]