A lightweight, customizable internationalization library for React inspired by FormatJS, but targeting modern browsers for enhanced performance and tree-shaking capabilities.
- πͺΆ Lightweight: Minimal bundle size with excellent tree-shaking support
- β‘ Modern: Built for modern browsers using native browser APIs
- π¨ Customizable: Flexible rendering with custom
onRenderfunctions - π§ Developer Experience: Comprehensive CLI for message extraction and validation
- π ICU Support: Native browser support for ICU message formatting (plural, select, selectordinal)
- π¦ Monorepo: Separate packages for React components and CLI tools
- β TypeScript: Full TypeScript support with excellent type safety
This monorepo contains two main packages:
| Package | Description | Version |
|---|---|---|
@zero-intl/core |
Core library | |
@zero-intl/react |
React components and hooks for internationalization | |
@zero-intl/cli |
CLI tool for extracting and validating translation keys |
# Install the React library
npm install @zero-intl/react
# Install the CLI (optional, for development)
npm install -g @zero-intl/cliimport React from 'react';
import { ZeroIntlProvider, T, useIntl } from '@zero-intl/react';
const messages = {
'welcome.title': 'Welcome to Zero Intl!',
'welcome.subtitle': 'A modern internationalization library',
'user.greeting': 'Hello, {name}!'
};
function App() {
return (
<ZeroIntlProvider
locale="en"
messages={messages}
defaultLocale="en"
>
<WelcomeComponent />
</ZeroIntlProvider>
);
}
function WelcomeComponent() {
const intl = useIntl();
return (
<div>
<h1>
<T id="welcome.title" defaultMessage="Welcome to Zero Intl!" />
</h1>
<p>
<T id="welcome.subtitle" defaultMessage="A modern internationalization library" />
</p>
<p>
{intl.formatMessage({
id: 'user.greeting',
defaultMessage: 'Hello, {name}!',
values: { name: 'John' }
})}
</p>
</div>
);
}function App() {
return (
<ZeroIntlProvider
locale="en"
messages={messages}
onRender={(record) => (
<span data-translation-key={record.translationKey}>
{record.translation}
</span>
)}
>
<YourApp />
</ZeroIntlProvider>
);
}Zero Intl supports native browser ICU formatting:
const messages = {
'items.count': '{count, plural, =0 {No items} =1 {One item} other {# items}}',
'user.gender': '{gender, select, male {He} female {She} other {They}} will arrive soon',
'ranking': '{place, selectordinal, =1 {1st} =2 {2nd} =3 {3rd} other {#th}} place'
};
function ItemCounter({ count }: { count: number }) {
return (
<T
id="items.count"
defaultMessage="{count, plural, =0 {No items} =1 {One item} other {# items}}"
values={{ count }}
/>
);
}Extract translation keys from your source code:
# Extract all translation keys
npx @zero-intl/cli extract "src/**/*.{ts,tsx}" --output messages.json# Clone the repository
git clone https://github.com/zero-intl/zero-intl.git
cd zero-intl
# Install dependencies
npm install
# Build all packages
npm run build
# Run tests
npm test
# Run tests in CI mode
npm run test:ci# Build specific package
npm run build -w @zero-intl/react
npm run build -w @zero-intl/cli
# Test specific package
npm run test -w @zero-intl/react
npm run test -w @zero-intl/cli
# Development mode (with watch)
npm run dev -w @zero-intl/reactThe @zero-intl/react package provides:
- ZeroIntlProvider: Context provider for internationalization
- T Component: Declarative component for displaying translated messages
- useIntl Hook: Hook for imperative message formatting
- ICU Support: Native browser support for plural, select, and selectordinal
Read the full React documentation β
Zero Intl is designed with the following principles:
- Modern First: Target modern browsers to leverage native APIs and reduce bundle size
- Tree-Shakable: Only include what you use in your final bundle
- Customizable: Provide flexible APIs for different use cases
- Developer Experience: Excellent tooling and TypeScript support
- Performance: Minimal runtime overhead and optimal bundle size
interface ZeroIntlProviderProps {
locale: string;
messages: Record<string, string>;
defaultLocale?: string;
onRender?: (record: TranslationRecord) => React.ReactNode;
children: React.ReactNode;
}interface TProps {
id: string;
defaultMessage?: string;
values?: Record<string, any>;
children?: (formattedMessage: string) => React.ReactNode;
}We welcome contributions!
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes and add tests
- Run tests:
npm test - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by FormatJS for the API design
- Thanks to the React team for the excellent Context API
- Built with modern tools: TypeScript, Vitest, tsup
- π Documentation
- π Issue Tracker
- π¬ Discussions
Made with β€οΈ for the modern web