0
# String Localization and Search
1
2
Advanced string handling including localization with interpolation, collation, and locale-aware text filtering capabilities.
3
4
## Capabilities
5
6
### useLocalizedStringFormatter Hook
7
8
Provides localized string formatting with support for interpolation, pluralization, and variable substitution.
9
10
```typescript { .api }
11
/**
12
* Provides localized string formatting for the current locale. Supports interpolating variables,
13
* selecting the correct pluralization, and formatting numbers. Automatically updates when the locale changes.
14
* @param strings - A mapping of languages to localized strings by key
15
* @param packageName - Optional package name for global dictionary lookup
16
* @returns LocalizedStringFormatter instance
17
*/
18
function useLocalizedStringFormatter<K extends string = string, T extends LocalizedString = string>(
19
strings: LocalizedStrings<K, T>,
20
packageName?: string
21
): LocalizedStringFormatter<K, T>;
22
23
type LocalizedStrings<K extends string = string, T extends LocalizedString = string> = Record<string, Record<K, T>>;
24
25
type LocalizedString = string | LocalizedStringParams;
26
27
interface LocalizedStringParams {
28
/** The default string value */
29
message: string;
30
/** Optional description for translators */
31
description?: string;
32
}
33
```
34
35
**Usage Examples:**
36
37
```typescript
38
import { useLocalizedStringFormatter, I18nProvider } from "@react-aria/i18n";
39
40
// Define localized strings
41
const strings = {
42
"en-US": {
43
greeting: "Hello, {name}!",
44
itemCount: {
45
message: "{count, plural, =0 {No items} =1 {One item} other {# items}}",
46
description: "Number of items in the list"
47
},
48
welcome: "Welcome to our application"
49
},
50
"es-ES": {
51
greeting: "¡Hola, {name}!",
52
itemCount: {
53
message: "{count, plural, =0 {Sin elementos} =1 {Un elemento} other {# elementos}}",
54
description: "Número de elementos en la lista"
55
},
56
welcome: "Bienvenido a nuestra aplicación"
57
},
58
"fr-FR": {
59
greeting: "Bonjour, {name} !",
60
itemCount: {
61
message: "{count, plural, =0 {Aucun élément} =1 {Un élément} other {# éléments}}",
62
description: "Nombre d'éléments dans la liste"
63
},
64
welcome: "Bienvenue dans notre application"
65
}
66
};
67
68
// Basic string localization
69
function LocalizedGreeting() {
70
const formatter = useLocalizedStringFormatter(strings);
71
72
return (
73
<div>
74
<p>{formatter.format("welcome")}</p>
75
<p>{formatter.format("greeting", { name: "Maria" })}</p>
76
</div>
77
);
78
}
79
80
// Pluralization example
81
function ItemCounter({ count }: { count: number }) {
82
const formatter = useLocalizedStringFormatter(strings);
83
84
return (
85
<p>{formatter.format("itemCount", { count })}</p>
86
);
87
}
88
89
// Using with different locales
90
function MultiLocaleExample() {
91
return (
92
<div>
93
<I18nProvider locale="en-US">
94
<LocalizedContent />
95
</I18nProvider>
96
<I18nProvider locale="es-ES">
97
<LocalizedContent />
98
</I18nProvider>
99
<I18nProvider locale="fr-FR">
100
<LocalizedContent />
101
</I18nProvider>
102
</div>
103
);
104
}
105
106
function LocalizedContent() {
107
const formatter = useLocalizedStringFormatter(strings);
108
109
return (
110
<div>
111
<h2>{formatter.format("welcome")}</h2>
112
<p>{formatter.format("greeting", { name: "Alex" })}</p>
113
<p>{formatter.format("itemCount", { count: 3 })}</p>
114
</div>
115
);
116
}
117
```
118
119
### useLocalizedStringDictionary Hook
120
121
Returns a cached LocalizedStringDictionary for reuse across components.
122
123
```typescript { .api }
124
/**
125
* Returns a cached LocalizedStringDictionary for the given strings.
126
* @param strings - A mapping of languages to localized strings by key
127
* @param packageName - Optional package name for global dictionary lookup
128
* @returns LocalizedStringDictionary instance
129
*/
130
function useLocalizedStringDictionary<K extends string = string, T extends LocalizedString = string>(
131
strings: LocalizedStrings<K, T>,
132
packageName?: string
133
): LocalizedStringDictionary<K, T>;
134
```
135
136
### useMessageFormatter Hook (Deprecated)
137
138
Legacy hook for ICU message formatting. Use `useLocalizedStringFormatter` instead.
139
140
```typescript { .api }
141
/**
142
* Handles formatting ICU Message strings to create localized strings for the current locale.
143
* @deprecated - use useLocalizedStringFormatter instead
144
* @param strings - A mapping of languages to strings by key
145
* @returns FormatMessage function
146
*/
147
function useMessageFormatter(strings: LocalizedStrings): FormatMessage;
148
149
type FormatMessage = (key: string, variables?: {[key: string]: any}) => string;
150
```
151
152
### useCollator Hook
153
154
Provides locale-aware string collation for sorting and comparison operations.
155
156
```typescript { .api }
157
/**
158
* Provides localized string collation for the current locale. Automatically updates when the locale changes,
159
* and handles caching of the collator for performance.
160
* @param options - Collator options for comparison behavior
161
* @returns Intl.Collator instance
162
*/
163
function useCollator(options?: Intl.CollatorOptions): Intl.Collator;
164
165
interface Intl.CollatorOptions {
166
/** The locale matching algorithm to use */
167
localeMatcher?: "best fit" | "lookup";
168
/** Whether to use numeric collation */
169
numeric?: boolean;
170
/** Case sensitivity */
171
caseFirst?: "upper" | "lower" | "false";
172
/** Sensitivity level */
173
sensitivity?: "base" | "accent" | "case" | "variant";
174
/** Whether to ignore punctuation */
175
ignorePunctuation?: boolean;
176
/** Usage hint */
177
usage?: "sort" | "search";
178
}
179
```
180
181
**Usage Examples:**
182
183
```typescript
184
import { useCollator } from "@react-aria/i18n";
185
186
// Basic string comparison and sorting
187
function StringComparison() {
188
const collator = useCollator();
189
190
const compare = (a: string, b: string) => collator.compare(a, b);
191
192
const items = ["zebra", "apple", "banana"];
193
const sortedItems = items.sort(compare);
194
195
return (
196
<ul>
197
{sortedItems.map(item => (
198
<li key={item}>{item}</li>
199
))}
200
</ul>
201
);
202
}
203
204
// Case-insensitive comparison
205
function CaseInsensitiveSort() {
206
const collator = useCollator({
207
sensitivity: "base" // ignore case and accents
208
});
209
210
const items = ["Apple", "banana", "Cherry"];
211
const sorted = items.sort(collator.compare);
212
213
return <p>Sorted: {sorted.join(", ")}</p>;
214
}
215
216
// Numeric sorting
217
function NumericSort() {
218
const collator = useCollator({
219
numeric: true
220
});
221
222
const files = ["file1.txt", "file10.txt", "file2.txt"];
223
const sorted = files.sort(collator.compare);
224
225
return <p>Files: {sorted.join(", ")}</p>;
226
// Result: "file1.txt, file2.txt, file10.txt"
227
}
228
229
// Accent-sensitive comparison
230
function AccentComparison() {
231
const collator = useCollator({
232
sensitivity: "accent"
233
});
234
235
const words = ["café", "cafe", "naïve", "naive"];
236
const grouped = words.reduce((acc, word) => {
237
const group = acc.find(g => collator.compare(g[0], word) === 0);
238
if (group) {
239
group.push(word);
240
} else {
241
acc.push([word]);
242
}
243
return acc;
244
}, [] as string[][]);
245
246
return (
247
<div>
248
{grouped.map((group, i) => (
249
<p key={i}>Group {i + 1}: {group.join(", ")}</p>
250
))}
251
</div>
252
);
253
}
254
```
255
256
### useFilter Hook
257
258
Provides locale-aware string filtering and search functionality.
259
260
```typescript { .api }
261
/**
262
* Provides localized string search functionality that is useful for filtering or matching items
263
* in a list. Options can be provided to adjust the sensitivity to case, diacritics, and other parameters.
264
* @param options - Collator options affecting search behavior
265
* @returns Filter object with search methods
266
*/
267
function useFilter(options?: Intl.CollatorOptions): Filter;
268
269
interface Filter {
270
/** Returns whether a string starts with a given substring */
271
startsWith(string: string, substring: string): boolean;
272
/** Returns whether a string ends with a given substring */
273
endsWith(string: string, substring: string): boolean;
274
/** Returns whether a string contains a given substring */
275
contains(string: string, substring: string): boolean;
276
}
277
```
278
279
**Usage Examples:**
280
281
```typescript
282
import { useFilter } from "@react-aria/i18n";
283
284
// Basic filtering
285
function SearchableList() {
286
const filter = useFilter();
287
const [query, setQuery] = useState("");
288
289
const items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
290
291
const filteredItems = items.filter(item =>
292
filter.contains(item.toLowerCase(), query.toLowerCase())
293
);
294
295
return (
296
<div>
297
<input
298
value={query}
299
onChange={(e) => setQuery(e.target.value)}
300
placeholder="Search items..."
301
/>
302
<ul>
303
{filteredItems.map(item => (
304
<li key={item}>{item}</li>
305
))}
306
</ul>
307
</div>
308
);
309
}
310
311
// Case-insensitive filtering
312
function CaseInsensitiveFilter() {
313
const filter = useFilter({
314
sensitivity: "base" // ignore case and accents
315
});
316
317
const names = ["André", "Anna", "Björn", "Café"];
318
const query = "an";
319
320
const matches = names.filter(name => filter.contains(name, query));
321
322
return <p>Matches for "{query}": {matches.join(", ")}</p>;
323
}
324
325
// Prefix filtering
326
function PrefixFilter() {
327
const filter = useFilter();
328
const [prefix, setPrefix] = useState("");
329
330
const words = ["react", "redux", "router", "component", "context"];
331
332
const prefixMatches = words.filter(word =>
333
filter.startsWith(word, prefix)
334
);
335
336
return (
337
<div>
338
<input
339
value={prefix}
340
onChange={(e) => setPrefix(e.target.value)}
341
placeholder="Type prefix..."
342
/>
343
<p>Words starting with "{prefix}": {prefixMatches.join(", ")}</p>
344
</div>
345
);
346
}
347
348
// Suffix filtering
349
function SuffixFilter() {
350
const filter = useFilter();
351
352
const files = ["document.pdf", "image.png", "script.js", "style.css"];
353
const extension = ".js";
354
355
const jsFiles = files.filter(file =>
356
filter.endsWith(file, extension)
357
);
358
359
return <p>JavaScript files: {jsFiles.join(", ")}</p>;
360
}
361
362
// Advanced search with multiple criteria
363
function AdvancedSearch() {
364
const filter = useFilter({
365
sensitivity: "base",
366
ignorePunctuation: true
367
});
368
369
const products = [
370
"iPhone 14 Pro",
371
"Samsung Galaxy S23",
372
"Google Pixel 7",
373
"OnePlus 11"
374
];
375
376
const [searchTerm, setSearchTerm] = useState("");
377
378
const results = products.filter(product => {
379
const terms = searchTerm.split(" ");
380
return terms.every(term =>
381
filter.contains(product, term)
382
);
383
});
384
385
return (
386
<div>
387
<input
388
value={searchTerm}
389
onChange={(e) => setSearchTerm(e.target.value)}
390
placeholder="Search products..."
391
/>
392
<ul>
393
{results.map(product => (
394
<li key={product}>{product}</li>
395
))}
396
</ul>
397
</div>
398
);
399
}
400
```