diff --git a/content/docs/i18n.mdx b/content/docs/i18n.mdx
new file mode 100644
index 00000000..2308e644
--- /dev/null
+++ b/content/docs/i18n.mdx
@@ -0,0 +1,357 @@
+---
+id: i18n
+title: i18n
+description: Translate jsonforms
+---
+
+import { I18nExample, ValuesTable } from '../../src/components/docs/i18n';
+
+:::note
+
+Please note that the renderer sets themselves are not yet translatable.
+You can find the current status here:
+https://github.com/eclipsesource/jsonforms/issues/1826
+
+:::
+
+The translate functionality of JSON Forms is integrated into the core component.
+In order to translate JSON Forms, you need to set a translation function and provide it to the JSON Forms component:
+```ts
+const translator = (key, defaultMessage) => {
+ console.log(`Key: ${key}, Default Message: ${defaultMessage}`);
+ return defaultMessage;
+};
+
+const [locale, setLocale] = useState<'de'|'en'>('de');
+const translation = useMemo(() => translator(locale), [locale]);
+
+
+```
+
+The `i18n` prop consists of three components: `locale`, `translate` and `translateError`.
+
+## `locale`
+
+Specifies the current locale of the application.
+This can be used by renderers to render locale specific UI elements, for example for locale-aware formatting of numbers.
+
+
+## `translate`
+
+Provides a translation function handling the actual translation.
+
+:::caution
+
+The translate function should be side effect free and should be stable (memoized) to avoid unnecessary re-renderings, i.e. the translate function should only change if there are new translations.
+
+:::
+
+The type of the translate is
+
+```ts
+(key: string, defaultMessage: string | undefined, context: any) => string | undefined)
+```
+
+with the following parameters:
+
+### `key`
+
+The key is used to identify the string that needs to be translated.
+It can be set using the `UI Schema`, the `JSON Schema` or is generated by default based on the property path.
+
+#### UI Schema option
+
+The key can be set via the `i18n` UI Schema option:
+```json
+{
+ "type": "Control",
+ "label": "name",
+ "scope": "#/properties/name",
+ "i18n": "customName"
+}
+```
+
+Therefore, the translation will be invoked with `customName.label` & `customName.description`.
+
+#### JSON Schema
+
+The key can also be set with the custom JSON Schema `i18n` option:
+```json
+{
+ "name": {
+ "type": "string",
+ "i18n": "myCustomName"
+ }
+}
+```
+
+Therefore, the translation will be invoked with `myCustomName.label` & `myCustomName.description`.
+
+#### Default: Property path
+
+If none of the above is set, the property path will be used as the key.
+
+```json
+{
+ "properties": {
+ "firstName": {
+ "type": "string"
+ },
+ "address": {
+ "type": "object",
+ "properties": {
+ "street": {
+ "type": "string"
+ }
+ }
+ },
+ "comments": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ }
+ }
+ }
+ }
+}
+```
+
+The paths in the above example are:
+- `firstName.label` & `firstName.description`
+- `address.label` & `address.description`
+- `address.street.label` & `address.street.description`
+- `comments.message.label` & `comments.message.description` (the path for arrays will not contain indices)
+
+### `defaultMessage`
+
+The default message is provided by JSON Forms and can act as a fallback or could be translated.
+
+:::note
+
+If the `defaultMessage` is `undefined`, you should also return `undefined` if there is no translation for the given key.
+Returning an empty string (or something similar) instead may result in undesired behavior.
+JSON Forms will use `undefined` when the message could be skipped or another more generic key could be tried.
+
+:::
+
+### `context`
+
+`context` can contain additional information for the current translation. The following parameters can be provided:
+
+
+
+Schema translations provide all properties, while UI schema translations only provide the `uischema` property.
+
+## `translateError`
+
+The `translateError` function is called whenever a single message is to be extracted from an AJV error object.
+
+The type of the `translateError` function is
+
+```ts
+(error: ErrorObject, translate: Translator, uischema?: UISchemaElement) => string
+```
+
+* The `error` is the AJV error object
+* `translate` is the i18n `translate` function handed over to JSON Forms
+* In cases where a UI Schema Element can be correlated to an `error` it will also be handed over
+* The `translateError` function always returns a `string`
+
+Usually this method does not need to be customized as JSON Forms already provides a sensible default implementation.
+A reason to customize this method could be to integrate third party frameworks like `ajv-i18n`.
+
+For more information about how errors can be customized, see the following section:
+
+## Error Customizations
+
+For each control a list of errors is determined.
+This section describes the default behavior of JSON forms to offer translation support for them.
+
+### `.error.custom`
+
+Before invoking the `translateError` function, JSON Forms will check whether a `.error.custom` translation exists.
+This is useful if there are many JSON Schema validaton keywords defined for a single property, but a single cohesive message shall be displayed.
+
+If no `.error.custom` message is returned by the `translate` function, `translateError` will be called for each AJV error and the results combined.
+
+The default implementation of `translateError` will invoke `translate` multiple times to determine the best message for the given error. Therefore it's usually not necessary to customize `translateError` itself.
+By default error it works like this:
+
+`` in the sections below refers to the key of the field (see `key` section above).
+
+### Evaluation order
+
+#### `.error.`
+
+Example keys: `name.error.required`, `address.street.error.pattern`
+
+The default `translateError` will first look for a concrete message for a specific field and a specific error type.
+
+#### `error.`
+
+Example keys: `error.required`, `error.pattern`
+
+After checking field specific translations, `translateError` will then look for form-wide translations of errors, independent of each respective field.
+This is useful to customize for example `required` or `pattern` messages for all properties.
+
+#### error message
+
+At last the default `translateError` implementation will check whether the `message` of the error object has a specific translation.
+
+#### Default AJV error message
+
+If none of the above apply, the `message` provided by the AJV error object will be used.
+
+### Example
+
+Consider the following schema for an object attribute:
+```js
+{
+ phone: {
+ type: "string",
+ minLength: 10,
+ pattern: "^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$"
+ }
+}
+```
+The order in which the error keys are evaluated is the following:
+- `phone.error.custom`: if this is set, the `pattern` and `minLength` errors will be ignores and just this message is used
+- `phone.error.pattern` & `phone.error.minLength`
+- `error.pattern` & `error.minLength`
+
+## Enum translation
+
+Enum translations are based on the respective entries:
+ - For JSON Schema `enum`, the stringified value is used.
+ - For JSON Schema `oneOf` enums which consist of (`title`, `const`) pairs a specialized `i18n` key or `title` is used.
+
+Therefore, in order to translate enum values, an additional key is checked for each enum entry.
+For example:
+Let's assume we have an enum attribute `gender`, which looks like this:
+
+```js
+{
+ gender: {
+ type: "string",
+ enum: ["male", "female", "other"]
+ }
+}
+```
+
+In this case the `translate` function will be invoked with the keys `gender.male`, `gender.female` and `gender.other` to translate these enum values. In case `gender` had an `i18n` property, it would be used instead, i.e. `.male` etc.
+
+Let's assume we have a `oneOf` enum attribute gender which looks like this:
+
+```js
+{
+ gender: {
+ oneOf: [
+ {
+ title: "Male",
+ const: 0
+ },
+ {
+ title: "Female",
+ const: "f",
+ i18n: "fem"
+ },
+ {
+ const: null
+ }
+ ]
+ }
+}
+```
+
+Here the requested keys are:
+
+* `gender.Male` - property path + `title` of the `oneOf` entry
+* `fem` - direct usage of the `i18n` property for the `oneOf` entry
+* `null` - the `title` attribute is missing, therefore the `null` value is stringified to `'null'`.
+
+## UI Schema Translation
+
+The UI schema has the elements `group`, `category` and `label`, which can also be translated.
+
+If a i18n-key is provided in a `group` or `category` element, `.label` will be used as key.
+If no i18n key is provided, the value of the `label`-property is used as key.
+In case neither a i18n-key nor a label is provided, `.label` will be used as default key.
+
+The `label` UI schema element will use `.text` as key, if provided.
+If no i18n key is provided, the value of the `text`-property is used as key.
+
+
+Let's assume we have the following UI schema:
+
+```js
+const uischema = {
+ type: 'VerticalLayout',
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/user',
+ options: {
+ detail: {
+ type: 'Group',
+ i18n: 'i18nUser',
+ }
+ }
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/address',
+ options: {
+ detail: {
+ type: 'Group',
+ label: 'labelAddress',
+ }
+ }
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/address'
+ },
+ {
+ type: 'Label',
+ i18n: 'i18nLabel'
+ },
+ {
+ type: 'Label',
+ text: 'textLabel'
+ },
+ ]
+};
+```
+
+Here the requested keys are:
+
+* `i18nUser.label` - i18n + `label`
+* `labelAddress` - direct usage of the `label` property
+* `address.label` - property path + `label`
+* `i18nLabel.text` - i18n + `text`
+* `textLabel` - direct usage of the `text` property
+
+## Access translation in custom renderer sets
+
+If you want to directly access the i18n properties within a custom renderer, you can use the JSON Forms context for that:
+
+```js
+const ctx = useJsonForms();
+
+const locale = ctx.i18n.locale;
+const translate = ctx.i18n.translate;
+const translateError = ctx.i18n.translateError;
+```
+
+With this you can for example change phone number patterns based on the current locale for validation.
+
+## Example
+
+
diff --git a/content/pages/assets/support.module.scss b/content/pages/assets/support.module.scss
index 9b56c4f2..03158831 100644
--- a/content/pages/assets/support.module.scss
+++ b/content/pages/assets/support.module.scss
@@ -1,4 +1,4 @@
-h3 {
+.comparison_container h3 {
font-size: 2rem;
padding: 0;
}
diff --git a/src/components/docs/i18n.js b/src/components/docs/i18n.js
new file mode 100644
index 00000000..07ca5d0a
--- /dev/null
+++ b/src/components/docs/i18n.js
@@ -0,0 +1,176 @@
+import React, { useMemo, useState } from 'react';
+import get from 'lodash/get';
+import {
+ materialCells,
+ materialRenderers,
+} from '@jsonforms/material-renderers';
+import Button from '@mui/material/Button';
+import { JsonForms } from '@jsonforms/react';
+import TableCell from '@mui/material/TableCell/TableCell';
+import TableHead from '@mui/material/TableHead/TableHead';
+import Table from '@mui/material/Table/Table';
+import TableBody from '@mui/material/TableBody/TableBody';
+import TableRow from '@mui/material/TableRow/TableRow';
+
+const i18n = {
+ schema: {
+ properties: {
+ firstName: {
+ type: "string"
+ },
+ lastName: {
+ type: "string"
+ },
+ email: {
+ type: "string"
+ },
+ gender: {
+ type: "string",
+ enum: [
+ "Male",
+ "Female",
+ "Other"
+ ]
+ }
+ },
+ required: [
+ "email"
+ ]
+ },
+ uischema: {
+ type: "VerticalLayout",
+ elements: [
+ {
+ type: "VerticalLayout",
+ elements: [
+ {
+ type: "Control",
+ scope: "#/properties/firstName"
+ },
+ {
+ type: "Control",
+ scope: "#/properties/lastName"
+ }
+ ]
+ },
+ {
+ type: "Control",
+ scope: "#/properties/email"
+ },
+ {
+ type: "Control",
+ scope: "#/properties/gender"
+ }
+ ]
+ }
+}
+
+export const en = {
+ firstName: {
+ label: 'First Name',
+ description: 'The first name of the person',
+ },
+ lastName: {
+ label: 'Last Name',
+ },
+ email: {
+ label: 'Email'
+ },
+ gender: {
+ label: 'Gender',
+ Male: 'Male',
+ Female: 'Female',
+ Other: 'Diverse'
+ },
+ error: {
+ required: 'This field is required',
+ },
+};
+
+export const de = {
+ firstName: {
+ label: 'Vorname',
+ description: 'Der Vorname der Person',
+ },
+ lastName: {
+ label: 'Nachname',
+ },
+ email: {
+ label: 'Email'
+ },
+ gender: {
+ label: 'Geschlecht',
+ Male: 'Männlich',
+ Female: 'Weiblich',
+ Other: 'Divers'
+ },
+ error: {
+ required: 'Dieses Feld muss ausgefüllt werden.',
+ },
+};
+
+export const I18nExample = () => {
+ const [locale, setLocale] = useState('de');
+
+ const createTranslator = (locale) => (key, defaultMessage) => {
+ return get(locale === 'en' ? en : de, key) ?? defaultMessage;
+ };
+
+ const translation = useMemo(() => createTranslator(locale), [locale]);
+
+ const switchLocale = () => {
+ if(locale === 'en') {
+ setLocale('de');
+ } else {
+ setLocale('en');
+ }
+ };
+
+ return (
+
+
+
+
+ Current language: {locale}
+
+ );
+};
+
+export const ValuesTable = () => (
+
+
+
+
+ Parameter
+ Description
+
+
+
+
+ errors
+ Array of AJV errors, that occurred during validation
+
+
+ path
+ The path of the translated element
+
+
+ schema
+ The schema of the translated element
+
+
+ uischema
+ The uischema of the translated element
+
+
+
+
+);
\ No newline at end of file
diff --git a/src/sidebars/docs.js b/src/sidebars/docs.js
index 3a53ad5f..813acdcb 100644
--- a/src/sidebars/docs.js
+++ b/src/sidebars/docs.js
@@ -10,6 +10,7 @@ module.exports = {
items: ['uischema/uischema', 'uischema/controls', 'uischema/layouts', 'uischema/rules'],
},
'labels',
+ 'i18n',
'renderer-sets',
'ref-resolving',
'readonly',