bolt.diy/app/components/header/Header.tsx
google-labs-jules[bot] 891257c1e2 feat: Implement internationalization and add Turkish language support
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.
2025-06-07 16:45:43 +00:00

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>
);
}