Comprehensive internationalization support for React applications with locale-aware hooks and utilities
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advanced string handling including localization with interpolation, collation, and locale-aware text filtering capabilities.
Provides localized string formatting with support for interpolation, pluralization, and variable substitution.
/**
* Provides localized string formatting for the current locale. Supports interpolating variables,
* selecting the correct pluralization, and formatting numbers. Automatically updates when the locale changes.
* @param strings - A mapping of languages to localized strings by key
* @param packageName - Optional package name for global dictionary lookup
* @returns LocalizedStringFormatter instance
*/
function useLocalizedStringFormatter<K extends string = string, T extends LocalizedString = string>(
strings: LocalizedStrings<K, T>,
packageName?: string
): LocalizedStringFormatter<K, T>;
type LocalizedStrings<K extends string = string, T extends LocalizedString = string> = Record<string, Record<K, T>>;
type LocalizedString = string | LocalizedStringParams;
interface LocalizedStringParams {
/** The default string value */
message: string;
/** Optional description for translators */
description?: string;
}Usage Examples:
import { useLocalizedStringFormatter, I18nProvider } from "@react-aria/i18n";
// Define localized strings
const strings = {
"en-US": {
greeting: "Hello, {name}!",
itemCount: {
message: "{count, plural, =0 {No items} =1 {One item} other {# items}}",
description: "Number of items in the list"
},
welcome: "Welcome to our application"
},
"es-ES": {
greeting: "¡Hola, {name}!",
itemCount: {
message: "{count, plural, =0 {Sin elementos} =1 {Un elemento} other {# elementos}}",
description: "Número de elementos en la lista"
},
welcome: "Bienvenido a nuestra aplicación"
},
"fr-FR": {
greeting: "Bonjour, {name} !",
itemCount: {
message: "{count, plural, =0 {Aucun élément} =1 {Un élément} other {# éléments}}",
description: "Nombre d'éléments dans la liste"
},
welcome: "Bienvenue dans notre application"
}
};
// Basic string localization
function LocalizedGreeting() {
const formatter = useLocalizedStringFormatter(strings);
return (
<div>
<p>{formatter.format("welcome")}</p>
<p>{formatter.format("greeting", { name: "Maria" })}</p>
</div>
);
}
// Pluralization example
function ItemCounter({ count }: { count: number }) {
const formatter = useLocalizedStringFormatter(strings);
return (
<p>{formatter.format("itemCount", { count })}</p>
);
}
// Using with different locales
function MultiLocaleExample() {
return (
<div>
<I18nProvider locale="en-US">
<LocalizedContent />
</I18nProvider>
<I18nProvider locale="es-ES">
<LocalizedContent />
</I18nProvider>
<I18nProvider locale="fr-FR">
<LocalizedContent />
</I18nProvider>
</div>
);
}
function LocalizedContent() {
const formatter = useLocalizedStringFormatter(strings);
return (
<div>
<h2>{formatter.format("welcome")}</h2>
<p>{formatter.format("greeting", { name: "Alex" })}</p>
<p>{formatter.format("itemCount", { count: 3 })}</p>
</div>
);
}Returns a cached LocalizedStringDictionary for reuse across components.
/**
* Returns a cached LocalizedStringDictionary for the given strings.
* @param strings - A mapping of languages to localized strings by key
* @param packageName - Optional package name for global dictionary lookup
* @returns LocalizedStringDictionary instance
*/
function useLocalizedStringDictionary<K extends string = string, T extends LocalizedString = string>(
strings: LocalizedStrings<K, T>,
packageName?: string
): LocalizedStringDictionary<K, T>;Legacy hook for ICU message formatting. Use useLocalizedStringFormatter instead.
/**
* Handles formatting ICU Message strings to create localized strings for the current locale.
* @deprecated - use useLocalizedStringFormatter instead
* @param strings - A mapping of languages to strings by key
* @returns FormatMessage function
*/
function useMessageFormatter(strings: LocalizedStrings): FormatMessage;
type FormatMessage = (key: string, variables?: {[key: string]: any}) => string;Provides locale-aware string collation for sorting and comparison operations.
/**
* Provides localized string collation for the current locale. Automatically updates when the locale changes,
* and handles caching of the collator for performance.
* @param options - Collator options for comparison behavior
* @returns Intl.Collator instance
*/
function useCollator(options?: Intl.CollatorOptions): Intl.Collator;
interface Intl.CollatorOptions {
/** The locale matching algorithm to use */
localeMatcher?: "best fit" | "lookup";
/** Whether to use numeric collation */
numeric?: boolean;
/** Case sensitivity */
caseFirst?: "upper" | "lower" | "false";
/** Sensitivity level */
sensitivity?: "base" | "accent" | "case" | "variant";
/** Whether to ignore punctuation */
ignorePunctuation?: boolean;
/** Usage hint */
usage?: "sort" | "search";
}Usage Examples:
import { useCollator } from "@react-aria/i18n";
// Basic string comparison and sorting
function StringComparison() {
const collator = useCollator();
const compare = (a: string, b: string) => collator.compare(a, b);
const items = ["zebra", "apple", "banana"];
const sortedItems = items.sort(compare);
return (
<ul>
{sortedItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
}
// Case-insensitive comparison
function CaseInsensitiveSort() {
const collator = useCollator({
sensitivity: "base" // ignore case and accents
});
const items = ["Apple", "banana", "Cherry"];
const sorted = items.sort(collator.compare);
return <p>Sorted: {sorted.join(", ")}</p>;
}
// Numeric sorting
function NumericSort() {
const collator = useCollator({
numeric: true
});
const files = ["file1.txt", "file10.txt", "file2.txt"];
const sorted = files.sort(collator.compare);
return <p>Files: {sorted.join(", ")}</p>;
// Result: "file1.txt, file2.txt, file10.txt"
}
// Accent-sensitive comparison
function AccentComparison() {
const collator = useCollator({
sensitivity: "accent"
});
const words = ["café", "cafe", "naïve", "naive"];
const grouped = words.reduce((acc, word) => {
const group = acc.find(g => collator.compare(g[0], word) === 0);
if (group) {
group.push(word);
} else {
acc.push([word]);
}
return acc;
}, [] as string[][]);
return (
<div>
{grouped.map((group, i) => (
<p key={i}>Group {i + 1}: {group.join(", ")}</p>
))}
</div>
);
}Provides locale-aware string filtering and search functionality.
/**
* Provides localized string search functionality that is useful for filtering or matching items
* in a list. Options can be provided to adjust the sensitivity to case, diacritics, and other parameters.
* @param options - Collator options affecting search behavior
* @returns Filter object with search methods
*/
function useFilter(options?: Intl.CollatorOptions): Filter;
interface Filter {
/** Returns whether a string starts with a given substring */
startsWith(string: string, substring: string): boolean;
/** Returns whether a string ends with a given substring */
endsWith(string: string, substring: string): boolean;
/** Returns whether a string contains a given substring */
contains(string: string, substring: string): boolean;
}Usage Examples:
import { useFilter } from "@react-aria/i18n";
// Basic filtering
function SearchableList() {
const filter = useFilter();
const [query, setQuery] = useState("");
const items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
const filteredItems = items.filter(item =>
filter.contains(item.toLowerCase(), query.toLowerCase())
);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search items..."
/>
<ul>
{filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
// Case-insensitive filtering
function CaseInsensitiveFilter() {
const filter = useFilter({
sensitivity: "base" // ignore case and accents
});
const names = ["André", "Anna", "Björn", "Café"];
const query = "an";
const matches = names.filter(name => filter.contains(name, query));
return <p>Matches for "{query}": {matches.join(", ")}</p>;
}
// Prefix filtering
function PrefixFilter() {
const filter = useFilter();
const [prefix, setPrefix] = useState("");
const words = ["react", "redux", "router", "component", "context"];
const prefixMatches = words.filter(word =>
filter.startsWith(word, prefix)
);
return (
<div>
<input
value={prefix}
onChange={(e) => setPrefix(e.target.value)}
placeholder="Type prefix..."
/>
<p>Words starting with "{prefix}": {prefixMatches.join(", ")}</p>
</div>
);
}
// Suffix filtering
function SuffixFilter() {
const filter = useFilter();
const files = ["document.pdf", "image.png", "script.js", "style.css"];
const extension = ".js";
const jsFiles = files.filter(file =>
filter.endsWith(file, extension)
);
return <p>JavaScript files: {jsFiles.join(", ")}</p>;
}
// Advanced search with multiple criteria
function AdvancedSearch() {
const filter = useFilter({
sensitivity: "base",
ignorePunctuation: true
});
const products = [
"iPhone 14 Pro",
"Samsung Galaxy S23",
"Google Pixel 7",
"OnePlus 11"
];
const [searchTerm, setSearchTerm] = useState("");
const results = products.filter(product => {
const terms = searchTerm.split(" ");
return terms.every(term =>
filter.contains(product, term)
);
});
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search products..."
/>
<ul>
{results.map(product => (
<li key={product}>{product}</li>
))}
</ul>
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-aria--i18n