Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Localize default messages #846

Closed
wants to merge 15 commits into from
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
- [Custom error messages](#custom-error-messages)
- [Error List Display](#error-list-display)
- [The case of empty strings](#the-case-of-empty-strings)
- [Localize default messages](#localize-default-messages)
- [Styling your forms](#styling-your-forms)
- [Schema definitions and references](#schema-definitions-and-references)
- [Property dependencies](#property-dependencies)
Expand Down Expand Up @@ -1589,6 +1590,21 @@ One consequence of this is that if you have an empty string in your `enum` array

If you want to have the field set to a default value when empty you can provide a `ui:emptyValue` field in the `uiSchema` object.

## Localize default messages

You can change localize to default messages errors.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can change localize to default messages errors.

=>

You can change the localization of the error messages.


Here are some examples from the [playground](http://mozilla-services.github.io/react-jsonschema-form/).

Here list of languages support [ajv-i18n](https://github.com/epoberezkin/ajv-i18n).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the list of supported languages ajv-i18n.


```jsx
render((
<Form schema={schema}
localize="es" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

localization="es"

), document.getElementById("app"));
```

## Styling your forms

This library renders form fields and widgets leveraging the [Bootstrap](http://getbootstrap.com/) semantics. That means your forms will be beautiful by default if you're loading its stylesheet in your page.
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "react-jsonschema-form",
"version": "1.0.6",

"description": "A simple React component capable of building HTML forms out of a JSON schema.",
"scripts": {
"build:readme": "toctoc README.md -w",
Expand Down Expand Up @@ -43,6 +44,7 @@
},
"dependencies": {
"ajv": "^5.2.3",
"ajv-i18n": "^3.0.0",
"babel-runtime": "^6.26.0",
"core-js": "^2.5.7",
"lodash.topath": "^4.5.2",
Expand Down
4 changes: 3 additions & 1 deletion playground/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ class App extends Component {
ArrayFieldTemplate,
ObjectFieldTemplate,
transformErrors,
localize = "en",
} = this.state;

return (
Expand Down Expand Up @@ -484,7 +485,8 @@ class App extends Component {
console.log(`Focused ${id} with value ${value}`)
}
transformErrors={transformErrors}
onError={log("errors")}>
onError={log("errors")}
localize={localize}>
<div className="row">
<div className="col-sm-3">
<button className="btn btn-primary" type="submit">
Expand Down
2 changes: 2 additions & 0 deletions playground/samples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import customObject from "./customObject";
import alternatives from "./alternatives";
import propertyDependencies from "./propertyDependencies";
import schemaDependencies from "./schemaDependencies";
import localize from "./localize";
import additionalProperties from "./additionalProperties";

export const samples = {
Expand All @@ -39,5 +40,6 @@ export const samples = {
Alternatives: alternatives,
"Property dependencies": propertyDependencies,
"Schema dependencies": schemaDependencies,
Localize: localize,
"Additional Properties": additionalProperties,
};
17 changes: 17 additions & 0 deletions playground/samples/localize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
schema: {
title: "Contextualized localize",
type: "object",
properties: {
active: {
type: "boolean",
title: "Active",
},
},
},
formData: {
active: "wrong",
},
uiSchema: {},
localize: "es",
};
7 changes: 5 additions & 2 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default class Form extends Component {
safeRenderCompletion: false,
noHtml5Validate: false,
ErrorList: DefaultErrorList,
localize: "en",
};

constructor(props) {
Expand Down Expand Up @@ -88,14 +89,15 @@ export default class Form extends Component {
}

validate(formData, schema = this.props.schema) {
const { validate, transformErrors } = this.props;
const { validate, transformErrors, localize } = this.props;
const { definitions } = this.getRegistry();
const resolvedSchema = retrieveSchema(schema, definitions, formData);
return validateFormData(
formData,
resolvedSchema,
validate,
transformErrors
transformErrors,
localize
);
}

Expand Down Expand Up @@ -294,5 +296,6 @@ if (process.env.NODE_ENV !== "production") {
transformErrors: PropTypes.func,
safeRenderCompletion: PropTypes.bool,
formContext: PropTypes.object,
localize: PropTypes.string,
};
}
5 changes: 4 additions & 1 deletion src/validate.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import toPath from "lodash.topath";
import Ajv from "ajv";
import localize from "ajv-i18n";
const ajv = new Ajv({
errorDataPath: "property",
allErrors: true,
Expand Down Expand Up @@ -152,10 +153,12 @@ export default function validateFormData(
formData,
schema,
customValidate,
transformErrors
transformErrors,
local
) {
try {
ajv.validate(schema, formData);
localize[local] && localize[local](ajv.errors);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All avji18n will be imported. Why, insted of take a "string" in props, taking a function, that can perform the localization ?
The user (dev?) will can import only needed local function, and pass it.

Suggested change
localize[local] && localize[local](ajv.errors);
localize && localize(ajv.errors);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miguelcast do you see how it would work here? Then to use the form, we would use the following:

var localize = require('ajv-i18n/localize/ru');
...
<Form localize={localize} ... />

} catch (e) {
// swallow errors thrown in ajv due to invalid schemas, these
// still get displayed
Expand Down
32 changes: 16 additions & 16 deletions test/Form_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ describe("Form", () => {
});

expect(comp.state.errorSchema).eql({
__errors: ["should NOT be shorter than 8 characters"],
__errors: ["should not be shorter than 8 characters"],
});
});

Expand All @@ -1131,7 +1131,7 @@ describe("Form", () => {
expect(node.querySelectorAll(".field-error")).to.have.length.of(1);
expect(
node.querySelector(".field-string .error-detail").textContent
).eql("should NOT be shorter than 8 characters");
).eql("should not be shorter than 8 characters");
});
});

Expand Down Expand Up @@ -1186,7 +1186,7 @@ describe("Form", () => {
Simulate.submit(node);

expect(comp.state.errorSchema).eql({
__errors: ["should NOT be shorter than 8 characters"],
__errors: ["should not be shorter than 8 characters"],
});
});

Expand All @@ -1204,7 +1204,7 @@ describe("Form", () => {
sinon.match(value => {
return (
value.length === 1 &&
value[0].message === "should NOT be shorter than 8 characters"
value[0].message === "should not be shorter than 8 characters"
);
})
);
Expand Down Expand Up @@ -1253,7 +1253,7 @@ describe("Form", () => {
const { comp } = createFormComponent(formProps);

expect(comp.state.errorSchema).eql({
__errors: ["should NOT be shorter than 8 characters"],
__errors: ["should not be shorter than 8 characters"],
});
});

Expand All @@ -1263,7 +1263,7 @@ describe("Form", () => {
expect(node.querySelectorAll(".field-error")).to.have.length.of(1);
expect(
node.querySelector(".field-string .error-detail").textContent
).eql("should NOT be shorter than 8 characters");
).eql("should not be shorter than 8 characters");
});
});

Expand All @@ -1282,7 +1282,7 @@ describe("Form", () => {
const { comp } = createFormComponent(formProps);
expect(comp.state.errorSchema).eql({
__errors: [
"should NOT be shorter than 8 characters",
"should not be shorter than 8 characters",
'should match pattern "d+"',
],
});
Expand All @@ -1295,7 +1295,7 @@ describe("Form", () => {
const errors = [].map.call(liNodes, li => li.textContent);

expect(errors).eql([
"should NOT be shorter than 8 characters",
"should not be shorter than 8 characters",
'should match pattern "d+"',
]);
});
Expand Down Expand Up @@ -1333,7 +1333,7 @@ describe("Form", () => {
expect(comp.state.errorSchema).eql({
level1: {
level2: {
__errors: ["should NOT be shorter than 8 characters"],
__errors: ["should not be shorter than 8 characters"],
},
},
});
Expand All @@ -1347,7 +1347,7 @@ describe("Form", () => {

expect(node.querySelectorAll(".field-error")).to.have.length.of(1);
expect(errorDetail.textContent).eql(
"should NOT be shorter than 8 characters"
"should not be shorter than 8 characters"
);
});
});
Expand Down Expand Up @@ -1387,7 +1387,7 @@ describe("Form", () => {
const errors = [].map.call(liNodes, li => li.textContent);

expect(fieldNodes[1].classList.contains("field-error")).eql(true);
expect(errors).eql(["should NOT be shorter than 4 characters"]);
expect(errors).eql(["should not be shorter than 4 characters"]);
});

it("should not denote errors on non impacted fields", () => {
Expand Down Expand Up @@ -1446,7 +1446,7 @@ describe("Form", () => {
const liNodes = node.querySelectorAll(".field-string .error-detail li");
const errors = [].map.call(liNodes, li => li.textContent);

expect(errors).eql(["should NOT be shorter than 4 characters"]);
expect(errors).eql(["should not be shorter than 4 characters"]);
});
});

Expand Down Expand Up @@ -1502,8 +1502,8 @@ describe("Form", () => {

expect(errors).eql([
null,
"should NOT be shorter than 4 characters",
"should NOT be shorter than 4 characters",
"should not be shorter than 4 characters",
"should not be shorter than 4 characters",
null,
]);
});
Expand Down Expand Up @@ -1535,7 +1535,7 @@ describe("Form", () => {
expect(comp.state.errorSchema).eql({
1: {
foo: {
__errors: ["should NOT be shorter than 4 characters"],
__errors: ["should not be shorter than 4 characters"],
},
},
});
Expand All @@ -1551,7 +1551,7 @@ describe("Form", () => {
const errors = [].map.call(liNodes, li => li.textContent);

expect(fieldNodes[1].classList.contains("field-error")).eql(true);
expect(errors).eql(["should NOT be shorter than 4 characters"]);
expect(errors).eql(["should not be shorter than 4 characters"]);
});
});

Expand Down
22 changes: 13 additions & 9 deletions test/validate_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,21 +238,23 @@ describe("Validation", () => {

it("should validate a required field", () => {
expect(comp.state.errors).to.have.length.of(1);
expect(comp.state.errors[0].message).eql("is a required property");
expect(comp.state.errors[0].message).eql(
"should have required property foo"
);
});

it("should render errors", () => {
expect(node.querySelectorAll(".errors li")).to.have.length.of(1);
expect(node.querySelector(".errors li").textContent).eql(
".foo is a required property"
".foo should have required property foo"
miguelcast marked this conversation as resolved.
Show resolved Hide resolved
);
});

it("should trigger the onError handler", () => {
sinon.assert.calledWith(
onError,
sinon.match(errors => {
return errors[0].message === "is a required property";
return errors[0].message === "should have required property foo";
})
);
});
Expand Down Expand Up @@ -290,14 +292,14 @@ describe("Validation", () => {
it("should validate a minLength field", () => {
expect(comp.state.errors).to.have.length.of(1);
expect(comp.state.errors[0].message).eql(
"should NOT be shorter than 10 characters"
"should not be shorter than 10 characters"
);
});

it("should render errors", () => {
expect(node.querySelectorAll(".errors li")).to.have.length.of(1);
expect(node.querySelector(".errors li").textContent).eql(
".foo should NOT be shorter than 10 characters"
".foo should not be shorter than 10 characters"
);
});

Expand All @@ -306,7 +308,7 @@ describe("Validation", () => {
onError,
sinon.match(errors => {
return (
errors[0].message === "should NOT be shorter than 10 characters"
errors[0].message === "should not be shorter than 10 characters"
);
})
);
Expand Down Expand Up @@ -422,7 +424,7 @@ describe("Validation", () => {
},
pass2: {
__errors: [
"should NOT be shorter than 3 characters",
"should not be shorter than 3 characters",
"Passwords don't match",
],
},
Expand Down Expand Up @@ -549,7 +551,9 @@ describe("Validation", () => {

it("should validate a required field", () => {
expect(comp.state.errors).to.have.length.of(1);
expect(comp.state.errors[0].message).eql("is a required property");
expect(comp.state.errors[0].message).eql(
"should have required property foo"
);
});

it("should not render error list if showErrorList prop true", () => {
Expand All @@ -560,7 +564,7 @@ describe("Validation", () => {
sinon.assert.calledWith(
onError,
sinon.match(errors => {
return errors[0].message === "is a required property";
return errors[0].message === "should have required property foo";
})
);
});
Expand Down