@i18n-micro/path-strategy
@i18n-micro/path-strategy
Runtime path and route strategies for Nuxt I18n Micro: locale resolution from URL, link generation, redirects, and SEO helpers. Use this package when you need a single routing strategy at build time so only the chosen implementation is bundled.
Overview
- Strategies:
no_prefix,prefix,prefix_except_default,prefix_and_default - API:
resolveLocaleFromPath,getLocaleFromPath,localeRoute,switchLocaleRoute,getRedirect,getClientRedirect,shouldReturn404,getCanonicalPath,getRouteBaseName,getCurrentLocale,getPluginRouteName,formatPathForResolve - Framework-agnostic: Works with any router that implements the
RouterAdapterinterface (hasRoute,resolve); compatible with Vue Router and custom adapters. - Tree-shakeable: Subpath exports (
/prefix,/no-prefix, etc.) let the bundler include only the selected strategy.
Package structure
| Path | Contents |
|---|---|
src/types.ts |
All type definitions (PathStrategyContext, RouteLike, ResolvedRouteLike, RouterAdapter, PathStrategy, etc.) |
src/path.ts |
Path utilities (normalizePath, joinUrl, buildUrl, getPathWithoutLocale, getLocaleFromPath, hasKeys, etc.) |
src/resolver.ts |
Route analysis and resolution (analyzeRoute, getRouteBaseName, buildLocalizedName, isIndexRouteName, resolveCustomPath, etc.) |
src/helpers.ts |
Strategy-agnostic helpers (shouldReturn404, preserveQueryAndHash, tryResolveByLocalizedName, etc.) |
src/strategies/ |
BasePathStrategy, createPathStrategy factory, and strategy implementations: no-prefix, prefix, prefix-except-default, prefix-and-default |
src/strategies/common.ts |
Shared strategy logic (defaultResolveLocaleRoute) |
Installation
pnpm add @i18n-micro/path-strategy @i18n-micro/types
Peer dependency: @i18n-micro/types.
Setup: Using a single strategy via Nuxt alias (build-time)
To ship only the selected strategy (no factory, no other implementations), wire a Nuxt alias at build time.
1. Configure the Nuxt module
In your Nuxt module's setup() (or in nuxt.config), set the alias to the strategy entry you want:
// src/module.ts (or nuxt.config.ts)
import type { Strategies } from '@i18n-micro/types'
const strategyEntries: Record<Strategies, string> = {
no_prefix: '@i18n-micro/path-strategy/no-prefix',
prefix: '@i18n-micro/path-strategy/prefix',
prefix_except_default: '@i18n-micro/path-strategy/prefix-except-default',
prefix_and_default: '@i18n-micro/path-strategy/prefix-and-default',
}
const selectedStrategy = strategyEntries[options.strategy] ?? strategyEntries.prefix_except_default
nuxt.options.alias['#i18n-strategy'] = selectedStrategy
2. Type declarations for #i18n-strategy
So that TypeScript and your IDE understand the alias, add a generated declaration in prepare:types:
import { resolve } from 'node:path'
nuxt.hook('prepare:types', ({ references }) => {
nuxt.addTemplate({
filename: 'i18n-strategy.d.ts',
getContents: () => `
import { BasePathStrategy } from '@i18n-micro/path-strategy'
export class Strategy extends BasePathStrategy {}
`,
})
references.push({ path: resolve(nuxt.options.buildDir, 'i18n-strategy.d.ts') })
})
If your setup already resolves types for this alias, you can skip this step.
3. Use in a plugin or composable
Import the strategy only via the alias so the bundler includes just that implementation:
// runtime/plugins/01.i18n-strategy.ts
import { Strategy } from '#i18n-strategy'
import type { PathStrategyContext } from '@i18n-micro/path-strategy/types'
export default defineNuxtPlugin((nuxtApp) => {
const context: PathStrategyContext = {
strategy: useRuntimeConfig().public.i18n.strategy,
defaultLocale: 'en',
locales: [/* ... */],
localizedRouteNamePrefix: 'localized-',
router: { hasRoute, resolve },
// optional: globalLocaleRoutes, routeLocales, routesLocaleLinks, noPrefixRedirect
}
const pathStrategy = new Strategy(context)
return { provide: { i18nStrategy: pathStrategy } }
})
Only the selected strategy and the base class are included in the bundle; the other strategies are tree-shaken out.
4. Package exports
| Entry | Contents |
|---|---|
@i18n-micro/path-strategy |
Main entry: types, factory, all strategies, resolver utils (getRouteBaseName, buildLocalizedName, isIndexRouteName, analyzeRoute) |
@i18n-micro/path-strategy/prefix |
PrefixPathStrategy and Strategy alias |
@i18n-micro/path-strategy/no-prefix |
NoPrefixPathStrategy and Strategy |
@i18n-micro/path-strategy/prefix-except-default |
PrefixExceptDefaultPathStrategy and Strategy |
@i18n-micro/path-strategy/prefix-and-default |
PrefixAndDefaultPathStrategy and Strategy |
@i18n-micro/path-strategy/types |
Types only (PathStrategyContext, RouteLike, ResolvedRouteLike, RouterAdapter, etc.) |
API
Factory
createPathStrategy(ctx: PathStrategyContext)— Returns the strategy instance forctx.strategy. Use for tests or server-side when you don't use the alias.
Resolver utilities (from main entry)
getRouteBaseName(route, options?)— Returns the base route name (without localized prefix/suffix).buildLocalizedName(baseName, localeCode, prefix?)— Builds a localized route name (e.g.about+de+localized-->localized-about-de).isIndexRouteName(name, options?)— Returns whether the route name is the index route (e.g.index,localized-index-en).analyzeRoute(route, ctx)— Returns aRouteAnalysisobject withbaseName,pathWithoutLocale,localeFromPath, andisIndexfor a given route and context.
Strategy interface
Each strategy implements:
| Method | Description |
|---|---|
localeRoute(targetLocale, routeOrPath, currentRoute?) |
Localized route for the target locale. Returns RouteLike with path and fullPath set. |
switchLocaleRoute(fromLocale, toLocale, route, options) |
Route to navigate to when switching locale. Returns RouteLike or string. |
getRedirect(currentPath, targetLocale) |
Path to redirect to on server, or null. |
getClientRedirect(currentPath, preferredLocale) |
Path to redirect to on client (after hydration), or null. |
shouldReturn404(currentPath) |
Returns a 404 redirect path if the current path is invalid, or null. |
getCanonicalPath(route, targetLocale) |
Custom path for the route in that locale (from globalLocaleRoutes), or null. |
resolveLocaleFromPath(path) |
Locale code from path (strategy-specific; e.g. no_prefix returns null). |
getLocaleFromPath(path) |
Parses path and returns first segment if it is a locale code. |
getRouteBaseName(route) |
Base name for the route (delegates to resolver with context). |
getCurrentLocale(route, defaultLocaleOverride?) |
Determines current locale from route (considers strategy, hashMode, params, path). |
getPluginRouteName(route, locale) |
Returns the base route name for translation loading. |
getCurrentLocaleName(route, defaultLocaleOverride?) |
Returns displayName of the current locale, or null. |
setRouter(router) |
Replaces the router adapter at runtime. |
formatPathForResolve(path, fromLocale, toLocale) |
Transforms a path for cross-locale resolution. |
getDefaultLocale() |
Returns the default locale code. |
getLocales() |
Returns the configured locales array. |
getStrategy() |
Returns the strategy name. |
Context (PathStrategyContext)
Import from @i18n-micro/path-strategy/types.
| Property | Description |
|---|---|
strategy, defaultLocale, locales, localizedRouteNamePrefix, router |
Required. |
localeCodes |
Pre-computed locale code strings. Set automatically in constructor from locales. |
globalLocaleRoutes |
Record<string, Record<string, string> | false>: custom path per route/locale; false = unlocalized. |
routeLocales |
Record<string, string[]>: route path or base name -> list of locale codes; limits hreflang entries. |
routesLocaleLinks |
Record<string, string>: base name -> key for routeLocales lookup (e.g. 'products-id' -> 'products'). |
noPrefixRedirect |
When true (default), NoPrefixPathStrategy.getRedirect strips a leading locale segment (e.g. /en/about -> /about). Set to false to disable. |
hashMode |
Locale stored in hash, no prefix in path. |
disablePageLocales |
When true, all pages use only global translations (no page-specific loading). |
debug |
Enable debug logging. |
Redirect behavior per strategy
- prefix: root
/->/{locale}; paths without prefix get prefix; wrong prefix is replaced. - prefix_except_default: default locale with prefix (e.g.
/en/about) -> path without prefix (/about); non-default without prefix -> add prefix; root with non-default ->/{locale}. - prefix_and_default: same as prefix (all locales have prefix in URL).
- no_prefix: paths that start with a locale segment (e.g.
/en/about) -> path without segment (/about), unlessnoPrefixRedirectisfalse.
Using getRedirect in middleware
const redirectPath = strategy.getRedirect(to.fullPath, detectedLocale)
if (redirectPath) return navigateTo(redirectPath)
Integration with Nuxt I18n Micro
The main Nuxt module (nuxt-i18n-micro) integrates path-strategy by default: it generates the strategy from the chosen strategy option, creates the instance with runtime config and router, and provides it as nuxtApp.$i18nStrategy. The redirect plugin calls strategy.getRedirect() (server) and strategy.getClientRedirect() (client) and redirects when a path is returned.
Testing
The package is tested with Jest. Tests include unit tests for each strategy, snapshot tests, a memory-leak test suite, and performance benchmarks.
# Unit tests
pnpm test
# Performance benchmarks (Node.js)
pnpm test:perf
# Performance benchmarks (real browsers — Chromium, Firefox, WebKit)
pnpm bench:browser:ci
# Single browser
pnpm bench:browser:ci -- --browser=firefox
# Interactive browser benchmark (opens dev server)
pnpm bench:browser
Browser benchmarks
The bench/ directory contains a browser-based performance benchmark that runs the same suite as the Node.js tests but in a real browser engine via Playwright. This measures actual client-side performance across V8 (Chromium), SpiderMonkey (Firefox), and JavaScriptCore (WebKit).
Results are saved as baselines per browser engine in tests/__perf__/baseline-browser-{engine}.json.
Performance optimizations
- Pure functions over classes — Route resolution (
analyzeRoute,resolveCustomPath, etc.) uses pure functions instead of a class-basedRouteResolver, enabling better tree-shaking and V8 inlining. - Pre-computed context flags —
localeCodes,_hasGR(has global locale routes), and_hasRL(has route locales) are computed once in the constructor, avoidingObject.keys()allocations on hot paths. - Minimal object allocation — Route objects are constructed via direct property assignment instead of
{ ...spread }to reduce GC pressure. - Fast path utilities —
normalizePath,joinUrl, andcleanDoubleSlashesuse character-code checks and fast paths for common cases (e.g. simple paths without double slashes). hasKeys()utility — O(1) check for whether an object has own keys, withoutObject.keys()array allocation.
License
MIT
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 1.3.20 | MIT | |
| 1.3.19 | MIT | |
| 1.3.18 | MIT | |
| 1.3.17 | MIT | |
| 1.3.16 | MIT | |
| 1.3.15 | MIT | |
| 1.3.12 | MIT | |
| 1.3.11 | MIT | |
| 1.3.10 | MIT | |
| 1.3.9 | MIT | |
| 1.3.7 | MIT | |
| 1.3.6 | MIT | |
| 1.3.5 | MIT | |
| 1.3.4 | MIT | |
| 1.3.3 | MIT | |
| 1.3.2 | MIT | |
| 1.3.0 | MIT | |
| 1.2.0 | MIT | |
| 1.1.3 | MIT | |
| 1.1.2 | MIT | |
| 1.1.1 | MIT | |
| 1.0.3 | MIT | |
| 1.0.1 | MIT | |
| 1.0.0 | MIT |