From d7919a8344614f15160c6fc304c3f4056c671a50 Mon Sep 17 00:00:00 2001 From: Heath Chiavettone Date: Mon, 27 Feb 2023 10:54:35 -0800 Subject: [PATCH] feature: Added the ability to localize RJSF internal strings True fix for #205 to support localizing the internal strings in @rjsf - In `@rjsf/utils`, added `TranslatableString` enum and `englishStringTranslator()` and `replaceStringParameters()` to support localization - Also updated the `Registry` type to add the `translateString` function - Added 100% unit testing for the new functions - In `@rjsf/core`, added a new `translateString` prop to `FormProps` as well as using that prop in the `registry` if provided - Updated the `getDefaultRegistry()` to set `translateString` to `englishStringTranslator` - Updated `ArrayField`, `BooleanField`, `MultiSchemaField`, `ObjectField`, `SchemaField`, `AddButton`, `IconButton`s, `ErrorList`, `WrapIfAdditionalTemplate` and `AltDateWidget` and `FileWidget` to use `translateString` - Added the `markdown-to-jsx` to support localizing the `UnsupportedField` - In all the themes, updated `ErrorList`, `IconButton`s, `AddButton`, `WrapIfAdditionalTemplate` and where applicable `AltDateWidget` to use `translateString` from the registry - Updated the snapshots for `UnsupportedField` based on the cleaned up translation formatting - In `@rjsf/chakra-ui`, updated `TextAreaWidget` and `UpDownWidget` to switch `??` syntax to `||` - In `@rjsf/semantic-ui`, updated the `FieldErrorTemplate` and `FieldHelpTemplate` to use the `errorId()` and `helpId()` functions - Updated the `utility-functions` docs for the enums and the new functions - Updated the `form-props` docs to describe the new `translateString` prop - Updated the `CHANGELOG` accordingly --- CHANGELOG.md | 31 +++ .../antd/src/templates/ErrorList/index.tsx | 6 +- .../antd/src/templates/IconButton/index.tsx | 31 ++- .../WrapIfAdditionalTemplate/index.tsx | 7 +- .../antd/src/widgets/AltDateWidget/index.tsx | 10 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- .../bootstrap-4/src/AddButton/AddButton.tsx | 4 +- .../bootstrap-4/src/ErrorList/ErrorList.tsx | 8 +- .../bootstrap-4/src/IconButton/IconButton.tsx | 26 ++- .../WrapIfAdditionalTemplate.tsx | 6 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- .../bootstrap-4/test/helpers/createMocks.ts | 8 +- .../chakra-ui/src/AddButton/AddButton.tsx | 4 +- .../src/AltDateWidget/AltDateWidget.tsx | 6 +- .../chakra-ui/src/ErrorList/ErrorList.tsx | 7 +- .../chakra-ui/src/IconButton/IconButton.tsx | 16 +- .../src/TextareaWidget/TextareaWidget.tsx | 2 +- .../src/UpDownWidget/UpDownWidget.tsx | 2 +- .../WrapIfAdditionalTemplate.tsx | 6 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- packages/core/package-lock.json | 207 +++++++++++++++--- packages/core/package.json | 1 + packages/core/src/components/Form.tsx | 16 +- .../core/src/components/fields/ArrayField.tsx | 16 +- .../src/components/fields/BooleanField.tsx | 11 +- .../components/fields/MultiSchemaField.tsx | 27 ++- .../src/components/fields/ObjectField.tsx | 9 +- .../src/components/fields/SchemaField.tsx | 21 +- .../templates/ButtonTemplates/AddButton.tsx | 4 +- .../templates/ButtonTemplates/IconButton.tsx | 16 +- .../src/components/templates/ErrorList.tsx | 8 +- .../components/templates/UnsupportedField.tsx | 28 ++- .../templates/WrapIfAdditionalTemplate.tsx | 6 +- .../src/components/widgets/AltDateWidget.tsx | 6 +- .../src/components/widgets/FileWidget.tsx | 23 +- packages/core/src/getDefaultRegistry.ts | 2 + packages/core/test/SchemaField_test.js | 3 +- .../docs/docs/api-reference/form-props.md | 26 ++- .../docs/api-reference/utility-functions.md | 27 +++ .../fluent-ui/src/AddButton/AddButton.tsx | 6 +- .../fluent-ui/src/ColorWidget/ColorWidget.tsx | 2 +- .../fluent-ui/src/DateWidget/DateWidget.tsx | 11 +- .../fluent-ui/src/ErrorList/ErrorList.tsx | 8 +- .../fluent-ui/src/IconButton/IconButton.tsx | 34 ++- .../src/UpDownWidget/UpDownWidget.tsx | 15 +- .../test/__snapshots__/Array.test.tsx.snap | 4 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- .../material-ui/src/AddButton/AddButton.tsx | 8 +- .../material-ui/src/ErrorList/ErrorList.tsx | 8 +- .../material-ui/src/IconButton/IconButton.tsx | 16 +- .../WrapIfAdditionalTemplate.tsx | 6 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- packages/mui/src/AddButton/AddButton.tsx | 8 +- packages/mui/src/ErrorList/ErrorList.tsx | 8 +- packages/mui/src/IconButton/IconButton.tsx | 16 +- .../WrapIfAdditionalTemplate.tsx | 6 +- .../mui/test/__snapshots__/Form.test.tsx.snap | 14 +- .../semantic-ui/src/AddButton/AddButton.tsx | 4 +- .../semantic-ui/src/ErrorList/ErrorList.tsx | 8 +- .../FieldErrorTemplate/FieldErrorTemplate.tsx | 3 +- .../FieldHelpTemplate/FieldHelpTemplate.tsx | 3 +- .../semantic-ui/src/IconButton/IconButton.tsx | 34 ++- .../WrapIfAdditionalTemplate.tsx | 6 +- .../test/__snapshots__/Form.test.tsx.snap | 14 +- packages/utils/src/englishStringTranslator.ts | 17 ++ packages/utils/src/enums.ts | 67 ++++++ packages/utils/src/index.ts | 6 + packages/utils/src/replaceStringParameters.ts | 20 ++ packages/utils/src/types.ts | 6 + .../test/englishStringTranslator.test.ts | 37 ++++ packages/utils/test/getTemplate.test.ts | 2 + .../test/replaceStringParameters.test.ts | 20 ++ 72 files changed, 897 insertions(+), 227 deletions(-) create mode 100644 packages/utils/src/englishStringTranslator.ts create mode 100644 packages/utils/src/enums.ts create mode 100644 packages/utils/src/replaceStringParameters.ts create mode 100644 packages/utils/test/englishStringTranslator.test.ts create mode 100644 packages/utils/test/replaceStringParameters.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ebe5f236..978302918f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,37 @@ it according to semantic versioning. For example, if your PR adds a breaking cha should change the heading of the (upcoming) version to include a major version bump. --> +# 5.2.0 + +## @rjsf/antd +- Updated `ErrorList`, `IconButton`s, `WrapIfAdditionalTemplate` and `AltDateWidget` to use the new `translateString()` function to support localization + +## @rjsf/bootstrap-4 +- Updated `AddButton`, `ErrorList`, `IconButton`s and `WrapIfAdditionalTemplate` to use the new `translateString()` function to support localization + +## @rjsf/chakra-ui +- Updated `AddButton`, `ErrorList`, `IconButton`s, `WrapIfAdditionalTemplate` and `AltDateWidget` to use the new `translateString()` function to support localization + +## @rjsf/core +- Updated `ArrayField`, `BooleanField`, `MultiSchemaField`, `ObjectField`, `SchemaField`, `AddButton`, `IconButton`s, `ErrorList`, `WrapIfAdditionalTemplate` and `AltDateWidget` and `FileWidget` to use the new `translateString()` function to support localization + - Also updated `Form` to take a new optional `translateString` prop and `getDefaultRegistry()` to set `translateString` to `englishStringTranslator()` + +## @rjsf/fluent-ui +- Updated `AddButton`, `ColorWidget`, `ErrorList`, `IconButton`s and `UpDownWidget` to use the new `translateString()` function to support localization + +## @rjsf/material-ui +- Updated `AddButton`, `ErrorList`, `IconButton`s and `WrapIfAdditionalTemplate` to use the new `translateString()` function to support localization + +## @rjsf/mui +- Updated `AddButton`, `ErrorList`, `IconButton`s and `WrapIfAdditionalTemplate` to use the new `translateString()` function to support localization + +## @rjsf/semantic-ui +- Updated `AddButton`, `ErrorList`, `IconButton`s and `WrapIfAdditionalTemplate` to use the new `translateString()` function to support localization + +## Dev / docs / playground +- Updated the `utility-functions` documentation for the `enums` and `englishStringTranslator()` & `replaceStringParameters()` functions +- Updated the `form-props` documentation for the new, optional `translateString` prop on `Form` + # 5.1.0 ## @rjsf/bootstrap-4 diff --git a/packages/antd/src/templates/ErrorList/index.tsx b/packages/antd/src/templates/ErrorList/index.tsx index 27d19c1334..c52cd23418 100644 --- a/packages/antd/src/templates/ErrorList/index.tsx +++ b/packages/antd/src/templates/ErrorList/index.tsx @@ -8,6 +8,7 @@ import { FormContextType, RJSFSchema, StrictRJSFSchema, + TranslatableString, } from "@rjsf/utils"; /** The `ErrorList` component is the template that renders the all the errors associated with the fields in the `Form` @@ -18,7 +19,8 @@ export default function ErrorList< T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any ->({ errors }: ErrorListProps) { +>({ errors, registry }: ErrorListProps) { + const { translateString } = registry; const renderErrors = () => ( {errors.map((error, index) => ( @@ -36,7 +38,7 @@ export default function ErrorList< ); diff --git a/packages/antd/src/templates/IconButton/index.tsx b/packages/antd/src/templates/IconButton/index.tsx index e3343ea642..bd973aafd2 100644 --- a/packages/antd/src/templates/IconButton/index.tsx +++ b/packages/antd/src/templates/IconButton/index.tsx @@ -10,6 +10,7 @@ import { IconButtonProps, RJSFSchema, StrictRJSFSchema, + TranslatableString, } from "@rjsf/utils"; // The `type` for IconButtonProps collides with the `type` for `ButtonProps` so omit it to avoid Typescript issue @@ -39,9 +40,12 @@ export function AddButton< S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any >(props: AntdIconButtonProps) { + const { + registry: { translateString }, + } = props; return ( (props: AntdIconButtonProps) { + const { + registry: { translateString }, + } = props; return ( - } /> + } + /> ); } @@ -65,7 +76,16 @@ export function MoveUpButton< S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any >(props: AntdIconButtonProps) { - return } />; + const { + registry: { translateString }, + } = props; + return ( + } + /> + ); } export function RemoveButton< @@ -75,9 +95,12 @@ export function RemoveButton< >(props: AntdIconButtonProps) { // The `block` prop is not part of the `IconButtonProps` defined in the template, so get it from the uiSchema instead const options = getUiOptions(props.uiSchema); + const { + registry: { translateString }, + } = props; return ( )} @@ -200,7 +202,7 @@ export default function AltDateWidget< onClick={handleClear} type="primary" > - Clear + {translateString(TranslatableString.ClearLabel)} )} diff --git a/packages/antd/test/__snapshots__/Form.test.tsx.snap b/packages/antd/test/__snapshots__/Form.test.tsx.snap index 051d38b993..3ec41e5813 100644 --- a/packages/antd/test/__snapshots__/Form.test.tsx.snap +++ b/packages/antd/test/__snapshots__/Form.test.tsx.snap @@ -1982,19 +1982,17 @@ exports[`single fields unsupported field 1`] = ` className="unsupported-field" >

- Unsupported field schema - for - field + Unsupported field schema for field root - - : - Unknown field type undefined - - . + + Unknown field type undefined + + . +

         {}
diff --git a/packages/bootstrap-4/src/AddButton/AddButton.tsx b/packages/bootstrap-4/src/AddButton/AddButton.tsx
index d643dbb6eb..d766307eea 100644
--- a/packages/bootstrap-4/src/AddButton/AddButton.tsx
+++ b/packages/bootstrap-4/src/AddButton/AddButton.tsx
@@ -4,6 +4,7 @@ import {
   IconButtonProps,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 import Button from "react-bootstrap/Button";
 import { BsPlus } from "@react-icons/all-files/bs/BsPlus";
@@ -13,12 +14,13 @@ export default function AddButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >({ uiSchema, registry, ...props }: IconButtonProps) {
+  const { translateString } = registry;
   return (
     
diff --git a/packages/bootstrap-4/src/ErrorList/ErrorList.tsx b/packages/bootstrap-4/src/ErrorList/ErrorList.tsx
index 5c87b90221..42a4bb5514 100644
--- a/packages/bootstrap-4/src/ErrorList/ErrorList.tsx
+++ b/packages/bootstrap-4/src/ErrorList/ErrorList.tsx
@@ -8,16 +8,20 @@ import {
   FormContextType,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 
 export default function ErrorList<
   T = any,
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
->({ errors }: ErrorListProps) {
+>({ errors, registry }: ErrorListProps) {
+  const { translateString } = registry;
   return (
     
-      Errors
+      
+        {translateString(TranslatableString.ErrorsLabel)}
+      
       
         
           {errors.map((error, i: number) => {
diff --git a/packages/bootstrap-4/src/IconButton/IconButton.tsx b/packages/bootstrap-4/src/IconButton/IconButton.tsx
index 07dec2fde9..c969c80ed5 100644
--- a/packages/bootstrap-4/src/IconButton/IconButton.tsx
+++ b/packages/bootstrap-4/src/IconButton/IconButton.tsx
@@ -4,6 +4,7 @@ import {
   IconButtonProps,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 import Button, { ButtonProps } from "react-bootstrap/Button";
 import { IoIosRemove } from "@react-icons/all-files/io/IoIosRemove";
@@ -34,8 +35,15 @@ export function MoveDownButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
+  const {
+    registry: { translateString },
+  } = props;
   return (
-    } />
+    }
+    />
   );
 }
 
@@ -44,7 +52,16 @@ export function MoveUpButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
-  return } />;
+  const {
+    registry: { translateString },
+  } = props;
+  return (
+    }
+    />
+  );
 }
 
 export function RemoveButton<
@@ -52,9 +69,12 @@ export function RemoveButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
+  const {
+    registry: { translateString },
+  } = props;
   return (
     }
diff --git a/packages/bootstrap-4/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/packages/bootstrap-4/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx
index b44a616713..f219d1e206 100644
--- a/packages/bootstrap-4/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx
+++ b/packages/bootstrap-4/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx
@@ -5,6 +5,7 @@ import {
   FormContextType,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
   WrapIfAdditionalTemplateProps,
 } from "@rjsf/utils";
 
@@ -31,9 +32,10 @@ export default function WrapIfAdditionalTemplate<
   uiSchema,
   registry,
 }: WrapIfAdditionalTemplateProps) {
+  const { templates, translateString } = registry;
   // Button templates are not overridden in the uiSchema
-  const { RemoveButton } = registry.templates.ButtonTemplates;
-  const keyLabel = `${label} Key`; // i18n ?
+  const { RemoveButton } = templates.ButtonTemplates;
+  const keyLabel = translateString(TranslatableString.KeyLabel, [label]);
   const additional = ADDITIONAL_PROPERTY_FLAG in schema;
 
   if (!additional) {
diff --git a/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap b/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap
index 00e5c25244..e3788162a4 100644
--- a/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap
+++ b/packages/bootstrap-4/test/__snapshots__/Form.test.tsx.snap
@@ -1597,19 +1597,17 @@ exports[`single fields unsupported field 1`] = `
         className="unsupported-field"
       >
         

- Unsupported field schema - for - field + Unsupported field schema for field root - - : - Unknown field type undefined - - . + + Unknown field type undefined + + . +

           {}
diff --git a/packages/bootstrap-4/test/helpers/createMocks.ts b/packages/bootstrap-4/test/helpers/createMocks.ts
index 936a885fe6..b4a2dd313c 100644
--- a/packages/bootstrap-4/test/helpers/createMocks.ts
+++ b/packages/bootstrap-4/test/helpers/createMocks.ts
@@ -1,4 +1,9 @@
-import { createSchemaUtils, WidgetProps, RJSFSchema } from "@rjsf/utils";
+import {
+  createSchemaUtils,
+  englishStringTranslator,
+  WidgetProps,
+  RJSFSchema,
+} from "@rjsf/utils";
 import { getDefaultRegistry } from "@rjsf/core";
 import validator from "@rjsf/validator-ajv8";
 
@@ -24,6 +29,7 @@ export function mockRegistry() {
     formContext: {},
     rootSchema: {},
     schemaUtils: mockSchemaUtils,
+    translateString: englishStringTranslator,
   };
 }
 
diff --git a/packages/chakra-ui/src/AddButton/AddButton.tsx b/packages/chakra-ui/src/AddButton/AddButton.tsx
index a3868a2842..ed96ce3f75 100644
--- a/packages/chakra-ui/src/AddButton/AddButton.tsx
+++ b/packages/chakra-ui/src/AddButton/AddButton.tsx
@@ -4,6 +4,7 @@ import {
   IconButtonProps,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 import { Button } from "@chakra-ui/react";
 import { AddIcon } from "@chakra-ui/icons";
@@ -13,9 +14,10 @@ export default function AddButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >({ uiSchema, registry, ...props }: IconButtonProps) {
+  const { translateString } = registry;
   return (
     
   );
 }
diff --git a/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx b/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx
index 56ac1091f3..306aaff3ef 100644
--- a/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx
+++ b/packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx
@@ -8,6 +8,7 @@ import {
   RJSFSchema,
   StrictRJSFSchema,
   toDateString,
+  TranslatableString,
   WidgetProps,
 } from "@rjsf/utils";
 import { Box, Button } from "@chakra-ui/react";
@@ -74,6 +75,7 @@ function AltDateWidget<
     showTime,
     value,
   } = props;
+  const { translateString } = registry;
   const [state, setState] = useState(parseDateString(value, showTime));
   useEffect(() => {
     setState(parseDateString(value, showTime));
@@ -160,14 +162,14 @@ function AltDateWidget<
             onClick={(e: MouseEvent) => handleNow(e)}
             mr="2"
           >
-            Now
+            {translateString(TranslatableString.NowLabel)}
           
         )}
         {!options.hideClearButton && (
           
         )}
       
diff --git a/packages/chakra-ui/src/ErrorList/ErrorList.tsx b/packages/chakra-ui/src/ErrorList/ErrorList.tsx
index af7a64e35a..7faa273515 100644
--- a/packages/chakra-ui/src/ErrorList/ErrorList.tsx
+++ b/packages/chakra-ui/src/ErrorList/ErrorList.tsx
@@ -4,6 +4,7 @@ import {
   FormContextType,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 import { List, ListIcon, ListItem, Alert, AlertTitle } from "@chakra-ui/react";
 import { WarningIcon } from "@chakra-ui/icons";
@@ -12,7 +13,8 @@ export default function ErrorList<
   T = any,
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
->({ errors }: ErrorListProps) {
+>({ errors, registry }: ErrorListProps) {
+  const { translateString } = registry;
   return (
     
-      Errors
-
+      {translateString(TranslatableString.ErrorsLabel)}
       
         {errors.map((error, i) => (
           
diff --git a/packages/chakra-ui/src/IconButton/IconButton.tsx b/packages/chakra-ui/src/IconButton/IconButton.tsx
index cd243f24c5..542d1ef6ef 100644
--- a/packages/chakra-ui/src/IconButton/IconButton.tsx
+++ b/packages/chakra-ui/src/IconButton/IconButton.tsx
@@ -5,6 +5,7 @@ import {
   IconButtonProps,
   RJSFSchema,
   StrictRJSFSchema,
+  TranslatableString,
 } from "@rjsf/utils";
 
 import { ArrowUpIcon, ArrowDownIcon, DeleteIcon } from "@chakra-ui/icons";
@@ -15,9 +16,12 @@ export function MoveDownButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
+  const {
+    registry: { translateString },
+  } = props;
   return (
     
-      title="Move down"
+      title={translateString(TranslatableString.MoveDownButton)}
       {...props}
       icon={}
     />
@@ -29,9 +33,12 @@ export function MoveUpButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
+  const {
+    registry: { translateString },
+  } = props;
   return (
     
-      title="Move up"
+      title={translateString(TranslatableString.MoveUpButton)}
       {...props}
       icon={}
     />
@@ -43,9 +50,12 @@ export function RemoveButton<
   S extends StrictRJSFSchema = RJSFSchema,
   F extends FormContextType = any
 >(props: IconButtonProps) {
+  const {
+    registry: { translateString },
+  } = props;
   return (
     
-      title="Remove"
+      title={translateString(TranslatableString.RemoveButton)}
       {...props}
       icon={}
     />
diff --git a/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx b/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx
index 9236d8beef..302403ba05 100644
--- a/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx
+++ b/packages/chakra-ui/src/TextareaWidget/TextareaWidget.tsx
@@ -63,7 +63,7 @@ export default function TextareaWidget<