Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2
🗺 Translations with React hooks
License
streamich/use-t
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
use-t is a lightweight, type-safe React internationalization (i18n) library that leverages React hooks and context for seamless translation management. Built with modern React patterns, it provides an intuitive API for handling translations, dynamic loading, namespaces, and complex interpolations.
🪝Hook-based API — Modern React hooks withuseT()
🚀Zero dependencies — Lightweight and fast
📦Dynamic loading — Load translations on-demand
🏷️Namespace support — Organize translations by feature
🔧Template literals — JSX interpolation witht.t
🌍Fallback locales — Graceful degradation
📝TypeScript — Full type safety and IntelliSense
⚡Multiple APIs — Hooks, HOCs, render props, and context
npm iuse-t
import{Provider,useT}from'use-t';// 1. Define your translationsconsttranslations={en:{main:{greeting:'Hello',welcome:'Welcome back!'}},es:{main:{greeting:'Hola',welcome:'¡Bienvenido de nuevo!'}}};// 2. Create a component that uses translationsconstApp=()=>{const[t,{setLocale, locale}]=useT();return(<div><h1>{t('greeting')}, World!</h1><p>{t('welcome')}</p><buttononClick={()=>setLocale(locale==='en' ?'es' :'en')}> Switch Language</button></div>);};// 3. Wrap your app with Providerexportdefault()=>(<Providerlocale="en"map={translations}><App/></Provider>);
consttranslations={en:{main:{userGreeting:(name)=>`Hello,${name}!`,itemCount:(count)=>`You have${count}${count===1 ?'item' :'items'}`}}};constUserProfile=({username, itemCount})=>{const[t]=useT();return(<div><h2>{t('userGreeting',username)}</h2><p>{t('itemCount',itemCount)}</p></div>);};
consttranslations={en:{main:{welcomeMessage:(interpolate)=>interpolate`Welcome${0}, you have${1} new messages!`}}};constDashboard=({user, messageCount})=>{const[t]=useT();return(<div>{/* With translation */}{t.t('welcomeMessage')`Welcome${user.name}, you have${messageCount} new messages!`}{/* Fallback if translation missing */}{t.t('missingKey')`Default message with${user.name}`}</div>);};
import{Provider,useT,withT,Trans,Consumer,context,createTranslations}from'use-t';
| Export | Type | Description |
|---|---|---|
<Provider> | Component | Context provider for translations |
useT() | Hook | React hook returning[t, state] |
withT() | HOC | Higher-order component injectingt andT props |
<Trans> | Component | Render prop component for translations |
<Consumer> | Component | Context consumer for provider state |
context | Context | React context object |
createTranslations() | Function | Create custom translation instances |
<Providerlocale="en"// Current locale (default: 'en')defaultLocale="en"// Fallback locale (default: 'en')ns="main"// Default namespace (default: 'main')map={translations}// Preloaded translationsloader={loadFn}// Dynamic loader function>
const[t,state]=useT();// Default namespaceconst[t,state]=useT('errors');// Single namespaceconst[t,state]=useT(['main','errors']);// Multiple namespaces
Translation functiont:
t(key)- Simple translationt(key, ...args)- Function translation with argumentst.t(key)- Template literal translation
State object:
state.locale- Current localestate.setLocale(locale)- Change localestate.load(locale, namespace)- Preload translations
constProvider=()=>{constloadTranslations=async(locale,namespace)=>{constresponse=awaitfetch(`/api/translations/${locale}/${namespace}`);returnresponse.json();};return(<Providerlocale="en"loader={loadTranslations}map={{en:{main:{loading:'Loading...'}}// Initial translations}}><App/></Provider>);};
consttranslations={en:{common:{save:'Save',cancel:'Cancel'},errors:{required:'This field is required',invalid:'Invalid input'},dashboard:{title:'Dashboard',stats:'Statistics'}}};// Use multiple namespacesconstForm=()=>{const[t]=useT(['common','errors']);return(<form><buttontype="submit">{t('save')}</button><buttontype="button">{t('cancel')}</button><spanclassName="error">{t('required')}</span></form>);};// Namespace-specific componentconstDashboard=()=>{const[t]=useT('dashboard');return(<div><h1>{t('title')}</h1><p>{t('stats')}</p></div>);};
consttranslations={en:{main:{loginFooter:(interpolate)=>interpolate` By signing in, you agree to our${0} and${1}. `,notification:(interpolate)=>interpolate`${0} sent you${1}${2} `}}};constLoginForm=()=>{const[t]=useT();return(<div><form>...</form><p>{t.t('loginFooter')` By signing in, you agree to our${<Linkto="/terms">Terms</Link>} and${<Linkto="/privacy">Privacy Policy</Link>}. `}</p></div>);};constNotificationItem=({sender, count, type})=>{const[t]=useT();return(<div>{t.t('notification')`${<strong>{sender}</strong>} sent you${count}${type} `}</div>);};
import{withT}from'use-t';constMyComponent=({t, T, ...otherProps})=>(<div><h1>{t('title')}</h1><buttononClick={()=>T.setLocale('es')}> Español</button></div>);exportdefaultwithT(MyComponent);// Or with specific namespace:exportdefaultwithT(MyComponent,'dashboard');
import{Trans}from'use-t';constNavigation=()=>(<nav><Transns="navigation">{(t,T)=>(<><ahref="/">{t('home')}</a><ahref="/about">{t('about')}</a><buttononClick={()=>T.setLocale('fr')}> Français</button></>)}</Trans></nav>);// String shorthandconstTitle=()=><Trans>pageTitle</Trans>;// Mixed contentconstHeader=()=>(<Trans>{t=>t('welcome')}!</Trans>);
use-t is written in TypeScript and provides full type safety:
import{TranslatorFn,ProviderState,TranslationMap}from'use-t';// Type your translationsinterfaceTranslations{greeting:string;userWelcome:(name:string)=>string;itemCount:(count:number)=>string;}consttranslations:TranslationMap={en:{main:{greeting:'Hello',userWelcome:(name:string)=>`Welcome,${name}!`,itemCount:(count:number)=>`${count} items`}asTranslations}};// Typed component propsinterfaceProps{t:TranslatorFn;T:ProviderState;}constMyComponent:React.FC<Props>=({t, T})=>(<div><h1>{t('greeting')}</h1><p>{t('userWelcome','John')}</p></div>);
// ✅ Good: Organize by feature/pageconsttranslations={en:{auth:{login:'Log In',signup:'Sign Up',forgotPassword:'Forgot Password?'},dashboard:{welcome:'Welcome back!',stats:'Your Statistics'},common:{save:'Save',cancel:'Cancel',loading:'Loading...'}}};// ❌ Avoid: All translations in one namespaceconsttranslations={en:{main:{login:'Log In',dashboardWelcome:'Welcome back!',saveButton:'Save',// ... hundreds of keys}}};
// ✅ Good: Load namespaces on-demandconstLazyDashboard=()=>{const[t]=useT('dashboard');// Only loads dashboard namespacereturn<div>{t('title')}</div>;};// ✅ Good: Preload critical translations<Providermap={{en:{common:commonTranslations// Critical UI elements}}}loader={dynamicLoader}// Non-critical loaded on-demand>// ✅ Good: Use default locale as fallback<Providerlocale="fr"defaultLocale="en"// Falls back to English if French missingmap={translations}>
consttranslations={en:{main:{// Use descriptive keys that work as fallbacks'user.welcome':'Welcome!','error.network':'Network error occurred','button.save':'Save'}}};// Keys become fallback text if translation missingconstComponent=()=>{const[t]=useT();return(<div>{/* Shows "Welcome!" or "user.welcome" if missing */}<h1>{t('user.welcome')}</h1>{/* Shows "Save" or "button.save" if missing */}<button>{t('button.save')}</button></div>);};
Create isolated translation contexts for libraries or complex apps:
import{createTranslations}from'use-t';// Create custom instance with different default namespaceconst{Provider:LibProvider,useT:useLibT}=createTranslations('library');constLibraryComponent=()=>{const[t]=useLibT();return<div>{t('libMessage')}</div>;};// Use in your app<LibProvidermap={{en:{library:{libMessage:'Hello from library!'}}}}><LibraryComponent/></LibProvider>
// react-i18nextimport{useTranslation}from'react-i18next';const{t, i18n}=useTranslation();t('key');i18n.changeLanguage('es');// use-t equivalentimport{useT}from'use-t';const[t,{setLocale}]=useT();t('key');setLocale('es');
// React Intlimport{useIntl}from'react-intl';constintl=useIntl();intl.formatMessage({id:'key'});// use-t equivalentimport{useT}from'use-t';const[t]=useT();t('key');
Translation not showing:
- Check if the key exists in your translation map
- Verify the correct namespace is being used
- Ensure Provider is wrapping your component
- Check browser console for loading errors
Dynamic loading not working:
- Verify your loader function returns a Promise
- Check network requests in browser dev tools
- Ensure proper error handling in loader
TypeScript errors:
- Import types:
import type {TranslatorFn} from 'use-t'; - Check translation map structure matches expected format
For comprehensive API documentation, see:
<Provider>- Context provider configurationuseT()- React hook usage and exampleswithT()- Higher-order component patterns<Trans>- Render prop component usage<Consumer>- Context consumer patternscontext- Direct context accesscreateTranslations()- Custom instances
Unlicense — public domain.
About
🗺 Translations with React hooks
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.