|  | 
|  | 1 | +# TanSTack Router example | 
|  | 2 | + | 
|  | 3 | +This example shows how to use Paraglide with TanStack Router. The source code can be found [here](https://github.com/opral/monorepo/tree/main/inlang/packages/paraglide/paraglide-js/examples/tanstack-router). | 
|  | 4 | + | 
|  | 5 | +## Getting started | 
|  | 6 | + | 
|  | 7 | +1. Init Paraglide JS | 
|  | 8 | + | 
|  | 9 | +```bash | 
|  | 10 | +npx @inlang/paraglide-js@latest init | 
|  | 11 | +``` | 
|  | 12 | + | 
|  | 13 | +2. Add the vite plugin to your `vite.config.ts`: | 
|  | 14 | + | 
|  | 15 | +```diff | 
|  | 16 | +import { defineConfig } from 'vite' | 
|  | 17 | +import react from '@vitejs/plugin-react' | 
|  | 18 | +import { tanstackRouter } from '@tanstack/router-plugin/vite' | 
|  | 19 | ++import { paraglideVitePlugin } from "@inlang/paraglide-js"; | 
|  | 20 | + | 
|  | 21 | +export default defineConfig({ | 
|  | 22 | +	plugins: [ | 
|  | 23 | +    tanstackRouter({ target: 'react', autoCodeSplitting: true }), | 
|  | 24 | +    react(), | 
|  | 25 | ++		paraglideVitePlugin({ | 
|  | 26 | ++			project: "./project.inlang", | 
|  | 27 | ++			outdir: "./app/paraglide", | 
|  | 28 | ++		}), | 
|  | 29 | +	], | 
|  | 30 | +}); | 
|  | 31 | +``` | 
|  | 32 | + | 
|  | 33 | +3. Done :) | 
|  | 34 | + | 
|  | 35 | +Run the app and start translating. See the [basics documentation](/m/gerre34r/library-inlang-paraglideJs/basics) for information on how to use Paraglide's messages, parameters, and locale management. | 
|  | 36 | + | 
|  | 37 | +## Rewrite URL | 
|  | 38 | + | 
|  | 39 | +If you want to handle how the URL looks when the user changes the locale, you can rewrite the URL in the router. | 
|  | 40 | + | 
|  | 41 | +```diff | 
|  | 42 | +import { createRouter } from "@tanstack/react-router"; | 
|  | 43 | +import { routeTree } from "./routeTree.gen"; | 
|  | 44 | ++import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; | 
|  | 45 | + | 
|  | 46 | +const router = createRouter({ | 
|  | 47 | +  routeTree, | 
|  | 48 | ++ rewrite: { | 
|  | 49 | ++   input: ({ url }) => deLocalizeUrl(url), | 
|  | 50 | ++   output: ({ url }) => localizeUrl(url), | 
|  | 51 | +  }, | 
|  | 52 | +}); | 
|  | 53 | +``` | 
|  | 54 | + | 
|  | 55 | +In `__root.tsx` add a `beforeLoad` hook to check if the user should be redirected and set the html `lang` attribute. | 
|  | 56 | + | 
|  | 57 | +```ts | 
|  | 58 | +import { shouldRedirect } from "../paraglide/runtime"; | 
|  | 59 | + | 
|  | 60 | +export const Route = createRootRoute({ | 
|  | 61 | +  beforeLoad: async () => { | 
|  | 62 | +    document.documentElement.setAttribute("lang", getLocale()); | 
|  | 63 | + | 
|  | 64 | +    const decision = await shouldRedirect({ url: window.location.href }); | 
|  | 65 | + | 
|  | 66 | +    if (decision.redirectUrl) { | 
|  | 67 | +      throw redirect({ href: decision.redirectUrl.href }); | 
|  | 68 | +    } | 
|  | 69 | +  }, | 
|  | 70 | +  ... | 
|  | 71 | +}); | 
|  | 72 | +``` | 
|  | 73 | + | 
|  | 74 | +## Typesafe translated pathnames | 
|  | 75 | + | 
|  | 76 | +If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. | 
|  | 77 | + | 
|  | 78 | +```ts | 
|  | 79 | +import { Locale } from "@/paraglide/runtime"; | 
|  | 80 | +import { FileRoutesByTo } from "../routeTree.gen"; | 
|  | 81 | + | 
|  | 82 | +type RoutePath = keyof FileRoutesByTo; | 
|  | 83 | + | 
|  | 84 | +const excludedPaths = ["admin", "docs", "api"] as const; | 
|  | 85 | + | 
|  | 86 | +type PublicRoutePath = Exclude< | 
|  | 87 | +  RoutePath, | 
|  | 88 | +  `${string}${(typeof excludedPaths)[number]}${string}` | 
|  | 89 | +>; | 
|  | 90 | + | 
|  | 91 | +type TranslatedPathname = { | 
|  | 92 | +  pattern: string; | 
|  | 93 | +  localized: Array<[Locale, string]>; | 
|  | 94 | +}; | 
|  | 95 | + | 
|  | 96 | +function toUrlPattern(path: string) { | 
|  | 97 | +  return ( | 
|  | 98 | +    path | 
|  | 99 | +      // catch-all | 
|  | 100 | +      .replace(/\/\$$/, "/:path(.*)?") | 
|  | 101 | +      // optional parameters: {-$param} | 
|  | 102 | +      .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") | 
|  | 103 | +      // named parameters: $param | 
|  | 104 | +      .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") | 
|  | 105 | +      // remove trailing slash | 
|  | 106 | +      .replace(/\/+$/, "") | 
|  | 107 | +  ); | 
|  | 108 | +} | 
|  | 109 | + | 
|  | 110 | +function createTranslatedPathnames( | 
|  | 111 | +  input: Record<PublicRoutePath, Record<Locale, string>> | 
|  | 112 | +): TranslatedPathname[] { | 
|  | 113 | +  return Object.entries(input).map(([pattern, locales]) => ({ | 
|  | 114 | +    pattern: toUrlPattern(pattern), | 
|  | 115 | +    localized: Object.entries(locales).map( | 
|  | 116 | +      ([locale, path]) => | 
|  | 117 | +        [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ | 
|  | 118 | +          Locale, | 
|  | 119 | +          string, | 
|  | 120 | +        ] | 
|  | 121 | +    ), | 
|  | 122 | +  })); | 
|  | 123 | +} | 
|  | 124 | + | 
|  | 125 | +export const translatedPathnames = createTranslatedPathnames({ | 
|  | 126 | +  "/": { | 
|  | 127 | +    en: "/", | 
|  | 128 | +    de: "/", | 
|  | 129 | +  }, | 
|  | 130 | +  "/about": { | 
|  | 131 | +    en: "/about", | 
|  | 132 | +    de: "/ueber", | 
|  | 133 | +  }, | 
|  | 134 | +}); | 
|  | 135 | +``` | 
|  | 136 | + | 
|  | 137 | +And import into the Paraglide Vite plguin. | 
|  | 138 | + | 
|  | 139 | +## Server side rendering | 
|  | 140 | + | 
|  | 141 | +For server side rerdering, check out the [TanStack Start guide](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/tanstack-start). | 
0 commit comments