-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
668 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package-lock.json | ||
yarn.lock | ||
pnpm-lock.yaml | ||
pnpm-lock.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
node_modules | ||
|
||
/.cache | ||
/build | ||
.env | ||
.react-router | ||
|
||
.nx/cache | ||
.nx/workspace-data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"typescript.tsdk": "node_modules/typescript/lib", | ||
"typescript.enablePromptUseWorkspaceTsdk": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Welcome to React Router! | ||
|
||
- 📖 [React Router docs](https://reactrouter.com/dev) | ||
|
||
## Development | ||
|
||
Run the dev server: | ||
|
||
```shellscript | ||
npm run dev | ||
``` | ||
|
||
## Deployment | ||
|
||
First, build your app for production: | ||
|
||
```sh | ||
npm run build | ||
``` | ||
|
||
Then run the app in production mode: | ||
|
||
```sh | ||
npm start | ||
``` | ||
|
||
Now you'll need to pick a host to deploy it to. | ||
|
||
### DIY | ||
|
||
If you're familiar with deploying Node applications, the built-in app server is production-ready. | ||
|
||
Make sure to deploy the output of `npm run build` | ||
|
||
- `build/server` | ||
- `build/client` | ||
|
||
## Styling | ||
|
||
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. |
56 changes: 56 additions & 0 deletions
56
templates/react-router-monorepo/apps/app/app/defaultMessages.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { ComponentProps } from "react"; | ||
import { BentoProvider } from "design-system"; | ||
import { useTranslation } from "react-i18next"; | ||
|
||
export const useDefaultMessages = (): ComponentProps<typeof BentoProvider>["defaultMessages"] => { | ||
const { t } = useTranslation(); | ||
|
||
return { | ||
Chip: { | ||
dismissButtonLabel: t("common.chip.dismissButtonLabel", "Remove"), | ||
}, | ||
Banner: { | ||
dismissButtonLabel: t("common.banner.dismissButtonLabel", "Close"), | ||
}, | ||
Modal: { | ||
closeButtonLabel: t("common.modal.closeButtonLabel", "Close"), | ||
}, | ||
SelectField: { | ||
noOptionsMessage: t("common.selectField.noOptionsMessage", "No options"), | ||
multiOptionsSelected: (n) => { | ||
const options = | ||
n > 1 | ||
? t("common.selectField.optionsPlural", "options") | ||
: t("common.selectField.optionsSingular", "option"); | ||
return t("common.selectField.multiOptionsSelected", "{{n}} {{options}} selected", { | ||
n, | ||
options, | ||
}); | ||
}, | ||
selectAllButtonLabel: t("common.selectField.selectAllButtonLabel", "Select all"), | ||
clearAllButtonLabel: t("common.selectField.clearAllButtonLabel", "Clear all"), | ||
}, | ||
SearchBar: { | ||
clearButtonLabel: t("common.searchBar.clearButtonLabel", "Clear"), | ||
}, | ||
Table: { | ||
noResultsTitle: t("common.table.noResultsTitle", "No results found"), | ||
noResultsDescription: t( | ||
"common.table.noResultsDescription", | ||
"Try adjusting your search filters to find what you're looking for." | ||
), | ||
missingValue: t("common.table.missingValue", "-"), | ||
}, | ||
Loader: { | ||
loadingMessage: t("common.loader.loadingMessage", "Loading..."), | ||
}, | ||
DateField: { | ||
previousMonthLabel: t("common.dateField.previousMonthLabel", "Prev month"), | ||
nextMonthLabel: t("common.dateField.nextMonthLabel", "Next month"), | ||
}, | ||
TextField: { | ||
showPasswordLabel: t("common.textField.showPasswordLabel", "Show password"), | ||
hidePasswordLabel: t("common.textField.hidePasswordLabel", "Hide password"), | ||
}, | ||
}; | ||
}; |
44 changes: 44 additions & 0 deletions
44
templates/react-router-monorepo/apps/app/app/entry.client.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { startTransition, StrictMode } from "react"; | ||
import { hydrateRoot } from "react-dom/client"; | ||
import { HydratedRouter } from "react-router/dom"; | ||
import i18n, { registerCustomFormats } from "./i18n"; | ||
import i18next from "i18next"; | ||
import { I18nextProvider, initReactI18next } from "react-i18next"; | ||
import LanguageDetector from "i18next-browser-languagedetector"; | ||
import Backend from "i18next-http-backend"; | ||
|
||
await i18next | ||
.use(initReactI18next) // Tell i18next to use the react-i18next plugin | ||
.use(LanguageDetector) // Setup a client-side language detector | ||
.use(Backend) // Setup your backend | ||
.init({ | ||
...i18n, // spread the configuration | ||
// This function detects the namespaces your routes rendered while SSR use | ||
ns: [], | ||
backend: { loadPath: "/locales/{{lng}}.json" }, | ||
detection: { | ||
// Here only enable htmlTag detection, we'll detect the language only | ||
// server-side with remix-i18next, by using the `<html lang>` attribute | ||
// we can communicate to the client the language detected server-side | ||
order: ["htmlTag"], | ||
// Because we only use htmlTag, there's no reason to cache the language | ||
// on the browser, so we disable it | ||
caches: [], | ||
}, | ||
}); | ||
|
||
registerCustomFormats(i18next); | ||
|
||
console.log(I18nextProvider); | ||
console.log(i18next); | ||
|
||
startTransition(() => { | ||
hydrateRoot( | ||
document, | ||
<I18nextProvider i18n={i18next}> | ||
<StrictMode> | ||
<HydratedRouter /> | ||
</StrictMode> | ||
</I18nextProvider> | ||
); | ||
}); |
103 changes: 103 additions & 0 deletions
103
templates/react-router-monorepo/apps/app/app/entry.server.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { PassThrough } from "node:stream"; | ||
|
||
import type { AppLoadContext, EntryContext } from "react-router"; | ||
import { createReadableStreamFromReadable } from "@react-router/node"; | ||
import { ServerRouter } from "react-router"; | ||
import { isbot } from "isbot"; | ||
import type { RenderToPipeableStreamOptions } from "react-dom/server"; | ||
import { renderToPipeableStream } from "react-dom/server"; | ||
import { createInstance } from "i18next"; | ||
import i18next from "./i18next.server"; | ||
import { I18nextProvider, initReactI18next } from "react-i18next"; | ||
import Backend from "i18next-fs-backend"; | ||
import i18n, { registerCustomFormats } from "./i18n"; // your i18n configuration file | ||
import * as path from "node:path"; | ||
|
||
const ABORT_DELAY = 5_000; | ||
|
||
// Override console.erro to suppress specific warnings | ||
const originalConsoleError = console.error; | ||
console.error = (msg, ...args) => { | ||
if (typeof msg === "string" && msg.includes("useLayoutEffect")) { | ||
return; | ||
} | ||
if (typeof msg === "string" && msg.includes("A props object containing")) { | ||
return; | ||
} | ||
originalConsoleError(msg, ...args); | ||
}; | ||
|
||
export default function handleRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
routerContext: EntryContext, | ||
loadContext: AppLoadContext | ||
) { | ||
return new Promise(async (resolve, reject) => { | ||
let shellRendered = false; | ||
let userAgent = request.headers.get("user-agent"); | ||
|
||
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding | ||
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation | ||
let readyOption: keyof RenderToPipeableStreamOptions = | ||
(userAgent && isbot(userAgent)) || routerContext.isSpaMode ? "onAllReady" : "onShellReady"; | ||
|
||
let instance = createInstance(); | ||
let lng = await i18next.getLocale(request); | ||
// let ns = i18next.getRouteNamespaces(routerContext); | ||
let ns = ["translation"] as string[]; | ||
|
||
await instance | ||
.use(initReactI18next) // Tell our instance to use react-i18next | ||
.use(Backend) // Setup our backend | ||
.init({ | ||
...i18n, // spread the configuration | ||
lng, // The locale we detected above | ||
ns, // The namespaces the routes about to render wants to use | ||
backend: { | ||
loadPath: path.resolve("./public/locales/{{lng}}.json"), | ||
}, | ||
}); | ||
|
||
registerCustomFormats(instance); | ||
|
||
const { pipe, abort } = renderToPipeableStream( | ||
<I18nextProvider i18n={instance}> | ||
<ServerRouter context={routerContext} url={request.url} abortDelay={ABORT_DELAY} />, | ||
</I18nextProvider>, | ||
{ | ||
[readyOption]() { | ||
shellRendered = true; | ||
const body = new PassThrough(); | ||
const stream = createReadableStreamFromReadable(body); | ||
|
||
responseHeaders.set("Content-Type", "text/html"); | ||
|
||
resolve( | ||
new Response(stream, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}) | ||
); | ||
|
||
pipe(body); | ||
}, | ||
onShellError(error: unknown) { | ||
reject(error); | ||
}, | ||
onError(error: unknown) { | ||
responseStatusCode = 500; | ||
// Log streaming rendering errors from inside the shell. Don't log | ||
// errors encountered during initial shell rendering since they'll | ||
// reject and get logged in handleDocumentRequest. | ||
if (shellRendered) { | ||
console.error(error); | ||
} | ||
}, | ||
} | ||
); | ||
|
||
setTimeout(abort, ABORT_DELAY); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { i18n, InitOptions } from "i18next"; | ||
import type en from "../public/locales/en.json"; | ||
|
||
export default { | ||
supportedLngs: ["en", "it"], | ||
fallbackLng: "en", | ||
} satisfies InitOptions; | ||
|
||
export function registerCustomFormats(i18n: i18n) { | ||
i18n.services.formatter?.add("capitalize", (value: string) => { | ||
return value.charAt(0).toUpperCase() + value.slice(1); | ||
}); | ||
} | ||
|
||
declare module "i18next" { | ||
interface CustomTypeOptions { | ||
defaultNS: "translation"; | ||
resources: { | ||
translation: typeof en; | ||
}; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
templates/react-router-monorepo/apps/app/app/i18next.server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import Backend from "i18next-fs-backend"; | ||
import { resolve } from "node:path"; | ||
import { RemixI18Next } from "remix-i18next/server"; | ||
import i18n from "./i18n"; | ||
|
||
const i18next = new RemixI18Next({ | ||
detection: { | ||
supportedLanguages: i18n.supportedLngs, | ||
fallbackLanguage: i18n.fallbackLng, | ||
}, | ||
// This is the configuration for i18next used | ||
// when translating messages server-side only | ||
i18next: { | ||
...i18n, | ||
backend: { | ||
loadPath: resolve("./public/locales/{{lng}}.json"), | ||
}, | ||
}, | ||
// The i18next plugins you want RemixI18next to use for `i18n.getFixedT` inside loaders and actions. | ||
// E.g. The Backend plugin for loading translations from the file system | ||
// Tip: You could pass `resources` to the `i18next` configuration and avoid a backend here | ||
plugins: [Backend], | ||
}); | ||
|
||
export default i18next; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
body { | ||
height: 100vh; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { | ||
Link, | ||
Links, | ||
LinksFunction, | ||
Meta, | ||
Outlet, | ||
Scripts, | ||
ScrollRestoration, | ||
} from "react-router"; | ||
import "design-system/index.css"; | ||
import { BentoProvider, Children } from "design-system"; | ||
import "./root.css"; | ||
import i18next from "./i18next.server"; | ||
import type * as Route from "./+types.root"; | ||
import { useChangeLanguage } from "remix-i18next/react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { useDefaultMessages } from "./defaultMessages"; | ||
|
||
export const links: LinksFunction = () => []; | ||
|
||
export async function loader({ request }: Route.LoaderArgs) { | ||
let locale = await i18next.getLocale(request); | ||
return { locale }; | ||
} | ||
|
||
export function Layout({ children }: { children: React.ReactNode }) { | ||
const { i18n } = useTranslation(); | ||
const defaultMessages = useDefaultMessages(); | ||
return ( | ||
<html lang={i18n.language} dir={i18n.dir()}> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
<BentoProvider | ||
defaultMessages={defaultMessages} | ||
linkComponent={({ href, ...props }) => { | ||
if (href.startsWith("/")) { | ||
return <Link to={href} {...props} />; | ||
} | ||
return <a href={href} {...props} />; | ||
}} | ||
> | ||
{children as Children} | ||
</BentoProvider> | ||
<ScrollRestoration /> | ||
<Scripts /> | ||
</body> | ||
</html> | ||
); | ||
} | ||
|
||
export default function App({ loaderData }: Route.ComponentProps) { | ||
useChangeLanguage(loaderData.locale); | ||
|
||
return <Outlet />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { type RouteConfig, index } from "@react-router/dev/routes"; | ||
|
||
export const routes: RouteConfig = [index("routes/index.tsx")]; |
Oops, something went wrong.