Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.refactkit.com/llms.txt

Use this file to discover all available pages before exploring further.

RefactKit ships with full internationalization support covering five locales and automatic RTL layout switching. Rather than using the full i18next instance directly, translations are loaded into a lightweight React context in src/i18n/context.tsx that exposes typed translation objects and a locale setter. This means you get TypeScript autocomplete on every translation key without any extra configuration.

Supported locales

LocaleLanguageDirection
enEnglishLTR
frFrenchLTR
esSpanishLTR
ptPortugueseLTR
arArabicRTL
The active locale is persisted in a cookie (lk_locale) so the server can render the correct language on the first request — no flash of the wrong language during hydration.

How i18n works

src/i18n/index.ts defines the Locale type, the locale cookie name, and two functions:
  • getTranslations(locale) — returns the typed translation object for a given locale
  • getServerLocale — a server function that reads the locale from the request cookie
src/i18n/context.tsx wraps these in a React context that any component can consume:
// src/i18n/context.tsx
export function I18nProvider({
  children,
  initialLocale,
}: {
  children: ReactNode
  initialLocale?: Locale
}) {
  const [locale, setLocaleState] = useState<Locale>(initialLocale || 'en')

  function setLocale(next: Locale) {
    setLocaleState(next)
    setLocaleCookie(next)
  }

  const dir = locale === 'ar' ? 'rtl' : 'ltr'

  useEffect(() => {
    document.documentElement.dir = dir
    document.documentElement.lang = locale
  }, [dir, locale])

  return (
    <I18nContext.Provider value={{ locale, dir, t: getTranslations(locale), setLocale }}>
      {children}
    </I18nContext.Provider>
  )
}

export function useI18n() {
  return useContext(I18nContext)
}
When the user switches locale, setLocale updates the React state, writes the cookie, and the useEffect updates the dir and lang attributes on <html> — which triggers the direction-aware font switch automatically.

Use translations in a component

Call useI18n() to get the translation object t and the current locale:
import { useI18n } from '@/i18n/context'

function DashboardPage() {
  const { t } = useI18n()
  return <h1>{t.dashboard.welcome.replace('{{org}}', org.name)}</h1>
}
Follow the t.key.path convention — traverse the nested translation object with dot notation. TypeScript will flag any missing or misspelled keys at compile time because t is typed as Translations (the shape of src/i18n/locales/en.ts).

Add a new translation key

1

Add the key to the English locale

Open src/i18n/locales/en.ts — this file is the Translations type definition. Add your key in the appropriate namespace:
// src/i18n/locales/en.ts
export const en = {
  // ... existing keys ...
  myFeature: {
    title: 'My Feature',
    description: 'This is my new feature.',
    actionButton: 'Get started',
  },
}
2

Add the key to all other locales

Open each of the remaining locale files (fr.ts, es.ts, pt.ts, ar.ts) and add the translated value for the same key. TypeScript will show a type error if any locale is missing a key that exists in en.ts.
// src/i18n/locales/fr.ts
export const fr = {
  myFeature: {
    title: 'Ma fonctionnalité',
    description: 'Ceci est ma nouvelle fonctionnalité.',
    actionButton: 'Commencer',
  },
}
3

Use the key in your component

const { t } = useI18n()
return <Button>{t.myFeature.actionButton}</Button>

Add a new locale

1

Create the locale file

Create src/i18n/locales/de.ts (or whichever language code you need) and export a complete translation object that matches the shape of en.ts:
// src/i18n/locales/de.ts
import type { Translations } from './en'

export const de: Translations = {
  common: {
    // ... all keys from en.ts translated to German
  },
  // ...
}
2

Register the locale in src/i18n/index.ts

Import the new locale and add it to the locales map and the Locale type:
// src/i18n/index.ts
import { de } from './locales/de'

export type Locale = 'en' | 'fr' | 'ar' | 'es' | 'pt' | 'de'

const locales: Record<Locale, Translations> = { en, fr, ar, es, pt, de }
Also update the detectLocale and getServerLocale functions to handle the new locale value from the cookie.
3

Add the locale to the language switcher

Update the lang translations object in each locale file to include the new language label, then add the new option to the LangSwitcher component in src/components/shared/lang-switcher.tsx.

Direction-aware fonts

The app automatically switches fonts based on the document direction:
  • LTR (English, French, Spanish, Portuguese): Google Sans Flex
  • RTL (Arabic): Zain
The switch happens at the CSS level using the data-font attribute on <html>. The useFont hook in src/hooks/use-font.ts manages this attribute and persists the user’s choice to localStorage:
// src/hooks/use-font.ts
export type Font = 'default' | 'google-sans' | 'zain' | 'geist' | 'baloo'

export function useFont() {
  const setFont = (newFont: Font) => {
    setFontState(newFont)
    localStorage.setItem(FONT_KEY, newFont)
    if (newFont === 'default') {
      document.documentElement.removeAttribute('data-font')
    } else {
      document.documentElement.setAttribute('data-font', newFont)
    }
  }
  // ...
}

Add a new font

1

Install the font package

pnpm add @fontsource/font-name
2

Import in globals.css

@import "@fontsource/font-name";
3

Register the CSS variable

Add a data-font selector to globals.css that sets the --font-family variable:
[data-font="my-font"] {
  --font-family: "My Font", sans-serif;
}
4

Update the Font type in use-font.ts

Add the new font key to the Font union type in src/hooks/use-font.ts:
export type Font = 'default' | 'google-sans' | 'zain' | 'geist' | 'baloo' | 'my-font'
5

Add to the appearance settings dropdown

Open src/components/settings/account/appearance.tsx and add the new font as an option in the font selector dropdown. Users will then be able to choose it from their account settings.
When adding a font intended for RTL use, set data-font conditionally based on dir rather than as a manual user choice. This ensures RTL users always get an appropriate font regardless of their manual preference setting.

Locale detection: server vs. client

RefactKit reads the locale from two places depending on context:
  • Server: getServerLocale() parses the lk_locale cookie from the incoming request headers. Call this in your root route loader to pass initialLocale to <I18nProvider>.
  • Client: detectLocale() reads the same cookie via js-cookie. This is used for the initial state on the client when SSR data is not available.
Both functions fall back to fr if no cookie is set. Change the fallback locale in src/i18n/index.ts if your default audience speaks a different language.