lang-provider
v1.0.5
Published
A flexible React provider for managing multilingual applications with **TypeScript** support and **automatic HTML lang attribute management**.
Readme
lang-provider
A flexible React provider for managing multilingual applications with TypeScript support and automatic HTML lang attribute management.
Features
- 🌍 Multi-language support
- 🎯 Automatic HTML lang attribute updates
- 🔧 Flexible configuration with custom translation loading
- 📱 SSR-safe - works in both browser and server environments
- 🎨 Zero dependencies (except React)
- 🔥 Lightweight - minimal bundle size
Installation
npm install lang-providerQuick Start
Follow these steps to get started with using lang-provider:
// Define your translations
const translations = {
sv: {
welcome: "Välkommen",
portfolio: "Min portfölj"
},
en: {
welcome: "Welcome",
portfolio: "My Portfolio"
}
};
// Create typed config
const config = {
defaultLanguage: "sv" as const, // Type assertion for literal type
languages: ["sv", "en"] as const, // Type assertion for tuple type
translations
};
// Infer types from config
type AppLanguages = typeof config.languages[number]; // Results in "sv" | "en"
type TextKeys = keyof typeof config.translations.sv & string; // Results in "welcome" | "portfolio"
const App = () => (
<LangProvider<AppLanguages, TextKeys> config={config}>
<TextProvider<AppLanguages, TextKeys> config={config}>
<YourApp />
</TextProvider>
</LangProvider>
);
// 🎯 HTML lang attribute is automatically set to "sv" when LangProvider mounts
// 🔄 Use switchLanguage('en') to change both state and <html lang="en">Configuration
Translation File Structure
{
"sv": {
"welcome": "Välkommen",
"portfolio": "Min portfölj"
},
"en": {
"welcome": "Welcome",
"portfolio": "My Portfolio"
}
}Config Interface
interface LanguageConfig<L extends string, K extends string> {
defaultLanguage: L; // The fallback language
languages: readonly L[] | L[]; // Array of supported languages
translations: Record<L, Record<K, string>>; // Translations map
}Hooks
useLang
Returns language control functions and state:
const {
language, // Current active language
switchLanguage, // Function to change language: (lang: L) => void
availableLanguages // Array of supported languages: L[]
} = useLang();useText
Returns text management functions:
const {
getText, // Get translated text: (key: K) => string
setTexts // Update translations: (texts: Record<L, Record<K, string>>) => void
} = useText();Usage Examples
Switching Languages
import { useLang } from "lang-provider";
const LanguageSelector = () => {
const { switchLanguage, availableLanguages, language } = useLang();
return (
<div>
{availableLanguages.map(lang => (
<button
key={lang}
onClick={() => switchLanguage(lang)}
disabled={lang === language}
>
{lang.toUpperCase()}
</button>
))}
</div>
);
};Displaying Translated Text
import { useText } from "lang-provider";
const Welcome = () => {
const { getText } = useText();
return <h1>{getText('welcome')}</h1>;
};Update Translations
The setTexts function from useText allows you to update translations at runtime. Here's a complete example:
import { useText } from "lang-provider";
import type { LanguageConfig } from "lang-provider";
// Define your types
type Languages = "en" | "sv";
type TextKeys = "welcome" | "portfolio";
const TranslationUpdater = () => {
const { setTexts } = useText();
const updateTranslations = async () => {
try {
// Fetch new translations from your API/source
const response = await fetch('https://api.example.com/translations');
const newTranslations: LanguageConfig<Languages, TextKeys>['translations'] = await response.json();
// Validate the structure matches your types
if (isValidTranslation(newTranslations)) {
setTexts(newTranslations);
}
} catch (error) {
console.error('Failed to update translations:', error);
}
};
// Type guard to validate translation structure
const isValidTranslation = (
data: unknown
): data is Record<Languages, Record<TextKeys, string>> => {
if (!data || typeof data !== 'object') return false;
const requiredLanguages: Languages[] = ['en', 'sv'];
const requiredKeys: TextKeys[] = ['welcome', 'portfolio'];
return requiredLanguages.every(lang =>
typeof data[lang] === 'object' &&
requiredKeys.every(key =>
typeof data[lang][key] === 'string'
)
);
};
return <button onClick={updateTranslations}>Update Translations</button>;
};Your translation API should return data in this format:
{
"sv": {
"welcome": "Välkommen",
"portfolio": "Min portfölj"
},
"en": {
"welcome": "Welcome",
"portfolio": "My Portfolio"
}
}The setTexts function will:
- Update the internal translation state
- Trigger a re-render of components using getText
- Preserve type safety with the provided generic types
Automatic HTML Lang Attribute
The lang-provider automatically updates the HTML document's lang attribute when the language changes.
How it Works
When you use switchLanguage(), the provider automatically:
- Updates the component state with the new language
- Sets
<html lang="...">attribute to match the selected language - Validates the language is in the allowed list before changing
const { switchLanguage } = useLang();
// This will:
// 1. Change the internal language state to 'en'
// 2. Set <html lang="en"> on the page
switchLanguage('en');
// This will:
// 1. Change the internal language state to 'sv'
// 2. Set <html lang="sv"> on the page
switchLanguage('sv');
// This will do nothing (invalid language)
switchLanguage('fr'); // Not in supported languagesBenefits
- 🎯 Accessibility: Screen readers and assistive technologies get correct language information
- 🌐 Browser Features: Form validation, spell-check, and autocomplete use the correct language
- 🔍 SEO: Search engines receive proper language metadata
- 🔄 Translation: Browser translation features work more accurately
Server-Side Rendering (SSR)
The HTML lang functionality is SSR-safe:
- ✅ Works in browser environments (sets HTML lang attribute)
- ✅ Safe in Node.js/SSR (gracefully skips DOM operations)
- ✅ No errors or warnings in server-side rendering
Initial Setup
The HTML lang attribute is set automatically when the LangProvider mounts:
// This will set <html lang="sv"> when the component first renders
<LangProvider config={{
defaultLanguage: 'sv',
languages: ['sv', 'en'],
translations: {...}
}}>
<App />
</LangProvider>TypeScript Support
Language Type
type AppLanguages = "en" | "sv"; // Your supported languagesText Keys
type TextKeys = "welcome" | "portfolio"; // Your translation keysProvider Types
<LangProvider<AppLanguages, TextKeys> config={config}>
<TextProvider<AppLanguages, TextKeys> config={config}>Best Practices
- Keep translations in separate JSON files
- Use TypeScript for type safety
- Use namespaced keys (e.g.,
common.welcome) - Always provide fallback texts in default language
- Load translations asynchronously for large applications
- Use constant assertions for better type inference
API Reference
LangProvider Props
config:LanguageConfigobjectchildren: React nodes
TextProvider Props
config:LanguageConfigobjectchildren: React nodes
LanguageConfig
defaultLanguage: Default language codelanguages: Array of supported language codestranslations: Translation key-value pairs
Hook Returns
useLang
language: Current language codeswitchLanguage: (lang: L) => voidavailableLanguages: L[]
useText
getText: (key: K) => stringsetTexts: (texts: Record<L, Record<K, string>>) => void
