From 57d30af4e247241fd7dd8893e3382bd58ab6d4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20M=C3=BChlbauer?= Date: Fri, 26 Sep 2025 11:08:18 -0300 Subject: [PATCH 1/7] docs: add paraglide examples --- examples/react/i18n-paraglide/.gitignore | 9 + .../i18n-paraglide/.vscode/settings.json | 11 + examples/react/i18n-paraglide/README.md | 141 +++++++++++++ examples/react/i18n-paraglide/index.html | 20 ++ .../react/i18n-paraglide/messages/de.json | 7 + .../react/i18n-paraglide/messages/en.json | 7 + examples/react/i18n-paraglide/package.json | 29 +++ .../i18n-paraglide/project.inlang/.gitignore | 1 + .../i18n-paraglide/project.inlang/project_id | 1 + .../project.inlang/settings.json | 15 ++ .../react/i18n-paraglide/public/favicon.ico | Bin 0 -> 3870 bytes .../react/i18n-paraglide/public/logo192.png | Bin 0 -> 5347 bytes .../react/i18n-paraglide/public/logo512.png | Bin 0 -> 9664 bytes .../react/i18n-paraglide/public/manifest.json | 25 +++ .../react/i18n-paraglide/public/robots.txt | 3 + examples/react/i18n-paraglide/src/logo.svg | 12 ++ examples/react/i18n-paraglide/src/main.tsx | 40 ++++ .../react/i18n-paraglide/src/routeTree.gen.ts | 77 +++++++ .../i18n-paraglide/src/routes/__root.tsx | 70 +++++++ .../react/i18n-paraglide/src/routes/about.tsx | 10 + .../react/i18n-paraglide/src/routes/index.tsx | 18 ++ examples/react/i18n-paraglide/src/styles.css | 1 + examples/react/i18n-paraglide/tsconfig.json | 29 +++ examples/react/i18n-paraglide/vite.config.ts | 49 +++++ .../react/start-i18n-paraglide/.gitignore | 10 + .../.vscode/extensions.json | 5 + .../.vscode/settings.json | 11 + examples/react/start-i18n-paraglide/README.md | 196 ++++++++++++++++++ .../start-i18n-paraglide/messages/de.json | 8 + .../start-i18n-paraglide/messages/en.json | 8 + .../react/start-i18n-paraglide/package.json | 29 +++ .../project.inlang/.gitignore | 1 + .../project.inlang/project_id | 1 + .../project.inlang/settings.json | 15 ++ .../start-i18n-paraglide/public/favicon.ico | Bin 0 -> 3870 bytes .../start-i18n-paraglide/public/logo192.png | Bin 0 -> 5347 bytes .../start-i18n-paraglide/public/logo512.png | Bin 0 -> 9664 bytes .../start-i18n-paraglide/public/manifest.json | 25 +++ .../start-i18n-paraglide/public/robots.txt | 3 + .../react/start-i18n-paraglide/src/logo.svg | 12 ++ .../start-i18n-paraglide/src/routeTree.gen.ts | 86 ++++++++ .../react/start-i18n-paraglide/src/router.tsx | 18 ++ .../src/routes/__root.tsx | 95 +++++++++ .../start-i18n-paraglide/src/routes/about.tsx | 10 + .../start-i18n-paraglide/src/routes/index.tsx | 33 +++ .../react/start-i18n-paraglide/src/server.ts | 8 + .../react/start-i18n-paraglide/src/styles.css | 1 + .../src/utils/prerender.ts | 8 + .../start-i18n-paraglide/src/utils/seo.ts | 33 +++ .../src/utils/translated-pathnames.ts | 56 +++++ .../react/start-i18n-paraglide/tsconfig.json | 29 +++ .../react/start-i18n-paraglide/vite.config.ts | 40 ++++ 52 files changed, 1316 insertions(+) create mode 100644 examples/react/i18n-paraglide/.gitignore create mode 100644 examples/react/i18n-paraglide/.vscode/settings.json create mode 100644 examples/react/i18n-paraglide/README.md create mode 100644 examples/react/i18n-paraglide/index.html create mode 100644 examples/react/i18n-paraglide/messages/de.json create mode 100644 examples/react/i18n-paraglide/messages/en.json create mode 100644 examples/react/i18n-paraglide/package.json create mode 100644 examples/react/i18n-paraglide/project.inlang/.gitignore create mode 100644 examples/react/i18n-paraglide/project.inlang/project_id create mode 100644 examples/react/i18n-paraglide/project.inlang/settings.json create mode 100644 examples/react/i18n-paraglide/public/favicon.ico create mode 100644 examples/react/i18n-paraglide/public/logo192.png create mode 100644 examples/react/i18n-paraglide/public/logo512.png create mode 100644 examples/react/i18n-paraglide/public/manifest.json create mode 100644 examples/react/i18n-paraglide/public/robots.txt create mode 100644 examples/react/i18n-paraglide/src/logo.svg create mode 100644 examples/react/i18n-paraglide/src/main.tsx create mode 100644 examples/react/i18n-paraglide/src/routeTree.gen.ts create mode 100644 examples/react/i18n-paraglide/src/routes/__root.tsx create mode 100644 examples/react/i18n-paraglide/src/routes/about.tsx create mode 100644 examples/react/i18n-paraglide/src/routes/index.tsx create mode 100644 examples/react/i18n-paraglide/src/styles.css create mode 100644 examples/react/i18n-paraglide/tsconfig.json create mode 100644 examples/react/i18n-paraglide/vite.config.ts create mode 100644 examples/react/start-i18n-paraglide/.gitignore create mode 100644 examples/react/start-i18n-paraglide/.vscode/extensions.json create mode 100644 examples/react/start-i18n-paraglide/.vscode/settings.json create mode 100644 examples/react/start-i18n-paraglide/README.md create mode 100644 examples/react/start-i18n-paraglide/messages/de.json create mode 100644 examples/react/start-i18n-paraglide/messages/en.json create mode 100644 examples/react/start-i18n-paraglide/package.json create mode 100644 examples/react/start-i18n-paraglide/project.inlang/.gitignore create mode 100644 examples/react/start-i18n-paraglide/project.inlang/project_id create mode 100644 examples/react/start-i18n-paraglide/project.inlang/settings.json create mode 100644 examples/react/start-i18n-paraglide/public/favicon.ico create mode 100644 examples/react/start-i18n-paraglide/public/logo192.png create mode 100644 examples/react/start-i18n-paraglide/public/logo512.png create mode 100644 examples/react/start-i18n-paraglide/public/manifest.json create mode 100644 examples/react/start-i18n-paraglide/public/robots.txt create mode 100644 examples/react/start-i18n-paraglide/src/logo.svg create mode 100644 examples/react/start-i18n-paraglide/src/routeTree.gen.ts create mode 100644 examples/react/start-i18n-paraglide/src/router.tsx create mode 100644 examples/react/start-i18n-paraglide/src/routes/__root.tsx create mode 100644 examples/react/start-i18n-paraglide/src/routes/about.tsx create mode 100644 examples/react/start-i18n-paraglide/src/routes/index.tsx create mode 100644 examples/react/start-i18n-paraglide/src/server.ts create mode 100644 examples/react/start-i18n-paraglide/src/styles.css create mode 100644 examples/react/start-i18n-paraglide/src/utils/prerender.ts create mode 100644 examples/react/start-i18n-paraglide/src/utils/seo.ts create mode 100644 examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts create mode 100644 examples/react/start-i18n-paraglide/tsconfig.json create mode 100644 examples/react/start-i18n-paraglide/vite.config.ts diff --git a/examples/react/i18n-paraglide/.gitignore b/examples/react/i18n-paraglide/.gitignore new file mode 100644 index 00000000000..693a705328d --- /dev/null +++ b/examples/react/i18n-paraglide/.gitignore @@ -0,0 +1,9 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +count.txt +.env +.nitro +.tanstack diff --git a/examples/react/i18n-paraglide/.vscode/settings.json b/examples/react/i18n-paraglide/.vscode/settings.json new file mode 100644 index 00000000000..00b5278e580 --- /dev/null +++ b/examples/react/i18n-paraglide/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/examples/react/i18n-paraglide/README.md b/examples/react/i18n-paraglide/README.md new file mode 100644 index 00000000000..deec86a1c7f --- /dev/null +++ b/examples/react/i18n-paraglide/README.md @@ -0,0 +1,141 @@ +# TanSTack Router example + +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). + +## Getting started + +1. Init Paraglide JS + +```bash +npx @inlang/paraglide-js@latest init +``` + +2. Add the vite plugin to your `vite.config.ts`: + +```diff +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { tanstackRouter } from '@tanstack/router-plugin/vite' ++import { paraglideVitePlugin } from "@inlang/paraglide-js"; + +export default defineConfig({ + plugins: [ + tanstackRouter({ target: 'react', autoCodeSplitting: true }), + react(), ++ paraglideVitePlugin({ ++ project: "./project.inlang", ++ outdir: "./app/paraglide", ++ }), + ], +}); +``` + +3. Done :) + +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. + +## Rewrite URL + +If you want to handle how the URL looks when the user changes the locale, you can rewrite the URL in the router. + +```diff +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; ++import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; + +const router = createRouter({ + routeTree, ++ rewrite: { ++ input: ({ url }) => deLocalizeUrl(url), ++ output: ({ url }) => localizeUrl(url), + }, +}); +``` + +In `__root.tsx` add a `beforeLoad` hook to check if the user should be redirected and set the html `lang` attribute. + +```ts +import { shouldRedirect } from "../paraglide/runtime"; + +export const Route = createRootRoute({ + beforeLoad: async () => { + document.documentElement.setAttribute("lang", getLocale()); + + const decision = await shouldRedirect({ url: window.location.href }); + + if (decision.redirectUrl) { + throw redirect({ href: decision.redirectUrl.href }); + } + }, + ... +}); +``` + +## Typesafe translated pathnames + +If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. + +```ts +import { Locale } from "@/paraglide/runtime"; +import { FileRoutesByTo } from "../routeTree.gen"; + +type RoutePath = keyof FileRoutesByTo; + +const excludedPaths = ["admin", "docs", "api"] as const; + +type PublicRoutePath = Exclude< + RoutePath, + `${string}${(typeof excludedPaths)[number]}${string}` +>; + +type TranslatedPathname = { + pattern: string; + localized: Array<[Locale, string]>; +}; + +function toUrlPattern(path: string) { + return ( + path + // catch-all + .replace(/\/\$$/, "/:path(.*)?") + // optional parameters: {-$param} + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + // named parameters: $param + .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + // remove trailing slash + .replace(/\/+$/, "") + ); +} + +function createTranslatedPathnames( + input: Record> +): TranslatedPathname[] { + return Object.entries(input).map(([pattern, locales]) => ({ + pattern: toUrlPattern(pattern), + localized: Object.entries(locales).map( + ([locale, path]) => + [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ + Locale, + string, + ] + ), + })); +} + +export const translatedPathnames = createTranslatedPathnames({ + "/": { + en: "/", + de: "/", + }, + "/about": { + en: "/about", + de: "/ueber", + }, +}); +``` + +And import into the Paraglide Vite plguin. + +## Server side rendering + +For server side rerdering, check out the [TanStack Start guide](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/tanstack-start). diff --git a/examples/react/i18n-paraglide/index.html b/examples/react/i18n-paraglide/index.html new file mode 100644 index 00000000000..b3e1dff9722 --- /dev/null +++ b/examples/react/i18n-paraglide/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Create TanStack App - i18n-paraglide + + +
+ + + diff --git a/examples/react/i18n-paraglide/messages/de.json b/examples/react/i18n-paraglide/messages/de.json new file mode 100644 index 00000000000..d980f6f76bb --- /dev/null +++ b/examples/react/i18n-paraglide/messages/de.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "example_message": "Guten Tag {username}", + "hello_about": "Hallo /ueber!", + "home_page": "Startseite", + "about_page": "Über uns" +} diff --git a/examples/react/i18n-paraglide/messages/en.json b/examples/react/i18n-paraglide/messages/en.json new file mode 100644 index 00000000000..8a85ed03fc9 --- /dev/null +++ b/examples/react/i18n-paraglide/messages/en.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "example_message": "Hello world {username}", + "hello_about": "Hello /about!", + "home_page": "Home page", + "about_page": "About page" +} diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json new file mode 100644 index 00000000000..57ee817fc91 --- /dev/null +++ b/examples/react/i18n-paraglide/package.json @@ -0,0 +1,29 @@ +{ + "name": "tanstack-router-i18n-paraglide", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "start": "vite --port 3000", + "build": "vite build && tsc", + "serve": "vite preview", + "test": "vitest run" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.13", + "@tanstack/react-router": "^1.132.2", + "@tanstack/router-plugin": "^1.132.3", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "tailwindcss": "^4.1.13" + }, + "devDependencies": { + "@types/node": "^22.18.6", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "typescript": "^5.9.2", + "vite": "^7.1.7", + "@inlang/paraglide-js": "^2.4.0" + } +} diff --git a/examples/react/i18n-paraglide/project.inlang/.gitignore b/examples/react/i18n-paraglide/project.inlang/.gitignore new file mode 100644 index 00000000000..5e465967597 --- /dev/null +++ b/examples/react/i18n-paraglide/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/examples/react/i18n-paraglide/project.inlang/project_id b/examples/react/i18n-paraglide/project.inlang/project_id new file mode 100644 index 00000000000..db50da9f95b --- /dev/null +++ b/examples/react/i18n-paraglide/project.inlang/project_id @@ -0,0 +1 @@ +72AJsIR0c0ewzkN33F \ No newline at end of file diff --git a/examples/react/i18n-paraglide/project.inlang/settings.json b/examples/react/i18n-paraglide/project.inlang/settings.json new file mode 100644 index 00000000000..62d77adb682 --- /dev/null +++ b/examples/react/i18n-paraglide/project.inlang/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "locales": [ + "en", + "de" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + } +} \ No newline at end of file diff --git a/examples/react/i18n-paraglide/public/favicon.ico b/examples/react/i18n-paraglide/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/react/i18n-paraglide/public/logo192.png b/examples/react/i18n-paraglide/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/examples/react/i18n-paraglide/public/manifest.json b/examples/react/i18n-paraglide/public/manifest.json new file mode 100644 index 00000000000..078ef501162 --- /dev/null +++ b/examples/react/i18n-paraglide/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "TanStack App", + "name": "Create TanStack App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/react/i18n-paraglide/public/robots.txt b/examples/react/i18n-paraglide/public/robots.txt new file mode 100644 index 00000000000..e9e57dc4d41 --- /dev/null +++ b/examples/react/i18n-paraglide/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/react/i18n-paraglide/src/logo.svg b/examples/react/i18n-paraglide/src/logo.svg new file mode 100644 index 00000000000..fe53fe8d0d2 --- /dev/null +++ b/examples/react/i18n-paraglide/src/logo.svg @@ -0,0 +1,12 @@ + + + logo + + \ No newline at end of file diff --git a/examples/react/i18n-paraglide/src/main.tsx b/examples/react/i18n-paraglide/src/main.tsx new file mode 100644 index 00000000000..65d3d911755 --- /dev/null +++ b/examples/react/i18n-paraglide/src/main.tsx @@ -0,0 +1,40 @@ +import { StrictMode } from "react"; +import ReactDOM from "react-dom/client"; +import { RouterProvider, createRouter } from "@tanstack/react-router"; +import "./styles.css"; +// Import the generated route tree +import { routeTree } from "./routeTree.gen"; +import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; + +// Create a new router instance +const router = createRouter({ + routeTree, + context: {}, + defaultPreload: "intent", + scrollRestoration: true, + defaultStructuralSharing: true, + defaultPreloadStaleTime: 0, + + rewrite: { + input: ({ url }) => deLocalizeUrl(url), + output: ({ url }) => localizeUrl(url), + }, +}); + +// Register the router instance for type safety +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } +} + +// Render the app +const rootElement = document.getElementById("app"); +if (rootElement && !rootElement.innerHTML) { + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + ); +} diff --git a/examples/react/i18n-paraglide/src/routeTree.gen.ts b/examples/react/i18n-paraglide/src/routeTree.gen.ts new file mode 100644 index 00000000000..59499d9fbf3 --- /dev/null +++ b/examples/react/i18n-paraglide/src/routeTree.gen.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as AboutRouteImport } from './routes/about' +import { Route as IndexRouteImport } from './routes/index' + +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/about' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/about' + id: '__root__' | '/' | '/about' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AboutRoute: typeof AboutRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AboutRoute: AboutRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/examples/react/i18n-paraglide/src/routes/__root.tsx b/examples/react/i18n-paraglide/src/routes/__root.tsx new file mode 100644 index 00000000000..d02ebd5f714 --- /dev/null +++ b/examples/react/i18n-paraglide/src/routes/__root.tsx @@ -0,0 +1,70 @@ +import { + Link, + Outlet, + createRootRoute, + redirect, +} from "@tanstack/react-router"; +import { + getLocale, + locales, + setLocale, + shouldRedirect, +} from "@/paraglide/runtime"; +import { m } from "@/paraglide/messages"; + +export const Route = createRootRoute({ + beforeLoad: async () => { + document.documentElement.setAttribute("lang", getLocale()); + + const decision = await shouldRedirect({ url: window.location.href }); + + if (decision.redirectUrl) { + throw redirect({ href: decision.redirectUrl.href }); + } + }, + component: () => ( + <> +
+
+ + {m.home_page()} + + + + {m.about_page()} + +
+ +
+ {locales.map((locale) => ( + + ))} +
+
+ +
+ +
+ +
+ + ), +}); diff --git a/examples/react/i18n-paraglide/src/routes/about.tsx b/examples/react/i18n-paraglide/src/routes/about.tsx new file mode 100644 index 00000000000..1ae1d7ef9ff --- /dev/null +++ b/examples/react/i18n-paraglide/src/routes/about.tsx @@ -0,0 +1,10 @@ +import { m } from "@/paraglide/messages"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/about")({ + component: RouteComponent, +}); + +function RouteComponent() { + return
{m.hello_about()}
; +} diff --git a/examples/react/i18n-paraglide/src/routes/index.tsx b/examples/react/i18n-paraglide/src/routes/index.tsx new file mode 100644 index 00000000000..87ca08bc711 --- /dev/null +++ b/examples/react/i18n-paraglide/src/routes/index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { m } from "@/paraglide/messages"; + +export const Route = createFileRoute("/")({ + component: App, +}); + +function App() { + return ( +
+

+ {m.example_message({ + username: "TanStack Router!", + })} +

+
+ ); +} diff --git a/examples/react/i18n-paraglide/src/styles.css b/examples/react/i18n-paraglide/src/styles.css new file mode 100644 index 00000000000..f1d8c73cdcf --- /dev/null +++ b/examples/react/i18n-paraglide/src/styles.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/examples/react/i18n-paraglide/tsconfig.json b/examples/react/i18n-paraglide/tsconfig.json new file mode 100644 index 00000000000..fded1fca188 --- /dev/null +++ b/examples/react/i18n-paraglide/tsconfig.json @@ -0,0 +1,29 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "target": "ES2022", + "jsx": "react-jsx", + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "allowJs": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + /* Linting */ + "skipLibCheck": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/examples/react/i18n-paraglide/vite.config.ts b/examples/react/i18n-paraglide/vite.config.ts new file mode 100644 index 00000000000..8d10ed74cf7 --- /dev/null +++ b/examples/react/i18n-paraglide/vite.config.ts @@ -0,0 +1,49 @@ +import { defineConfig } from "vite"; +import viteReact from "@vitejs/plugin-react"; +import { tanstackRouter } from "@tanstack/router-plugin/vite"; +import { resolve } from "node:path"; +import { paraglideVitePlugin } from "@inlang/paraglide-js"; +import tailwindcss from "@tailwindcss/vite"; + +export default defineConfig({ + plugins: [ + tailwindcss(), + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./src/paraglide", + outputStructure: "message-modules", + cookieName: "PARAGLIDE_LOCALE", + strategy: ["url", "cookie", "preferredLanguage", "baseLocale"], + urlPatterns: [ + { + pattern: "/", + localized: [ + ["en", "/"], + ["de", "/de"], + ], + }, + { + pattern: "/about", + localized: [ + ["en", "/about"], + ["de", "/de/ueber"], + ], + }, + { + pattern: "/:path(.*)?", + localized: [ + ["en", "/:path(.*)?"], + ["de", "/de/:path(.*)?"], + ], + }, + ], + }), + tanstackRouter({ autoCodeSplitting: true }), + viteReact(), + ], + resolve: { + alias: { + "@": resolve(__dirname, "./src"), + }, + }, +}); diff --git a/examples/react/start-i18n-paraglide/.gitignore b/examples/react/start-i18n-paraglide/.gitignore new file mode 100644 index 00000000000..eb0412b5f5b --- /dev/null +++ b/examples/react/start-i18n-paraglide/.gitignore @@ -0,0 +1,10 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +count.txt +.env +.nitro +.tanstack +.output diff --git a/examples/react/start-i18n-paraglide/.vscode/extensions.json b/examples/react/start-i18n-paraglide/.vscode/extensions.json new file mode 100644 index 00000000000..116d6852a68 --- /dev/null +++ b/examples/react/start-i18n-paraglide/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "inlang.vs-code-extension" + ] +} \ No newline at end of file diff --git a/examples/react/start-i18n-paraglide/.vscode/settings.json b/examples/react/start-i18n-paraglide/.vscode/settings.json new file mode 100644 index 00000000000..00b5278e580 --- /dev/null +++ b/examples/react/start-i18n-paraglide/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/examples/react/start-i18n-paraglide/README.md b/examples/react/start-i18n-paraglide/README.md new file mode 100644 index 00000000000..0770dc86a80 --- /dev/null +++ b/examples/react/start-i18n-paraglide/README.md @@ -0,0 +1,196 @@ +# TanSTack Start example + +This example shows how to use Paraglide with TanStack Start. The source code can be found [here](https://github.com/opral/monorepo/tree/main/inlang/packages/paraglide/paraglide-js/examples/tanstack-start). + +## Getting started + +1. Init Paraglide JS + +```bash +npx @inlang/paraglide-js@latest init +``` + +2. Add the vite plugin to your `vite.config.ts`: + +```diff +import { defineConfig } from 'vite' +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import react from '@vitejs/plugin-react' ++import { paraglideVitePlugin } from "@inlang/paraglide-js"; + +export default defineConfig({ + plugins: [ + tanstackStart(), + react(), ++ paraglideVitePlugin({ ++ project: "./project.inlang", ++ outdir: "./app/paraglide", ++ outputStructure: "message-modules", ++ cookieName: "PARAGLIDE_LOCALE", ++ strategy: ["url", "cookie", "preferredLanguage", "baseLocale"], ++ urlPatterns: [ ++ { ++ pattern: "/:path(.*)?", ++ localized: [ ++ ["en", "/en/:path(.*)?"], ++ ], ++ }, ++ ], ++ }), + ], +}); +``` + +3. Done :) + +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. + +## Rewrite URL + +If you want to handle how the URL looks when the user changes the locale, you can rewrite the URL in the router. + +```diff +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; ++import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; + +const router = createRouter({ + routeTree, ++ rewrite: { ++ input: ({ url }) => deLocalizeUrl(url), ++ output: ({ url }) => localizeUrl(url), + }, +}); +``` + +In `server.ts` intercept the request with the paraglideMiddleware. + +```ts +import { paraglideMiddleware } from "./paraglide/server.js"; +import handler from "@tanstack/react-start/server-entry"; + +export default { + fetch(req: Request): Promise { + return paraglideMiddleware(req, ({ request }) => handler.fetch(request)); + }, +}; +``` + +In `__root.tsx` add change the html lang attribute to the current locale. + +```tsx +import { getLocale } from "../paraglide/runtime.js"; + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + + {children} + + + + ); +} +``` + +## Offline redirect + +If you have an application that needs to work offline, you will need to handle the redirect in the client like this. + +```ts +import { shouldRedirect } from "../paraglide/runtime"; + +export const Route = createRootRoute({ + beforeLoad: async () => { + const decision = await shouldRedirect({ url: window.location.href }); + + if (decision.redirectUrl) { + throw redirect({ href: decision.redirectUrl.href }); + } + }, + ... +}); +``` + +## Typesafe translated pathnames + +If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. + +```ts +import { Locale } from "@/paraglide/runtime"; +import { FileRoutesByTo } from "../routeTree.gen"; + +type RoutePath = keyof FileRoutesByTo; + +const excludedPaths = ["admin", "docs", "api"] as const; + +type PublicRoutePath = Exclude< + RoutePath, + `${string}${(typeof excludedPaths)[number]}${string}` +>; + +type TranslatedPathname = { + pattern: string; + localized: Array<[Locale, string]>; +}; + +function toUrlPattern(path: string) { + return ( + path + // catch-all + .replace(/\/\$$/, "/:path(.*)?") + // optional parameters: {-$param} + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + // named parameters: $param + .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + // remove trailing slash + .replace(/\/+$/, "") + ); +} + +function createTranslatedPathnames( + input: Record> +): TranslatedPathname[] { + return Object.entries(input).map(([pattern, locales]) => ({ + pattern: toUrlPattern(pattern), + localized: Object.entries(locales).map( + ([locale, path]) => + [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ + Locale, + string, + ] + ), + })); +} + +export const translatedPathnames = createTranslatedPathnames({ + "/": { + en: "/", + de: "/", + }, + "/about": { + en: "/about", + de: "/ueber", + }, +}); +``` + +And import into the Paraglide Vite plguin. + +# Prerender routes + +You can use use the `localizeHref` function to map the routes to localized versions and import into the pages option in the TanStack Start plugin. For this to work you will need to compile paraglide before the build with the CLI. + +```ts +import { localizeHref } from "./paraglide/runtime"; + +export const prerenderRoutes = ["/", "/about"].map((path) => ({ + path: localizeHref(path), + prerender: { + enabled: true, + }, +})); +``` diff --git a/examples/react/start-i18n-paraglide/messages/de.json b/examples/react/start-i18n-paraglide/messages/de.json new file mode 100644 index 00000000000..2c2167c3ca0 --- /dev/null +++ b/examples/react/start-i18n-paraglide/messages/de.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "example_message": "Guten Tag {username}", + "server_message": "Server Nachricht {emoji}", + "about_message": "Über uns", + "home_page": "Startseite", + "about_page": "Über uns" +} diff --git a/examples/react/start-i18n-paraglide/messages/en.json b/examples/react/start-i18n-paraglide/messages/en.json new file mode 100644 index 00000000000..204789de75c --- /dev/null +++ b/examples/react/start-i18n-paraglide/messages/en.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "example_message": "Hello world {username}", + "server_message": "Server message {emoji}", + "about_message": "About message", + "home_page": "Home page", + "about_page": "About page" +} diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json new file mode 100644 index 00000000000..c4687ce4b3d --- /dev/null +++ b/examples/react/start-i18n-paraglide/package.json @@ -0,0 +1,29 @@ +{ + "name": "tanstack-start-i18n-paraglide", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --port 3000", + "start": "node .output/server/index.mjs", + "build": "vite build", + "serve": "vite preview" + }, + "dependencies": { + "@tanstack/react-router": "^1.132.7", + "@tanstack/react-start": "^1.132.9", + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@types/node": "^22.18.6", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^4.7.0", + "typescript": "^5.9.2", + "vite": "^7.1.7", + "vite-tsconfig-paths": "^5.1.4", + "@tailwindcss/vite": "^4.1.13", + "tailwindcss": "^4.1.13", + "@inlang/paraglide-js": "2.4.0" + } +} diff --git a/examples/react/start-i18n-paraglide/project.inlang/.gitignore b/examples/react/start-i18n-paraglide/project.inlang/.gitignore new file mode 100644 index 00000000000..5e465967597 --- /dev/null +++ b/examples/react/start-i18n-paraglide/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/examples/react/start-i18n-paraglide/project.inlang/project_id b/examples/react/start-i18n-paraglide/project.inlang/project_id new file mode 100644 index 00000000000..a956b223fac --- /dev/null +++ b/examples/react/start-i18n-paraglide/project.inlang/project_id @@ -0,0 +1 @@ +UoZ15Q8qSGIbImRS3Y \ No newline at end of file diff --git a/examples/react/start-i18n-paraglide/project.inlang/settings.json b/examples/react/start-i18n-paraglide/project.inlang/settings.json new file mode 100644 index 00000000000..62d77adb682 --- /dev/null +++ b/examples/react/start-i18n-paraglide/project.inlang/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "locales": [ + "en", + "de" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + } +} \ No newline at end of file diff --git a/examples/react/start-i18n-paraglide/public/favicon.ico b/examples/react/start-i18n-paraglide/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 zcma);c{J4h9>;%nil|2-o+rCuEF-(I%-F}ijC~o(k~HKAkr0)!FCj~d>`RtpD?8b; zXOC1OD!V*IsqUwzbMF1)-gEDD=A573Z-&G7^LoAC9|WO7Xc0Cx1g^Zu0u_SjAPB
3vGa^W|sj)80f#V0@M_CAZTIO(t--xg= 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/react/start-i18n-paraglide/public/logo192.png b/examples/react/start-i18n-paraglide/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/examples/react/start-i18n-paraglide/public/manifest.json b/examples/react/start-i18n-paraglide/public/manifest.json new file mode 100644 index 00000000000..078ef501162 --- /dev/null +++ b/examples/react/start-i18n-paraglide/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "TanStack App", + "name": "Create TanStack App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/react/start-i18n-paraglide/public/robots.txt b/examples/react/start-i18n-paraglide/public/robots.txt new file mode 100644 index 00000000000..e9e57dc4d41 --- /dev/null +++ b/examples/react/start-i18n-paraglide/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/react/start-i18n-paraglide/src/logo.svg b/examples/react/start-i18n-paraglide/src/logo.svg new file mode 100644 index 00000000000..fe53fe8d0d2 --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/logo.svg @@ -0,0 +1,12 @@ + + + logo + + \ No newline at end of file diff --git a/examples/react/start-i18n-paraglide/src/routeTree.gen.ts b/examples/react/start-i18n-paraglide/src/routeTree.gen.ts new file mode 100644 index 00000000000..421daf2790a --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/routeTree.gen.ts @@ -0,0 +1,86 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as AboutRouteImport } from './routes/about' +import { Route as IndexRouteImport } from './routes/index' + +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/about' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/about' + id: '__root__' | '/' | '/about' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AboutRoute: typeof AboutRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AboutRoute: AboutRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/react/start-i18n-paraglide/src/router.tsx b/examples/react/start-i18n-paraglide/src/router.tsx new file mode 100644 index 00000000000..0e4ef34366f --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/router.tsx @@ -0,0 +1,18 @@ +import { createRouter } from "@tanstack/react-router"; + +// Import the generated route tree +import { routeTree } from "./routeTree.gen"; +import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime"; + +// Create a new router instance +export const getRouter = () => { + return createRouter({ + routeTree, + scrollRestoration: true, + defaultPreloadStaleTime: 0, + rewrite: { + input: ({ url }) => deLocalizeUrl(url), + output: ({ url }) => localizeUrl(url), + }, + }); +}; diff --git a/examples/react/start-i18n-paraglide/src/routes/__root.tsx b/examples/react/start-i18n-paraglide/src/routes/__root.tsx new file mode 100644 index 00000000000..4992ddf54e2 --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/routes/__root.tsx @@ -0,0 +1,95 @@ +import { + HeadContent, + Link, + Scripts, + createRootRoute, +} from "@tanstack/react-router"; +import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; +import { TanstackDevtools } from "@tanstack/react-devtools"; +import styles from "../styles.css?url"; +import { getLocale, locales, setLocale } from "@/paraglide/runtime"; +import { m } from "@/paraglide/messages"; + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: "utf-8", + }, + { + name: "viewport", + content: "width=device-width, initial-scale=1", + }, + { + title: "TanStack Start Starter", + }, + ], + links: [{ rel: "stylesheet", href: styles }], + }), + + shellComponent: RootDocument, +}); + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
+
+ + {m.home_page()} + + + + {m.about_page()} + +
+ +
+ {locales.map((locale) => ( + + ))} +
+
+ +
+ +
{children}
+ + , + }, + ]} + /> + + + + ); +} diff --git a/examples/react/start-i18n-paraglide/src/routes/about.tsx b/examples/react/start-i18n-paraglide/src/routes/about.tsx new file mode 100644 index 00000000000..50145df75ed --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/routes/about.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { m } from "@/paraglide/messages"; + +export const Route = createFileRoute("/about")({ + component: RouteComponent, +}); + +function RouteComponent() { + return
{m.about_message()}
; +} diff --git a/examples/react/start-i18n-paraglide/src/routes/index.tsx b/examples/react/start-i18n-paraglide/src/routes/index.tsx new file mode 100644 index 00000000000..46a67dfef30 --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/routes/index.tsx @@ -0,0 +1,33 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { m } from "@/paraglide/messages.js"; +import { getLocale } from "@/paraglide/runtime.js"; +import { createServerFn } from "@tanstack/react-start"; + +const getServerMessage = createServerFn() + .inputValidator((emoji: string) => emoji) + .handler((ctx) => { + return m.server_message({ emoji: ctx.data }); + }); + +export const Route = createFileRoute("/")({ + component: Home, + loader: () => { + return { + localeFromLoader: getLocale(), + messageFromLoader: m.example_message({ username: "John Doe" }), + serverFunctionMessage: getServerMessage({ data: "📩" }), + }; + }, +}); + +function Home() { + const { serverFunctionMessage, messageFromLoader, localeFromLoader } = + Route.useLoaderData(); + return ( +
+

Message from loader: {messageFromLoader}

+

Server function message: {serverFunctionMessage}:

+

{m.example_message({ username: "John Doe" })}

+
+ ); +} diff --git a/examples/react/start-i18n-paraglide/src/server.ts b/examples/react/start-i18n-paraglide/src/server.ts new file mode 100644 index 00000000000..d22159ef4cd --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/server.ts @@ -0,0 +1,8 @@ +import { paraglideMiddleware } from "./paraglide/server.js"; +import handler from "@tanstack/react-start/server-entry"; + +export default { + fetch(req: Request): Promise { + return paraglideMiddleware(req, ({ request }) => handler.fetch(request)); + }, +}; diff --git a/examples/react/start-i18n-paraglide/src/styles.css b/examples/react/start-i18n-paraglide/src/styles.css new file mode 100644 index 00000000000..f1d8c73cdcf --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/styles.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/examples/react/start-i18n-paraglide/src/utils/prerender.ts b/examples/react/start-i18n-paraglide/src/utils/prerender.ts new file mode 100644 index 00000000000..52104cd4914 --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/utils/prerender.ts @@ -0,0 +1,8 @@ +import { localizeHref } from "../paraglide/runtime"; + +export const prerenderRoutes = ["/", "/about"].map((path) => ({ + path: localizeHref(path), + prerender: { + enabled: true, + }, +})); diff --git a/examples/react/start-i18n-paraglide/src/utils/seo.ts b/examples/react/start-i18n-paraglide/src/utils/seo.ts new file mode 100644 index 00000000000..8b0957ebc1c --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string; + description?: string; + image?: string; + keywords?: string; +}) => { + const tags = [ + { title }, + { name: "description", content: description }, + { name: "keywords", content: keywords }, + { name: "twitter:title", content: title }, + { name: "twitter:description", content: description }, + { name: "twitter:creator", content: "@tannerlinsley" }, + { name: "twitter:site", content: "@tannerlinsley" }, + { name: "og:type", content: "website" }, + { name: "og:title", content: title }, + { name: "og:description", content: description }, + ...(image + ? [ + { name: "twitter:image", content: image }, + { name: "twitter:card", content: "summary_large_image" }, + { name: "og:image", content: image }, + ] + : []), + ]; + + return tags; +}; diff --git a/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts b/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts new file mode 100644 index 00000000000..18322410923 --- /dev/null +++ b/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts @@ -0,0 +1,56 @@ +import { Locale } from "@/paraglide/runtime"; +import { FileRoutesByTo } from "../routeTree.gen"; + +type RoutePath = keyof FileRoutesByTo; + +const excludedPaths = ["admin", "docs", "api"] as const; + +type PublicRoutePath = Exclude< + RoutePath, + `${string}${(typeof excludedPaths)[number]}${string}` +>; + +type TranslatedPathname = { + pattern: string; + localized: Array<[Locale, string]>; +}; + +function toUrlPattern(path: string) { + return ( + path + // catch-all + .replace(/\/\$$/, "/:path(.*)?") + // optional parameters: {-$param} + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + // named parameters: $param + .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + // remove trailing slash + .replace(/\/+$/, "") + ); +} + +function createTranslatedPathnames( + input: Record> +): TranslatedPathname[] { + return Object.entries(input).map(([pattern, locales]) => ({ + pattern: toUrlPattern(pattern), + localized: Object.entries(locales).map( + ([locale, path]) => + [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ + Locale, + string, + ] + ), + })); +} + +export const translatedPathnames = createTranslatedPathnames({ + "/": { + en: "/", + de: "/", + }, + "/about": { + en: "/about", + de: "/ueber", + }, +}); diff --git a/examples/react/start-i18n-paraglide/tsconfig.json b/examples/react/start-i18n-paraglide/tsconfig.json new file mode 100644 index 00000000000..3e42c72626a --- /dev/null +++ b/examples/react/start-i18n-paraglide/tsconfig.json @@ -0,0 +1,29 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "target": "ES2022", + "jsx": "react-jsx", + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "allowJs": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": false, + "noEmit": true, + + /* Linting */ + "skipLibCheck": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/examples/react/start-i18n-paraglide/vite.config.ts b/examples/react/start-i18n-paraglide/vite.config.ts new file mode 100644 index 00000000000..a90c761355b --- /dev/null +++ b/examples/react/start-i18n-paraglide/vite.config.ts @@ -0,0 +1,40 @@ +import { paraglideVitePlugin } from "@inlang/paraglide-js"; +import { defineConfig } from "vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import viteReact from "@vitejs/plugin-react"; +import viteTsConfigPaths from "vite-tsconfig-paths"; +import tailwindcss from "@tailwindcss/vite"; + +const config = defineConfig({ + plugins: [ + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./src/paraglide", + outputStructure: "message-modules", + cookieName: "PARAGLIDE_LOCALE", + strategy: ["url", "cookie", "preferredLanguage", "baseLocale"], + urlPatterns: [ + { + pattern: "/about", + localized: [ + ["en", "/en/about"], + ["de", "/de/ueber"], + ], + }, + { + pattern: "/:path(.*)?", + localized: [ + ["en", "/en/:path(.*)?"], + ["de", "/de/:path(.*)?"], + ], + }, + ], + }), + viteTsConfigPaths(), + tanstackStart(), + viteReact(), + tailwindcss(), + ], +}); + +export default config; From 8f457b4ad445334aa94a650b51ff025627b69666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20M=C3=BChlbauer?= Date: Fri, 26 Sep 2025 12:39:30 -0300 Subject: [PATCH 2/7] fix link in readme --- examples/react/i18n-paraglide/README.md | 2 +- examples/react/start-i18n-paraglide/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/react/i18n-paraglide/README.md b/examples/react/i18n-paraglide/README.md index deec86a1c7f..036deea276c 100644 --- a/examples/react/i18n-paraglide/README.md +++ b/examples/react/i18n-paraglide/README.md @@ -32,7 +32,7 @@ export default defineConfig({ 3. Done :) -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. +Run the app and start translating. See the [basics documentation](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/basics) for information on how to use Paraglide's messages, parameters, and locale management. ## Rewrite URL diff --git a/examples/react/start-i18n-paraglide/README.md b/examples/react/start-i18n-paraglide/README.md index 0770dc86a80..453f9cd7bdb 100644 --- a/examples/react/start-i18n-paraglide/README.md +++ b/examples/react/start-i18n-paraglide/README.md @@ -43,7 +43,7 @@ export default defineConfig({ 3. Done :) -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. +Run the app and start translating. See the [basics documentation](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/basics) for information on how to use Paraglide's messages, parameters, and locale management. ## Rewrite URL From e82dbd655bcd807a04ab250f684cf132c55d7d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20M=C3=BChlbauer?= Date: Fri, 26 Sep 2025 12:58:45 -0300 Subject: [PATCH 3/7] fix tantsack naming in readme --- examples/react/i18n-paraglide/README.md | 2 +- examples/react/start-i18n-paraglide/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/react/i18n-paraglide/README.md b/examples/react/i18n-paraglide/README.md index 036deea276c..3b2cfa89e3e 100644 --- a/examples/react/i18n-paraglide/README.md +++ b/examples/react/i18n-paraglide/README.md @@ -1,4 +1,4 @@ -# TanSTack Router example +# TanStack Router example 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). diff --git a/examples/react/start-i18n-paraglide/README.md b/examples/react/start-i18n-paraglide/README.md index 453f9cd7bdb..7a52f61aa08 100644 --- a/examples/react/start-i18n-paraglide/README.md +++ b/examples/react/start-i18n-paraglide/README.md @@ -1,4 +1,4 @@ -# TanSTack Start example +# TanStack Start example This example shows how to use Paraglide with TanStack Start. The source code can be found [here](https://github.com/opral/monorepo/tree/main/inlang/packages/paraglide/paraglide-js/examples/tanstack-start). From ca6ebcc2439936a651f2316ea71b2ed6569d01c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20M=C3=BChlbauer?= Date: Fri, 26 Sep 2025 13:01:29 -0300 Subject: [PATCH 4/7] add missing devtools package --- examples/react/start-i18n-paraglide/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index c4687ce4b3d..8d4d3d5e272 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -9,7 +9,9 @@ "serve": "vite preview" }, "dependencies": { + "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.132.7", + "@tanstack/react-router-devtools": "^1.132.7", "@tanstack/react-start": "^1.132.9", "react": "^19.1.1", "react-dom": "^19.1.1" From 3f37c7aab9c00813e56f4aee28b224a91a4db7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20M=C3=BChlbauer?= Date: Fri, 26 Sep 2025 13:03:02 -0300 Subject: [PATCH 5/7] TanStackDevtools --- examples/react/start-i18n-paraglide/src/routes/__root.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/react/start-i18n-paraglide/src/routes/__root.tsx b/examples/react/start-i18n-paraglide/src/routes/__root.tsx index 4992ddf54e2..153d90381bd 100644 --- a/examples/react/start-i18n-paraglide/src/routes/__root.tsx +++ b/examples/react/start-i18n-paraglide/src/routes/__root.tsx @@ -5,7 +5,7 @@ import { createRootRoute, } from "@tanstack/react-router"; import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; -import { TanstackDevtools } from "@tanstack/react-devtools"; +import { TanStackDevtools } from "@tanstack/react-devtools"; import styles from "../styles.css?url"; import { getLocale, locales, setLocale } from "@/paraglide/runtime"; import { m } from "@/paraglide/messages"; @@ -77,7 +77,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
{children}
- Date: Sat, 27 Sep 2025 15:31:27 +0200 Subject: [PATCH 6/7] lockfile --- pnpm-lock.yaml | 286 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 276 insertions(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9e0b9a23f5..4bebe79f8d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3583,6 +3583,49 @@ importers: specifier: ^7.1.7 version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + examples/react/i18n-paraglide: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.13 + version: 4.1.13(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + tailwindcss: + specifier: ^4.1.13 + version: 4.1.13 + devDependencies: + '@inlang/paraglide-js': + specifier: ^2.4.0 + version: 2.4.0(babel-plugin-macros@3.1.0) + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@types/react': + specifier: ^19.0.8 + version: 19.0.8 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.0.3(@types/react@19.0.8) + '@vitejs/plugin-react': + specifier: ^5.0.3 + version: 5.0.3(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + typescript: + specifier: ^5.9.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + examples/react/kitchen-sink: dependencies: '@tanstack/react-router': @@ -5341,6 +5384,58 @@ importers: specifier: ^7.1.7 version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + examples/react/start-i18n-paraglide: + dependencies: + '@tanstack/react-devtools': + specifier: ^0.7.0 + version: 0.7.0(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.5) + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-router-devtools': + specifier: workspace:^ + version: link:../../../packages/react-router-devtools + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + devDependencies: + '@inlang/paraglide-js': + specifier: 2.4.0 + version: 2.4.0(babel-plugin-macros@3.1.0) + '@tailwindcss/vite': + specifier: ^4.1.13 + version: 4.1.13(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@types/react': + specifier: ^19.0.8 + version: 19.0.8 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.0.3(@types/react@19.0.8) + '@vitejs/plugin-react': + specifier: ^4.7.0 + version: 4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + tailwindcss: + specifier: ^4.1.13 + version: 4.1.13 + typescript: + specifier: ^5.9.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + examples/react/start-large: dependencies: '@tanstack/react-query': @@ -8948,6 +9043,17 @@ packages: cpu: [x64] os: [win32] + '@inlang/paraglide-js@2.4.0': + resolution: {integrity: sha512-T/m9uoev574/1JrhCnPcgK1xnAwkVMgaDev4LFthnmID8ubX2xjboSGO3IztwXWwO0aJoT1UJr89JCwjbwgnJQ==} + hasBin: true + + '@inlang/recommend-sherlock@0.2.1': + resolution: {integrity: sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==} + + '@inlang/sdk@2.4.9': + resolution: {integrity: sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ==} + engines: {node: '>=18.0.0'} + '@inquirer/confirm@5.1.4': resolution: {integrity: sha512-EsiT7K4beM5fN5Mz6j866EFA9+v9d5o9VUra3hrg8zY4GHmCS8b616FErbdo5eyKoVotBQkHzMIeeKYsKDStDw==} engines: {node: '>=18'} @@ -9057,6 +9163,13 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + '@lix-js/sdk@0.4.7': + resolution: {integrity: sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==} + engines: {node: '>=18'} + + '@lix-js/server-protocol-schema@0.1.1': + resolution: {integrity: sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==} + '@mapbox/node-pre-gyp@2.0.0': resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} engines: {node: '>=18'} @@ -10142,6 +10255,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.19': resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-beta.35': resolution: {integrity: sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==} @@ -10580,6 +10696,9 @@ packages: '@shikijs/vscode-textmate@10.0.1': resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@sinclair/typebox@0.31.28': + resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} + '@sinclair/typebox@0.34.38': resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} @@ -10698,6 +10817,10 @@ packages: '@speed-highlight/core@1.2.7': resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} + '@sqlite.org/sqlite-wasm@3.48.0-build4': + resolution: {integrity: sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ==} + hasBin: true + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -11606,6 +11729,12 @@ packages: peerDependencies: vite: ^7.1.7 + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^7.1.7 + '@vitejs/plugin-react@5.0.3': resolution: {integrity: sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -12024,6 +12153,9 @@ packages: resolution: {integrity: sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==} engines: {node: '>=0.10.0'} + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + asn1js@3.0.6: resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} engines: {node: '>=12.0.0'} @@ -12349,6 +12481,10 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + comment-json@4.2.5: + resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} + engines: {node: '>= 6'} + comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} @@ -12398,6 +12534,10 @@ packages: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -12619,6 +12759,14 @@ packages: decimal.js@10.5.0: resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dedent@1.7.0: resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} peerDependencies: @@ -13539,6 +13687,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-own-prop@2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -13656,6 +13808,10 @@ packages: httpxy@0.1.7: resolution: {integrity: sha512-pXNx8gnANKAndgga5ahefxc++tJvNL87CXoRwxn1cJE2ZkWEojF3tNfQIEhZX/vfpt+wzeAzpUI4qkediX1MLQ==} + human-id@4.1.1: + resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} + hasBin: true + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -13962,6 +14118,9 @@ packages: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + js-sha256@0.11.1: + resolution: {integrity: sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -14066,6 +14225,10 @@ packages: resolution: {integrity: sha512-zYEr/gh7uLW2l4su11bmQ2M9xLgQLjyvx58UyNM/6nuqyWFHPX5ktMjvpev3F8QWdjSsHUpnWew4PBCswBNuMQ==} engines: {node: '>=18'} + kysely@0.27.6: + resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} + engines: {node: '>=14.0.0'} + launch-editor@2.9.1: resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==} @@ -15269,6 +15432,10 @@ packages: renderkid@3.0.0: resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -15597,6 +15764,11 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sqlite-wasm-kysely@0.3.0: + resolution: {integrity: sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg==} + peerDependencies: + kysely: '*' + srvx@0.8.7: resolution: {integrity: sha512-g3+15LlwVOGL2QpoTPZlvRjg+9a5Tx/69CatXjFP6txvhIaW2FmGyzJfb8yft5wyfGddvJmP/Yx+e/uNDMRSLQ==} engines: {node: '>=20.16.0'} @@ -16277,6 +16449,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -17823,7 +17999,7 @@ snapshots: dependencies: '@eslint-react/eff': 1.26.2 '@typescript-eslint/utils': 8.23.0(eslint@9.22.0(jiti@2.6.0))(typescript@5.9.2) - picomatch: 4.0.2 + picomatch: 4.0.3 ts-pattern: 5.6.2 transitivePeerDependencies: - eslint @@ -18329,6 +18505,32 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true + '@inlang/paraglide-js@2.4.0(babel-plugin-macros@3.1.0)': + dependencies: + '@inlang/recommend-sherlock': 0.2.1 + '@inlang/sdk': 2.4.9(babel-plugin-macros@3.1.0) + commander: 11.1.0 + consola: 3.4.0 + json5: 2.2.3 + unplugin: 2.3.10 + urlpattern-polyfill: 10.1.0 + transitivePeerDependencies: + - babel-plugin-macros + + '@inlang/recommend-sherlock@0.2.1': + dependencies: + comment-json: 4.2.5 + + '@inlang/sdk@2.4.9(babel-plugin-macros@3.1.0)': + dependencies: + '@lix-js/sdk': 0.4.7(babel-plugin-macros@3.1.0) + '@sinclair/typebox': 0.31.28 + kysely: 0.27.6 + sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + uuid: 10.0.0 + transitivePeerDependencies: + - babel-plugin-macros + '@inquirer/confirm@5.1.4(@types/node@22.10.2)': dependencies: '@inquirer/core': 10.1.5(@types/node@22.10.2) @@ -18390,8 +18592,8 @@ snapshots: '@jridgewell/remapping@2.3.5': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} @@ -18399,8 +18601,8 @@ snapshots: '@jridgewell/source-map@0.3.6': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.0': {} @@ -18447,6 +18649,20 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} + '@lix-js/sdk@0.4.7(babel-plugin-macros@3.1.0)': + dependencies: + '@lix-js/server-protocol-schema': 0.1.1 + dedent: 1.5.1(babel-plugin-macros@3.1.0) + human-id: 4.1.1 + js-sha256: 0.11.1 + kysely: 0.27.6 + sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + uuid: 10.0.0 + transitivePeerDependencies: + - babel-plugin-macros + + '@lix-js/server-protocol-schema@0.1.1': {} + '@mapbox/node-pre-gyp@2.0.0': dependencies: consola: 3.4.2 @@ -19607,6 +19823,8 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.19': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-beta.35': {} '@rolldown/pluginutils@1.0.0-beta.40': {} @@ -20043,6 +20261,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.1': {} + '@sinclair/typebox@0.31.28': {} + '@sinclair/typebox@0.34.38': {} '@sindresorhus/is@4.6.0': {} @@ -20177,6 +20397,8 @@ snapshots: '@speed-highlight/core@1.2.7': {} + '@sqlite.org/sqlite-wasm@3.48.0-build4': {} + '@standard-schema/spec@1.0.0': {} '@stylistic/eslint-plugin-js@2.13.0(eslint@9.22.0(jiti@2.6.0))': @@ -21235,6 +21457,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-react@4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-react@5.0.3(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0))': dependencies: '@babel/core': 7.28.4 @@ -21734,6 +21968,8 @@ snapshots: array-slice@1.1.0: {} + array-timsort@1.0.3: {} + asn1js@3.0.6: dependencies: pvtsutils: 1.3.6 @@ -22151,6 +22387,14 @@ snapshots: commander@8.3.0: {} + comment-json@4.2.5: + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + comment-parser@1.4.1: {} commondir@1.0.1: {} @@ -22210,6 +22454,8 @@ snapshots: connect-history-api-fallback@2.0.0: {} + consola@3.4.0: {} + consola@3.4.2: {} content-disposition@0.5.4: @@ -22378,6 +22624,10 @@ snapshots: decimal.js@10.5.0: {} + dedent@1.5.1(babel-plugin-macros@3.1.0): + optionalDependencies: + babel-plugin-macros: 3.1.0 + dedent@1.7.0(babel-plugin-macros@3.1.0): optionalDependencies: babel-plugin-macros: 3.1.0 @@ -23046,8 +23296,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -23512,6 +23762,8 @@ snapshots: has-flag@4.0.0: {} + has-own-prop@2.0.0: {} + has-symbols@1.1.0: {} hasown@2.0.2: @@ -23654,6 +23906,8 @@ snapshots: httpxy@0.1.7: {} + human-id@4.1.1: {} + human-signals@5.0.0: {} hyperdyperid@1.2.0: {} @@ -23900,6 +24154,8 @@ snapshots: js-cookie@3.0.5: {} + js-sha256@0.11.1: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -24019,6 +24275,8 @@ snapshots: ky@1.7.4: {} + kysely@0.27.6: {} + launch-editor@2.9.1: dependencies: picocolors: 1.1.1 @@ -25375,6 +25633,8 @@ snapshots: lodash: 4.17.21 strip-ansi: 6.0.1 + repeat-string@1.6.1: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -25778,6 +26038,11 @@ snapshots: sprintf-js@1.0.3: {} + sqlite-wasm-kysely@0.3.0(kysely@0.27.6): + dependencies: + '@sqlite.org/sqlite-wasm': 3.48.0-build4 + kysely: 0.27.6 + srvx@0.8.7: dependencies: cookie-es: 2.0.0 @@ -26238,7 +26503,7 @@ snapshots: acorn: 8.14.1 estree-walker: 3.0.3 magic-string: 0.30.19 - unplugin: 2.3.4 + unplugin: 2.3.10 undici-types@6.20.0: {} @@ -26392,8 +26657,7 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - urlpattern-polyfill@10.1.0: - optional: true + urlpattern-polyfill@10.1.0: {} use-callback-ref@1.3.3(@types/react@19.0.8)(react@19.0.0): dependencies: @@ -26420,6 +26684,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@11.1.0: optional: true From f164c5002e7b0525bfbe99c06a91fe0b781fe95b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:40:42 +0000 Subject: [PATCH 7/7] ci: apply automated fixes --- examples/react/i18n-paraglide/README.md | 46 ++++++------- .../project.inlang/settings.json | 7 +- examples/react/i18n-paraglide/src/main.tsx | 28 ++++---- .../i18n-paraglide/src/routes/__root.tsx | 23 +++---- .../react/i18n-paraglide/src/routes/about.tsx | 10 +-- .../react/i18n-paraglide/src/routes/index.tsx | 12 ++-- examples/react/i18n-paraglide/src/styles.css | 2 +- examples/react/i18n-paraglide/vite.config.ts | 44 ++++++------- .../.vscode/extensions.json | 6 +- examples/react/start-i18n-paraglide/README.md | 64 +++++++++---------- .../project.inlang/settings.json | 7 +- .../react/start-i18n-paraglide/src/router.tsx | 10 +-- .../src/routes/__root.tsx | 34 +++++----- .../start-i18n-paraglide/src/routes/about.tsx | 10 +-- .../start-i18n-paraglide/src/routes/index.tsx | 28 ++++---- .../react/start-i18n-paraglide/src/server.ts | 8 +-- .../react/start-i18n-paraglide/src/styles.css | 2 +- .../src/utils/prerender.ts | 6 +- .../start-i18n-paraglide/src/utils/seo.ts | 38 +++++------ .../src/utils/translated-pathnames.ts | 46 ++++++------- .../react/start-i18n-paraglide/vite.config.ts | 38 +++++------ 21 files changed, 228 insertions(+), 241 deletions(-) diff --git a/examples/react/i18n-paraglide/README.md b/examples/react/i18n-paraglide/README.md index 3b2cfa89e3e..20fe372f869 100644 --- a/examples/react/i18n-paraglide/README.md +++ b/examples/react/i18n-paraglide/README.md @@ -76,39 +76,39 @@ export const Route = createRootRoute({ If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. ```ts -import { Locale } from "@/paraglide/runtime"; -import { FileRoutesByTo } from "../routeTree.gen"; +import { Locale } from '@/paraglide/runtime' +import { FileRoutesByTo } from '../routeTree.gen' -type RoutePath = keyof FileRoutesByTo; +type RoutePath = keyof FileRoutesByTo -const excludedPaths = ["admin", "docs", "api"] as const; +const excludedPaths = ['admin', 'docs', 'api'] as const type PublicRoutePath = Exclude< RoutePath, `${string}${(typeof excludedPaths)[number]}${string}` ->; +> type TranslatedPathname = { - pattern: string; - localized: Array<[Locale, string]>; -}; + pattern: string + localized: Array<[Locale, string]> +} function toUrlPattern(path: string) { return ( path // catch-all - .replace(/\/\$$/, "/:path(.*)?") + .replace(/\/\$$/, '/:path(.*)?') // optional parameters: {-$param} - .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ':$1?') // named parameters: $param - .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + .replace(/\$([a-zA-Z0-9_]+)/g, ':$1') // remove trailing slash - .replace(/\/+$/, "") - ); + .replace(/\/+$/, '') + ) } function createTranslatedPathnames( - input: Record> + input: Record>, ): TranslatedPathname[] { return Object.entries(input).map(([pattern, locales]) => ({ pattern: toUrlPattern(pattern), @@ -117,21 +117,21 @@ function createTranslatedPathnames( [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ Locale, string, - ] + ], ), - })); + })) } export const translatedPathnames = createTranslatedPathnames({ - "/": { - en: "/", - de: "/", + '/': { + en: '/', + de: '/', }, - "/about": { - en: "/about", - de: "/ueber", + '/about': { + en: '/about', + de: '/ueber', }, -}); +}) ``` And import into the Paraglide Vite plguin. diff --git a/examples/react/i18n-paraglide/project.inlang/settings.json b/examples/react/i18n-paraglide/project.inlang/settings.json index 62d77adb682..9bdce4c8cc9 100644 --- a/examples/react/i18n-paraglide/project.inlang/settings.json +++ b/examples/react/i18n-paraglide/project.inlang/settings.json @@ -1,10 +1,7 @@ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", - "locales": [ - "en", - "de" - ], + "locales": ["en", "de"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" @@ -12,4 +9,4 @@ "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" } -} \ No newline at end of file +} diff --git a/examples/react/i18n-paraglide/src/main.tsx b/examples/react/i18n-paraglide/src/main.tsx index 65d3d911755..b30558a9472 100644 --- a/examples/react/i18n-paraglide/src/main.tsx +++ b/examples/react/i18n-paraglide/src/main.tsx @@ -1,16 +1,16 @@ -import { StrictMode } from "react"; -import ReactDOM from "react-dom/client"; -import { RouterProvider, createRouter } from "@tanstack/react-router"; -import "./styles.css"; +import { StrictMode } from 'react' +import ReactDOM from 'react-dom/client' +import { RouterProvider, createRouter } from '@tanstack/react-router' +import './styles.css' // Import the generated route tree -import { routeTree } from "./routeTree.gen"; -import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime.js"; +import { routeTree } from './routeTree.gen' +import { deLocalizeUrl, localizeUrl } from './paraglide/runtime.js' // Create a new router instance const router = createRouter({ routeTree, context: {}, - defaultPreload: "intent", + defaultPreload: 'intent', scrollRestoration: true, defaultStructuralSharing: true, defaultPreloadStaleTime: 0, @@ -19,22 +19,22 @@ const router = createRouter({ input: ({ url }) => deLocalizeUrl(url), output: ({ url }) => localizeUrl(url), }, -}); +}) // Register the router instance for type safety -declare module "@tanstack/react-router" { +declare module '@tanstack/react-router' { interface Register { - router: typeof router; + router: typeof router } } // Render the app -const rootElement = document.getElementById("app"); +const rootElement = document.getElementById('app') if (rootElement && !rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement); + const root = ReactDOM.createRoot(rootElement) root.render( - - ); + , + ) } diff --git a/examples/react/i18n-paraglide/src/routes/__root.tsx b/examples/react/i18n-paraglide/src/routes/__root.tsx index d02ebd5f714..f7748c65278 100644 --- a/examples/react/i18n-paraglide/src/routes/__root.tsx +++ b/examples/react/i18n-paraglide/src/routes/__root.tsx @@ -1,25 +1,20 @@ -import { - Link, - Outlet, - createRootRoute, - redirect, -} from "@tanstack/react-router"; +import { Link, Outlet, createRootRoute, redirect } from '@tanstack/react-router' import { getLocale, locales, setLocale, shouldRedirect, -} from "@/paraglide/runtime"; -import { m } from "@/paraglide/messages"; +} from '@/paraglide/runtime' +import { m } from '@/paraglide/messages' export const Route = createRootRoute({ beforeLoad: async () => { - document.documentElement.setAttribute("lang", getLocale()); + document.documentElement.setAttribute('lang', getLocale()) - const decision = await shouldRedirect({ url: window.location.href }); + const decision = await shouldRedirect({ url: window.location.href }) if (decision.redirectUrl) { - throw redirect({ href: decision.redirectUrl.href }); + throw redirect({ href: decision.redirectUrl.href }) } }, component: () => ( @@ -29,7 +24,7 @@ export const Route = createRootRoute({ @@ -39,7 +34,7 @@ export const Route = createRootRoute({ {m.about_page()} @@ -67,4 +62,4 @@ export const Route = createRootRoute({ ), -}); +}) diff --git a/examples/react/i18n-paraglide/src/routes/about.tsx b/examples/react/i18n-paraglide/src/routes/about.tsx index 1ae1d7ef9ff..31a5beeb4aa 100644 --- a/examples/react/i18n-paraglide/src/routes/about.tsx +++ b/examples/react/i18n-paraglide/src/routes/about.tsx @@ -1,10 +1,10 @@ -import { m } from "@/paraglide/messages"; -import { createFileRoute } from "@tanstack/react-router"; +import { m } from '@/paraglide/messages' +import { createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute("/about")({ +export const Route = createFileRoute('/about')({ component: RouteComponent, -}); +}) function RouteComponent() { - return
{m.hello_about()}
; + return
{m.hello_about()}
} diff --git a/examples/react/i18n-paraglide/src/routes/index.tsx b/examples/react/i18n-paraglide/src/routes/index.tsx index 87ca08bc711..a35e78256dc 100644 --- a/examples/react/i18n-paraglide/src/routes/index.tsx +++ b/examples/react/i18n-paraglide/src/routes/index.tsx @@ -1,18 +1,18 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { m } from "@/paraglide/messages"; +import { createFileRoute } from '@tanstack/react-router' +import { m } from '@/paraglide/messages' -export const Route = createFileRoute("/")({ +export const Route = createFileRoute('/')({ component: App, -}); +}) function App() { return (

{m.example_message({ - username: "TanStack Router!", + username: 'TanStack Router!', })}

- ); + ) } diff --git a/examples/react/i18n-paraglide/src/styles.css b/examples/react/i18n-paraglide/src/styles.css index f1d8c73cdcf..d4b5078586e 100644 --- a/examples/react/i18n-paraglide/src/styles.css +++ b/examples/react/i18n-paraglide/src/styles.css @@ -1 +1 @@ -@import "tailwindcss"; +@import 'tailwindcss'; diff --git a/examples/react/i18n-paraglide/vite.config.ts b/examples/react/i18n-paraglide/vite.config.ts index 8d10ed74cf7..97e9dda7bc0 100644 --- a/examples/react/i18n-paraglide/vite.config.ts +++ b/examples/react/i18n-paraglide/vite.config.ts @@ -1,39 +1,39 @@ -import { defineConfig } from "vite"; -import viteReact from "@vitejs/plugin-react"; -import { tanstackRouter } from "@tanstack/router-plugin/vite"; -import { resolve } from "node:path"; -import { paraglideVitePlugin } from "@inlang/paraglide-js"; -import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from 'vite' +import viteReact from '@vitejs/plugin-react' +import { tanstackRouter } from '@tanstack/router-plugin/vite' +import { resolve } from 'node:path' +import { paraglideVitePlugin } from '@inlang/paraglide-js' +import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ tailwindcss(), paraglideVitePlugin({ - project: "./project.inlang", - outdir: "./src/paraglide", - outputStructure: "message-modules", - cookieName: "PARAGLIDE_LOCALE", - strategy: ["url", "cookie", "preferredLanguage", "baseLocale"], + project: './project.inlang', + outdir: './src/paraglide', + outputStructure: 'message-modules', + cookieName: 'PARAGLIDE_LOCALE', + strategy: ['url', 'cookie', 'preferredLanguage', 'baseLocale'], urlPatterns: [ { - pattern: "/", + pattern: '/', localized: [ - ["en", "/"], - ["de", "/de"], + ['en', '/'], + ['de', '/de'], ], }, { - pattern: "/about", + pattern: '/about', localized: [ - ["en", "/about"], - ["de", "/de/ueber"], + ['en', '/about'], + ['de', '/de/ueber'], ], }, { - pattern: "/:path(.*)?", + pattern: '/:path(.*)?', localized: [ - ["en", "/:path(.*)?"], - ["de", "/de/:path(.*)?"], + ['en', '/:path(.*)?'], + ['de', '/de/:path(.*)?'], ], }, ], @@ -43,7 +43,7 @@ export default defineConfig({ ], resolve: { alias: { - "@": resolve(__dirname, "./src"), + '@': resolve(__dirname, './src'), }, }, -}); +}) diff --git a/examples/react/start-i18n-paraglide/.vscode/extensions.json b/examples/react/start-i18n-paraglide/.vscode/extensions.json index 116d6852a68..8cf06c2f61a 100644 --- a/examples/react/start-i18n-paraglide/.vscode/extensions.json +++ b/examples/react/start-i18n-paraglide/.vscode/extensions.json @@ -1,5 +1,3 @@ { - "recommendations": [ - "inlang.vs-code-extension" - ] -} \ No newline at end of file + "recommendations": ["inlang.vs-code-extension"] +} diff --git a/examples/react/start-i18n-paraglide/README.md b/examples/react/start-i18n-paraglide/README.md index 7a52f61aa08..8ea0ce8cc18 100644 --- a/examples/react/start-i18n-paraglide/README.md +++ b/examples/react/start-i18n-paraglide/README.md @@ -66,20 +66,20 @@ const router = createRouter({ In `server.ts` intercept the request with the paraglideMiddleware. ```ts -import { paraglideMiddleware } from "./paraglide/server.js"; -import handler from "@tanstack/react-start/server-entry"; +import { paraglideMiddleware } from './paraglide/server.js' +import handler from '@tanstack/react-start/server-entry' export default { fetch(req: Request): Promise { - return paraglideMiddleware(req, ({ request }) => handler.fetch(request)); + return paraglideMiddleware(req, ({ request }) => handler.fetch(request)) }, -}; +} ``` In `__root.tsx` add change the html lang attribute to the current locale. ```tsx -import { getLocale } from "../paraglide/runtime.js"; +import { getLocale } from '../paraglide/runtime.js' function RootDocument({ children }: { children: React.ReactNode }) { return ( @@ -92,7 +92,7 @@ function RootDocument({ children }: { children: React.ReactNode }) { - ); + ) } ``` @@ -120,39 +120,39 @@ export const Route = createRootRoute({ If you don't want to miss any translated path, you can create a `createTranslatedPathnames` function and pass it to the vite plugin. ```ts -import { Locale } from "@/paraglide/runtime"; -import { FileRoutesByTo } from "../routeTree.gen"; +import { Locale } from '@/paraglide/runtime' +import { FileRoutesByTo } from '../routeTree.gen' -type RoutePath = keyof FileRoutesByTo; +type RoutePath = keyof FileRoutesByTo -const excludedPaths = ["admin", "docs", "api"] as const; +const excludedPaths = ['admin', 'docs', 'api'] as const type PublicRoutePath = Exclude< RoutePath, `${string}${(typeof excludedPaths)[number]}${string}` ->; +> type TranslatedPathname = { - pattern: string; - localized: Array<[Locale, string]>; -}; + pattern: string + localized: Array<[Locale, string]> +} function toUrlPattern(path: string) { return ( path // catch-all - .replace(/\/\$$/, "/:path(.*)?") + .replace(/\/\$$/, '/:path(.*)?') // optional parameters: {-$param} - .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ':$1?') // named parameters: $param - .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + .replace(/\$([a-zA-Z0-9_]+)/g, ':$1') // remove trailing slash - .replace(/\/+$/, "") - ); + .replace(/\/+$/, '') + ) } function createTranslatedPathnames( - input: Record> + input: Record>, ): TranslatedPathname[] { return Object.entries(input).map(([pattern, locales]) => ({ pattern: toUrlPattern(pattern), @@ -161,21 +161,21 @@ function createTranslatedPathnames( [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ Locale, string, - ] + ], ), - })); + })) } export const translatedPathnames = createTranslatedPathnames({ - "/": { - en: "/", - de: "/", + '/': { + en: '/', + de: '/', }, - "/about": { - en: "/about", - de: "/ueber", + '/about': { + en: '/about', + de: '/ueber', }, -}); +}) ``` And import into the Paraglide Vite plguin. @@ -185,12 +185,12 @@ And import into the Paraglide Vite plguin. You can use use the `localizeHref` function to map the routes to localized versions and import into the pages option in the TanStack Start plugin. For this to work you will need to compile paraglide before the build with the CLI. ```ts -import { localizeHref } from "./paraglide/runtime"; +import { localizeHref } from './paraglide/runtime' -export const prerenderRoutes = ["/", "/about"].map((path) => ({ +export const prerenderRoutes = ['/', '/about'].map((path) => ({ path: localizeHref(path), prerender: { enabled: true, }, -})); +})) ``` diff --git a/examples/react/start-i18n-paraglide/project.inlang/settings.json b/examples/react/start-i18n-paraglide/project.inlang/settings.json index 62d77adb682..9bdce4c8cc9 100644 --- a/examples/react/start-i18n-paraglide/project.inlang/settings.json +++ b/examples/react/start-i18n-paraglide/project.inlang/settings.json @@ -1,10 +1,7 @@ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", - "locales": [ - "en", - "de" - ], + "locales": ["en", "de"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" @@ -12,4 +9,4 @@ "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" } -} \ No newline at end of file +} diff --git a/examples/react/start-i18n-paraglide/src/router.tsx b/examples/react/start-i18n-paraglide/src/router.tsx index 0e4ef34366f..3a8413a9645 100644 --- a/examples/react/start-i18n-paraglide/src/router.tsx +++ b/examples/react/start-i18n-paraglide/src/router.tsx @@ -1,8 +1,8 @@ -import { createRouter } from "@tanstack/react-router"; +import { createRouter } from '@tanstack/react-router' // Import the generated route tree -import { routeTree } from "./routeTree.gen"; -import { deLocalizeUrl, localizeUrl } from "./paraglide/runtime"; +import { routeTree } from './routeTree.gen' +import { deLocalizeUrl, localizeUrl } from './paraglide/runtime' // Create a new router instance export const getRouter = () => { @@ -14,5 +14,5 @@ export const getRouter = () => { input: ({ url }) => deLocalizeUrl(url), output: ({ url }) => localizeUrl(url), }, - }); -}; + }) +} diff --git a/examples/react/start-i18n-paraglide/src/routes/__root.tsx b/examples/react/start-i18n-paraglide/src/routes/__root.tsx index 153d90381bd..f1a83836a0e 100644 --- a/examples/react/start-i18n-paraglide/src/routes/__root.tsx +++ b/examples/react/start-i18n-paraglide/src/routes/__root.tsx @@ -3,32 +3,32 @@ import { Link, Scripts, createRootRoute, -} from "@tanstack/react-router"; -import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; -import { TanStackDevtools } from "@tanstack/react-devtools"; -import styles from "../styles.css?url"; -import { getLocale, locales, setLocale } from "@/paraglide/runtime"; -import { m } from "@/paraglide/messages"; +} from '@tanstack/react-router' +import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' +import { TanStackDevtools } from '@tanstack/react-devtools' +import styles from '../styles.css?url' +import { getLocale, locales, setLocale } from '@/paraglide/runtime' +import { m } from '@/paraglide/messages' export const Route = createRootRoute({ head: () => ({ meta: [ { - charSet: "utf-8", + charSet: 'utf-8', }, { - name: "viewport", - content: "width=device-width, initial-scale=1", + name: 'viewport', + content: 'width=device-width, initial-scale=1', }, { - title: "TanStack Start Starter", + title: 'TanStack Start Starter', }, ], - links: [{ rel: "stylesheet", href: styles }], + links: [{ rel: 'stylesheet', href: styles }], }), shellComponent: RootDocument, -}); +}) function RootDocument({ children }: { children: React.ReactNode }) { return ( @@ -42,7 +42,7 @@ function RootDocument({ children }: { children: React.ReactNode }) { @@ -52,7 +52,7 @@ function RootDocument({ children }: { children: React.ReactNode }) { {m.about_page()} @@ -79,11 +79,11 @@ function RootDocument({ children }: { children: React.ReactNode }) { , }, ]} @@ -91,5 +91,5 @@ function RootDocument({ children }: { children: React.ReactNode }) { - ); + ) } diff --git a/examples/react/start-i18n-paraglide/src/routes/about.tsx b/examples/react/start-i18n-paraglide/src/routes/about.tsx index 50145df75ed..0ee95a577a2 100644 --- a/examples/react/start-i18n-paraglide/src/routes/about.tsx +++ b/examples/react/start-i18n-paraglide/src/routes/about.tsx @@ -1,10 +1,10 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { m } from "@/paraglide/messages"; +import { createFileRoute } from '@tanstack/react-router' +import { m } from '@/paraglide/messages' -export const Route = createFileRoute("/about")({ +export const Route = createFileRoute('/about')({ component: RouteComponent, -}); +}) function RouteComponent() { - return
{m.about_message()}
; + return
{m.about_message()}
} diff --git a/examples/react/start-i18n-paraglide/src/routes/index.tsx b/examples/react/start-i18n-paraglide/src/routes/index.tsx index 46a67dfef30..a7a4b403388 100644 --- a/examples/react/start-i18n-paraglide/src/routes/index.tsx +++ b/examples/react/start-i18n-paraglide/src/routes/index.tsx @@ -1,33 +1,33 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { m } from "@/paraglide/messages.js"; -import { getLocale } from "@/paraglide/runtime.js"; -import { createServerFn } from "@tanstack/react-start"; +import { createFileRoute } from '@tanstack/react-router' +import { m } from '@/paraglide/messages.js' +import { getLocale } from '@/paraglide/runtime.js' +import { createServerFn } from '@tanstack/react-start' const getServerMessage = createServerFn() .inputValidator((emoji: string) => emoji) .handler((ctx) => { - return m.server_message({ emoji: ctx.data }); - }); + return m.server_message({ emoji: ctx.data }) + }) -export const Route = createFileRoute("/")({ +export const Route = createFileRoute('/')({ component: Home, loader: () => { return { localeFromLoader: getLocale(), - messageFromLoader: m.example_message({ username: "John Doe" }), - serverFunctionMessage: getServerMessage({ data: "📩" }), - }; + messageFromLoader: m.example_message({ username: 'John Doe' }), + serverFunctionMessage: getServerMessage({ data: '📩' }), + } }, -}); +}) function Home() { const { serverFunctionMessage, messageFromLoader, localeFromLoader } = - Route.useLoaderData(); + Route.useLoaderData() return (

Message from loader: {messageFromLoader}

Server function message: {serverFunctionMessage}:

-

{m.example_message({ username: "John Doe" })}

+

{m.example_message({ username: 'John Doe' })}

- ); + ) } diff --git a/examples/react/start-i18n-paraglide/src/server.ts b/examples/react/start-i18n-paraglide/src/server.ts index d22159ef4cd..9542b01d4ac 100644 --- a/examples/react/start-i18n-paraglide/src/server.ts +++ b/examples/react/start-i18n-paraglide/src/server.ts @@ -1,8 +1,8 @@ -import { paraglideMiddleware } from "./paraglide/server.js"; -import handler from "@tanstack/react-start/server-entry"; +import { paraglideMiddleware } from './paraglide/server.js' +import handler from '@tanstack/react-start/server-entry' export default { fetch(req: Request): Promise { - return paraglideMiddleware(req, ({ request }) => handler.fetch(request)); + return paraglideMiddleware(req, ({ request }) => handler.fetch(request)) }, -}; +} diff --git a/examples/react/start-i18n-paraglide/src/styles.css b/examples/react/start-i18n-paraglide/src/styles.css index f1d8c73cdcf..d4b5078586e 100644 --- a/examples/react/start-i18n-paraglide/src/styles.css +++ b/examples/react/start-i18n-paraglide/src/styles.css @@ -1 +1 @@ -@import "tailwindcss"; +@import 'tailwindcss'; diff --git a/examples/react/start-i18n-paraglide/src/utils/prerender.ts b/examples/react/start-i18n-paraglide/src/utils/prerender.ts index 52104cd4914..0cb1630595f 100644 --- a/examples/react/start-i18n-paraglide/src/utils/prerender.ts +++ b/examples/react/start-i18n-paraglide/src/utils/prerender.ts @@ -1,8 +1,8 @@ -import { localizeHref } from "../paraglide/runtime"; +import { localizeHref } from '../paraglide/runtime' -export const prerenderRoutes = ["/", "/about"].map((path) => ({ +export const prerenderRoutes = ['/', '/about'].map((path) => ({ path: localizeHref(path), prerender: { enabled: true, }, -})); +})) diff --git a/examples/react/start-i18n-paraglide/src/utils/seo.ts b/examples/react/start-i18n-paraglide/src/utils/seo.ts index 8b0957ebc1c..d18ad84b74e 100644 --- a/examples/react/start-i18n-paraglide/src/utils/seo.ts +++ b/examples/react/start-i18n-paraglide/src/utils/seo.ts @@ -4,30 +4,30 @@ export const seo = ({ keywords, image, }: { - title: string; - description?: string; - image?: string; - keywords?: string; + title: string + description?: string + image?: string + keywords?: string }) => { const tags = [ { title }, - { name: "description", content: description }, - { name: "keywords", content: keywords }, - { name: "twitter:title", content: title }, - { name: "twitter:description", content: description }, - { name: "twitter:creator", content: "@tannerlinsley" }, - { name: "twitter:site", content: "@tannerlinsley" }, - { name: "og:type", content: "website" }, - { name: "og:title", content: title }, - { name: "og:description", content: description }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, ...(image ? [ - { name: "twitter:image", content: image }, - { name: "twitter:card", content: "summary_large_image" }, - { name: "og:image", content: image }, + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, ] : []), - ]; + ] - return tags; -}; + return tags +} diff --git a/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts b/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts index 18322410923..bb8649ff4e0 100644 --- a/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts +++ b/examples/react/start-i18n-paraglide/src/utils/translated-pathnames.ts @@ -1,36 +1,36 @@ -import { Locale } from "@/paraglide/runtime"; -import { FileRoutesByTo } from "../routeTree.gen"; +import { Locale } from '@/paraglide/runtime' +import { FileRoutesByTo } from '../routeTree.gen' -type RoutePath = keyof FileRoutesByTo; +type RoutePath = keyof FileRoutesByTo -const excludedPaths = ["admin", "docs", "api"] as const; +const excludedPaths = ['admin', 'docs', 'api'] as const type PublicRoutePath = Exclude< RoutePath, `${string}${(typeof excludedPaths)[number]}${string}` ->; +> type TranslatedPathname = { - pattern: string; - localized: Array<[Locale, string]>; -}; + pattern: string + localized: Array<[Locale, string]> +} function toUrlPattern(path: string) { return ( path // catch-all - .replace(/\/\$$/, "/:path(.*)?") + .replace(/\/\$$/, '/:path(.*)?') // optional parameters: {-$param} - .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ":$1?") + .replace(/\{-\$([a-zA-Z0-9_]+)\}/g, ':$1?') // named parameters: $param - .replace(/\$([a-zA-Z0-9_]+)/g, ":$1") + .replace(/\$([a-zA-Z0-9_]+)/g, ':$1') // remove trailing slash - .replace(/\/+$/, "") - ); + .replace(/\/+$/, '') + ) } function createTranslatedPathnames( - input: Record> + input: Record>, ): TranslatedPathname[] { return Object.entries(input).map(([pattern, locales]) => ({ pattern: toUrlPattern(pattern), @@ -39,18 +39,18 @@ function createTranslatedPathnames( [locale as Locale, `/${locale}${toUrlPattern(path)}`] satisfies [ Locale, string, - ] + ], ), - })); + })) } export const translatedPathnames = createTranslatedPathnames({ - "/": { - en: "/", - de: "/", + '/': { + en: '/', + de: '/', }, - "/about": { - en: "/about", - de: "/ueber", + '/about': { + en: '/about', + de: '/ueber', }, -}); +}) diff --git a/examples/react/start-i18n-paraglide/vite.config.ts b/examples/react/start-i18n-paraglide/vite.config.ts index a90c761355b..d99139e4595 100644 --- a/examples/react/start-i18n-paraglide/vite.config.ts +++ b/examples/react/start-i18n-paraglide/vite.config.ts @@ -1,31 +1,31 @@ -import { paraglideVitePlugin } from "@inlang/paraglide-js"; -import { defineConfig } from "vite"; -import { tanstackStart } from "@tanstack/react-start/plugin/vite"; -import viteReact from "@vitejs/plugin-react"; -import viteTsConfigPaths from "vite-tsconfig-paths"; -import tailwindcss from "@tailwindcss/vite"; +import { paraglideVitePlugin } from '@inlang/paraglide-js' +import { defineConfig } from 'vite' +import { tanstackStart } from '@tanstack/react-start/plugin/vite' +import viteReact from '@vitejs/plugin-react' +import viteTsConfigPaths from 'vite-tsconfig-paths' +import tailwindcss from '@tailwindcss/vite' const config = defineConfig({ plugins: [ paraglideVitePlugin({ - project: "./project.inlang", - outdir: "./src/paraglide", - outputStructure: "message-modules", - cookieName: "PARAGLIDE_LOCALE", - strategy: ["url", "cookie", "preferredLanguage", "baseLocale"], + project: './project.inlang', + outdir: './src/paraglide', + outputStructure: 'message-modules', + cookieName: 'PARAGLIDE_LOCALE', + strategy: ['url', 'cookie', 'preferredLanguage', 'baseLocale'], urlPatterns: [ { - pattern: "/about", + pattern: '/about', localized: [ - ["en", "/en/about"], - ["de", "/de/ueber"], + ['en', '/en/about'], + ['de', '/de/ueber'], ], }, { - pattern: "/:path(.*)?", + pattern: '/:path(.*)?', localized: [ - ["en", "/en/:path(.*)?"], - ["de", "/de/:path(.*)?"], + ['en', '/en/:path(.*)?'], + ['de', '/de/:path(.*)?'], ], }, ], @@ -35,6 +35,6 @@ const config = defineConfig({ viteReact(), tailwindcss(), ], -}); +}) -export default config; +export default config