Localization
Filter Sphere provides a variety of ways to localize the UI. This is useful when you want to display the UI in a different language.
Built-in Locales
Section titled “Built-in Locales”Filter Sphere provides some built-in locales. You can import them from the @fn-sphere/filter/locales module.
| Locale | Import name |
|---|---|
| English | enUS |
| Japanese | jaJP |
| Chinese (Simplified) | zhCN |
import { enUS, jaJP, zhCN } from "@fn-sphere/filter/locales";
const locale = enUS;
const customGetLocaleText = (key: string): string => { if (!(key in locale)) return key; return locale[key as keyof typeof locale];};
export default function AdvancedFilter() { const { filterRule, predicate, context } = useFilterSphere({ schema, getLocaleText: customGetLocaleText, }); return ( <FilterSphereProvider context={context}> <FilterBuilder /> </FilterSphereProvider> );}getLocaleText
Section titled “getLocaleText”The default locale of Filter Sphere is English (United States). You can use the getLocaleText function to translate the filter UI.
import { FilterSphereProvider, FilterBuilder, useFilterSphere,} from "@fn-sphere/filter";import { enUS, jaJP, zhCN } from "@fn-sphere/filter/locales";import { z } from "zod";import { useState } from "react";
const locales = [ { key: "cn", label: "中文", value: { ...zhCN, price: "价格", name: "名称" } }, { key: "jp", label: "日本語", value: { ...jaJP, price: "価格", name: "名前" }, }, { key: "en", label: "English", value: enUS },];
const schema = z.object({ name: z.string(), price: z.number(),});
export default function AdvancedFilter() { const [localeKey, setLocaleKey] = useState("cn");
const customGetLocaleText = (key: string): string => { const locale = locales.find((locale) => locale.key === localeKey)?.value; if (!locale || !(key in locale)) return key; return locale[key as keyof typeof locale]; };
const { filterRule, predicate, context } = useFilterSphere({ schema, getLocaleText: customGetLocaleText, }); return ( <FilterSphereProvider context={context}> <select onChange={(e) => setLocaleKey(e.target.value)}> {locales.map((locale) => ( <option key={locale.key} value={locale.key}> {locale.label} </option> ))} </select> <FilterBuilder /> </FilterSphereProvider> );}mapFieldName / mapFilterName
Section titled “mapFieldName / mapFilterName”You can customize the field name and filter name by providing the mapFieldName and mapFilterName functions.
import { FilterSphereProvider, FilterBuilder, useFilterSphere,} from "@fn-sphere/filter";import { z } from "zod";
const customMapFieldName = (field: FilterField) => { return field.fieldSchema.description;};
const customMapFilterName = (filterSchema: StandardFnSchema) => { return filterSchema.name.toUpperCase();};
const schema = z.object({ id: z.number().describe("ID"), name: z.string().describe("Name"),});
export default function AdvancedFilter() { const { filterRule, predicate, context } = useFilterSphere({ schema, mapFilterName: customMapFilterName, mapFieldName: customMapFieldName, }); return ( <FilterSphereProvider context={context}> <FilterBuilder /> </FilterSphereProvider> );}Filter Sphere provides default implementations for the mapFieldName and mapFilterName functions.
export const defaultMapFieldName: (field: FilterField) => string = (field) => { // By default, filter sphere uses the field description as the field name. if (field.fieldSchema.description) { return field.fieldSchema.description; } // If the field description is not provided, the field path is used as the field name. if (field.path.length) { return field.path.join("."); } return "root";};
export const defaultMapFilterName: ( filterSchema: StandardFnSchema, field: FilterField,) => string = (filterSchema) => { return filterSchema.name;};Integration with i18n Libraries
Section titled “Integration with i18n Libraries”If your project already uses an i18n library (e.g. next-intl, react-intl, react-i18next), you can bridge it with Filter Sphere’s getLocaleText instead of maintaining separate locale files.
The key idea is to create a custom getLocaleText that delegates to your i18n library’s translation function.
Create a hook that bridges your i18n library
Section titled “Create a hook that bridges your i18n library”Wrap your i18n library’s useTranslations (or equivalent) in a custom hook. This example uses next-intl:
import { defaultGetLocaleText } from "@fn-sphere/filter";import { useTranslations } from "next-intl";import { useCallback } from "react";
const FIELD_PREFIX = "filter"; // Optional prefix for filter keys
export function useGetLocaleText() { const t = useTranslations();
const getLocaleText = useCallback( (key: string) => t(`${FIELD_PREFIX}.${key}`) ?? defaultGetLocaleText(key), [t], );
return getLocaleText;}Use the hook with Filter Sphere
Section titled “Use the hook with Filter Sphere”Pass the bridged getLocaleText to useFilterSphere. All Filter Sphere UI labels (operator names, button text, field names, filter names) will be resolved through your i18n pipeline.
import { FilterSphereProvider, FilterBuilder, useFilterSphere, type FilterSphereInput,} from "@fn-sphere/filter";import { z } from "zod";import { useMemo } from "react";
const schema = z.object({ name: z.string().describe("name"), price: z.number().describe("price"),});
function MyFilter() { const getLocaleText = useGetLocaleText(); const { context } = useFilterSphere({ schema, getLocaleText, });
return ( <FilterSphereProvider context={context}> <FilterBuilder /> </FilterSphereProvider> );}Then add the corresponding keys to your translation files:
{ "filter.name": "Name", "filter.price": "Price"}With this approach, all Filter Sphere labels are resolved through your existing i18n pipeline, so you only need to maintain a single set of translation files.
i18n Keys
Section titled “i18n Keys”The following table lists all available i18n keys and their default values. Use these keys when providing translations via getLocaleText or your i18n library.
| Key | Default Value | Category |
|---|---|---|
| operatorAnd | And | Layout |
| operatorOr | Or | Layout |
| operatorNot | Not | Layout |
| addRule | Add Rule | Layout |
| addGroup | Add Group | Layout |
| deleteRule | Delete | Layout |
| deleteGroup | Delete Group | Layout |
| equals | equals | General Filter |
| notEqual | not equal | General Filter |
| isEmpty | is empty | General Filter |
| isNotEmpty | is not empty | General Filter |
| enumEquals | equals | Enum Filter |
| enumNotEqual | not equal | Enum Filter |
| contains | contains | String Filter |
| notContains | not contains | String Filter |
| startsWith | starts with | String Filter |
| endsWith | ends with | String Filter |
| valueTrue | true | Boolean Filter |
| valueFalse | false | Boolean Filter |
| numberEquals | = | Number Filter |
| numberNotEqual | != | Number Filter |
| greaterThan | > | Number Filter |
| greaterThanOrEqual | >= | Number Filter |
| lessThan | < | Number Filter |
| lessThanOrEqual | <= | Number Filter |
| before | before | Date Filter |
| after | after | Date Filter |