mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 10:16:01 +00:00
This commit introduces internationalization (i18n) to your application using the `remix-i18next` framework, along with `i18next` and `react-i18next`. It also includes Turkish as the first additional language. Key changes include: 1. **Framework Integration:** * Installed necessary dependencies: `remix-i18next`, `i18next`, `react-i18next`, `i18next-browser-languagedetector`, and `i18next-http-backend`. * Configured `remix-i18next` middleware (`app/middleware/i18next.ts`) with language detection (cookie-based) and resource loading. * Updated `app/root.tsx` to incorporate the i18n middleware, manage locale state via a loader, and set appropriate HTML attributes. * Modified `app/entry.client.tsx` and `app/entry.server.tsx` to initialize i18next and wrap the application with `I18nextProvider` for both client-side rendering and SSR. 2. **Localization Files:** * Created `app/locales/en.ts` for English (fallback) translations. * Created `app/locales/tr.ts` for Turkish translations. * Populated these files with initial strings for UI elements in the header. 3. **Component Internationalization:** * Modified `app/components/header/Header.tsx` and `app/components/header/HeaderActionButtons.client.tsx` to use the `useTranslation` hook and `t()` function for displaying translated strings. This includes static text, dynamic text with interpolation, and alt attributes for images. 4. **Language Switching:** * Implemented a language switcher dropdown component within `app/components/header/Header.tsx`. * The switcher allows you to select between English and Turkish, with the selection persisted via a cookie. 5. **Documentation:** * Added a new "Internationalization (i18n)" section to `README.md`, detailing how to add/modify translations and support new languages. This work completes Part 1 of the issue, laying the foundation for a multilingual application.
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { useStore } from '@nanostores/react';
|
|
import { ClientOnly } from 'remix-utils/client-only';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { chatStore } from '~/lib/stores/chat';
|
|
import { classNames } from '~/utils/classNames';
|
|
import { HeaderActionButtons } from './HeaderActionButtons.client';
|
|
import { ChatDescription } from '~/lib/persistence/ChatDescription.client';
|
|
|
|
export function Header() {
|
|
const chat = useStore(chatStore);
|
|
const { i18n, t } = useTranslation();
|
|
|
|
const handleChangeLanguage = (lang: string) => {
|
|
i18n.changeLanguage(lang);
|
|
};
|
|
|
|
return (
|
|
<header
|
|
className={classNames('flex items-center p-5 border-b h-[var(--header-height)]', {
|
|
'border-transparent': !chat.started,
|
|
'border-bolt-elements-borderColor': chat.started,
|
|
})}
|
|
>
|
|
<div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary cursor-pointer">
|
|
<div className="i-ph:sidebar-simple-duotone text-xl" />
|
|
<a href="/" className="text-2xl font-semibold text-accent flex items-center">
|
|
{/* <span className="i-bolt:logo-text?mask w-[46px] inline-block" /> */}
|
|
<img src="/logo-light-styled.png" alt={t('logoAlt')} className="w-[90px] inline-block dark:hidden" />
|
|
<img src="/logo-dark-styled.png" alt={t('logoAlt')} className="w-[90px] inline-block hidden dark:block" />
|
|
</a>
|
|
</div>
|
|
|
|
{/* Spacer when chat has not started to push language switcher to the right */}
|
|
{!chat.started && <div className="flex-grow"></div>}
|
|
|
|
{chat.started && (
|
|
<span className="flex-1 px-4 truncate text-center text-bolt-elements-textPrimary">
|
|
<ClientOnly>{() => <ChatDescription />}</ClientOnly>
|
|
</span>
|
|
)}
|
|
|
|
<div className={classNames("flex items-center", { "ml-auto": !chat.started, "ml-2": chat.started })}>
|
|
<select
|
|
value={i18n.language.split('-')[0]} // Use base language (e.g., 'en' from 'en-US')
|
|
onChange={(e) => handleChangeLanguage(e.target.value)}
|
|
className="bg-bolt-elements-backgroundDefault text-bolt-elements-textPrimary border border-bolt-elements-borderColor rounded-md p-1 text-sm focus:ring-accent focus:border-accent"
|
|
aria-label={t('languageSelectorLabel')}
|
|
>
|
|
<option value="en">English</option>
|
|
<option value="tr">Türkçe</option>
|
|
</select>
|
|
</div>
|
|
|
|
{chat.started && (
|
|
<ClientOnly>
|
|
{() => (
|
|
<div className="ml-2"> {/* Adjusted margin for spacing */}
|
|
<HeaderActionButtons />
|
|
</div>
|
|
)}
|
|
</ClientOnly>
|
|
)}
|
|
</header>
|
|
);
|
|
}
|