CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-aria--i18n

Comprehensive internationalization support for React applications with locale-aware hooks and utilities

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

string-localization.mddocs/

String Localization and Search

Advanced string handling including localization with interpolation, collation, and locale-aware text filtering capabilities.

Capabilities

useLocalizedStringFormatter Hook

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>
  );
}

useLocalizedStringDictionary Hook

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>;

useMessageFormatter Hook (Deprecated)

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;

useCollator Hook

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>
  );
}

useFilter Hook

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

docs

context-locale.md

date-time-formatting.md

index.md

number-list-formatting.md

server-support.md

string-localization.md

tile.json