Translation support #1186
Replies: 3 comments 5 replies
-
@DarkGhostHunter interesting idea. have you has success with dynamic namespaces for your translations? i.e. fetch only what is needed when it is needed? i have been working with the following but after 3 days, im stuck // composer.json
"require": {
"php": "^8.2",
"inertiajs/inertia-laravel": "2.0",
"laravel/framework": "^11.31",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.9",
"tightenco/ziggy": "^2.0"
}, // package.json
"dependencies": {
"i18next": "^24.1.0",
"i18next-http-backend": "^3.0.1",
"react-i18next": "^15.2.0"
}
"devDependencies": {
"@headlessui/react": "^2.0.0",
"@inertiajs/react": "^2.0.0",
"@tailwindcss/forms": "^0.5.3",
"@types/node": "^18.13.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.2.0",
"autoprefixer": "^10.4.12",
"axios": "^1.7.4",
"concurrently": "^9.0.1",
"cross-env": "^7.0.3",
"laravel-vite-plugin": "^1.0",
"nodemon": "^3.1.9",
"postcss": "^8.4.31",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^3.2.1",
"typescript": "^5.0.2",
"vite": "^6.0"
},
i use react for the frontend and react-i18next as my translations and localization manager. after 3 days of trying to find a solution that works in ssr i have come up with the following: // HandleInertiaRequests.php middleware
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
],
'ziggy' => fn () => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
'ns' => ['dashboard', 'profile', 'translation']
];
} // i18.config
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
export function createI18nInstance(lng = 'en', resources = {}) {
const instance = i18n.createInstance();
instance
.use(initReactI18next)
.init({
lng,
fallbackLng: 'en',
debug: false,
resources,
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
}
});
return instance;
} //ssr.tsx
import {createInertiaApp, router} from '@inertiajs/react';
import createServer from '@inertiajs/react/server';
import {resolvePageComponent} from 'laravel-vite-plugin/inertia-helpers';
import ReactDOMServer from 'react-dom/server';
import {RouteName} from 'ziggy-js';
import {route} from '../../vendor/tightenco/ziggy';
import {I18nextProvider, initReactI18next} from "react-i18next";
import Backend from "i18next-http-backend";
import i18n from "i18next";
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
createServer(async (page) => {
const i18nInstance = i18n.createInstance();
await i18nInstance
.use(initReactI18next)
.use(Backend)
.init({
// debug: true,
backend: {
//example: https://example.com/locales/{{lng}}/{{ns}}.json
loadPath: import.meta.env.VITE_TRANSLATIONS_ENDPOINT,
},
lng: 'en',
ns: page.props.ns,
fallbackLng: 'en',
interpolation: {escapeValue: false},
});
const initialI18nStore = i18nInstance.store.data;
const initialLanguage = i18nInstance.language;
page.props.initialI18nStore = initialI18nStore;
page.props.initialLanguage = initialLanguage;
return createInertiaApp({
page,
render: ReactDOMServer.renderToString,
title: (title) => `${title} - ${appName}`,
resolve: (name) => {
return resolvePageComponent(
`./Pages/${name}.tsx`,
import.meta.glob('./Pages/**/*.tsx'),
)
},
setup: ({App, props}) => {
/* eslint-disable */
// @ts-expect-error
global.route<RouteName> = (name, params, absolute) =>
route(name, params as any, absolute, {
...page.props.ziggy,
location: new URL(page.props.ziggy.location),
});
/* eslint-enable */
return (
<I18nextProvider i18n={i18nInstance}>
<App {...props}/>
</I18nextProvider>
);
},
})
},
); //app.tsx
import '../css/app.css';
import './bootstrap';
import {createInertiaApp} from '@inertiajs/react';
import {resolvePageComponent} from 'laravel-vite-plugin/inertia-helpers';
import {createRoot, hydrateRoot} from 'react-dom/client';
import {I18nextProvider} from "react-i18next";
import {createI18nInstance} from "@/utils/i18n.config";
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => {
return resolvePageComponent(
`./Pages/${name}.tsx`,
import.meta.glob('./Pages/**/*.tsx'),
)
},
setup({el, App, props}) {
const {initialI18nStore, initialLanguage} = props.initialPage.props;
const i18nInstance = createI18nInstance(initialLanguage as string, initialI18nStore as {});
const element = (
<I18nextProvider i18n={i18nInstance}>
<App {...props} />
</I18nextProvider>
);
if (import.meta.env.SSR) {
hydrateRoot(el, element);
} else {
createRoot(el).render(element);
}
},
progress: {
color: '#4B5563',
},
}); as you can see, i get the translations on the initial render and pass them through to the client. this is to prevent mismatch between client and server because if i dont forward this to the client, react-i18next will fetch on the client after the translations have been fetched on the server. even though this solution is far from perfect it is the closest i have been able to come to a working solution after 3 days of struggling to what would have been achievable in 20 mins with next-i18-next. the downside to this solution is that you need to fetch all your translations on the initial render even though you might need only 1 for a given page. how come app.tsx doesnt run when client side navigating but even more importantly what does run in client side navigation aside from the components re-render that would allow me to have translation and ssr? |
Beta Was this translation helpful? Give feedback.
-
I ended up ditching inertia for another framework, so I can no longer provide assistance. |
Beta Was this translation helpful? Give feedback.
-
guys, it's not better to share a json file containing the translation? deosn't make sense have a i18n on react level because laravel already has it... |
Beta Was this translation helpful? Give feedback.
-
The procedure is the following:
2.1 If the file exists and the hash is equal, don't regenerate it.
2.2 Otherwise, regenerate the file, and include all dev-set keys.
Make the site set up a locale based on a path:
For point 2, a middleware would change the locale for the app when its using a locale prefix, route or header. We can check if it's needed by checking
On the frontend, we could save the JSON file generated into
LocalStorage
, along with the hash. It has a 5MB limit, so unless the file is large, it can be saved into the browser.From there, Inertia could pull translation lines ala Laravel:
That would fix a lot of headaches with localization, because, mainly, i18 is usually handled at the server instead of the frontend. @barryvdh made something for that in his translation manager.
At least, on Laravel, the problem is how to retrieve all keys, from all namespaces.
Beta Was this translation helpful? Give feedback.
All reactions