Skip to content

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.

Filter Sphere provides some built-in locales. You can import them from the @fn-sphere/filter/locales module.

LocaleImport name
EnglishenUS
JapanesejaJP
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>
);
}

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

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

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

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:

en.json
{
"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.

The following table lists all available i18n keys and their default values. Use these keys when providing translations via getLocaleText or your i18n library.

KeyDefault ValueCategory
operatorAndAndLayout
operatorOrOrLayout
operatorNotNotLayout
addRuleAdd RuleLayout
addGroupAdd GroupLayout
deleteRuleDeleteLayout
deleteGroupDelete GroupLayout
equalsequalsGeneral Filter
notEqualnot equalGeneral Filter
isEmptyis emptyGeneral Filter
isNotEmptyis not emptyGeneral Filter
enumEqualsequalsEnum Filter
enumNotEqualnot equalEnum Filter
containscontainsString Filter
notContainsnot containsString Filter
startsWithstarts withString Filter
endsWithends withString Filter
valueTruetrueBoolean Filter
valueFalsefalseBoolean Filter
numberEquals=Number Filter
numberNotEqual!=Number Filter
greaterThan>Number Filter
greaterThanOrEqual>=Number Filter
lessThan<Number Filter
lessThanOrEqual<=Number Filter
beforebeforeDate Filter
afterafterDate Filter