Skip to content

Commit aad1db1

Browse files
committed
Messages: Support default messages
1 parent a559aed commit aad1db1

File tree

3 files changed

+186
-78
lines changed

3 files changed

+186
-78
lines changed

README.md

Lines changed: 100 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,25 @@ The numeric value to be formatted. Required.
5555

5656
#### Usage
5757

58-
- Default format with USD in English
59-
60-
`<FormatCurrency currency="USD">{150}</FormatCurrency>`
61-
62-
Result: `<span>$150.00</span>` when using the `en` (English) locale.
63-
64-
- Accounting format with EUR in Portuguese
65-
66-
`<FormatCurrency currency="EUR" options={{ style: "accounting" }}>{-150}</FormatCurrency>`
67-
68-
Result: `<span>(€150,00)</span>` when using the `pt` (Portuguese) locale.
58+
Default format with USD.
59+
60+
```js
61+
<FormatCurrency currency="USD">{150}</FormatCurrency>
62+
// Which would render:
63+
// <span>$150.00</span> when using the `en` (English) locale, or
64+
// <span>US$150,00</span> when using the `pt` (Portuguese) locale.
65+
```
66+
67+
Accounting format with EUR.
68+
69+
```js
70+
<FormatCurrency currency="EUR" options={{style: "accounting"}}>
71+
{-150}
72+
</FormatCurrency>
73+
// Which would render:
74+
// <span>(€150.00)</span> when using the `en` (English) locale, or
75+
// <span>(€150,00)</span> when using the `pt` (Portuguese) locale.
76+
```
6977

7078
### FormatDate
7179

@@ -88,17 +96,27 @@ The date object to be formatted. Required.
8896

8997
#### Usage
9098

91-
- Simple string skeleton
92-
93-
`<FormatDate options={{skeleton: "GyMMMd"}}>{new Date()}</FormatDate>`
99+
Simple string skeleton.
94100

95-
Result: `<span>Feb 27, 2015 AD</span>` when using the `en` (English) locale.
101+
```js
102+
<FormatDate options={{skeleton: "GyMMMd"}}>
103+
{new Date()}
104+
</FormatDate>
105+
// Which would render:
106+
// <span>Feb 27, 2015 AD</span> when using the `en` (English) locale, or
107+
// <span>27 de fev de 2015 d.C.</span> when using the `pt` (Portuguese) locale.
108+
```
96109

97-
- Medium length date and time
110+
Medium length date and time.
98111

99-
`<FormatDate options={{ datetime: "medium" }}>{new Date()}</FormatDate>`
100-
101-
Result: `<span>27 de fev de 2015 11:17:10</span>` when using the `pt` (Portuguese) locale.
112+
```js
113+
<FormatDate options={{datetime: "medium"}}>
114+
{new Date()}
115+
</FormatDate>
116+
// Which would render:
117+
// <span>Feb 27, 2015, 11:17:10 AM</span> when using the `en` (English) locale, or
118+
// <span>27 de fev de 2015 11:17:10</span> when using the `pt` (Portuguese) locale.
119+
```
102120

103121
### FormatMessage
104122

@@ -109,63 +127,60 @@ It allows for the creation of internationalized messages (as defined by the [ICU
109127

110128
#### Children
111129

112-
String or array path to traverse a set of messages store in JSON format. Required.
130+
Required unless the `path` property is set. It's a string with the default message. Either this or the `path` property is required.
113131

114132
#### Props
115133

116-
- **path** - required
117-
- String or array path to traverse a set of messages store in JSON format
134+
- **path** - required unless children is set
135+
- String or array path to traverse a set of messages store in JSON format. Defaults to the message itself defined by the children.
118136
- **variables** - optional
119137
- An array (where variables are represented as indeces) or object (for named variables) which contains values for variable replacement within a message.
120138
- **locale** - optional
121139
- A string value representing the locale (as defined by BCP47) used to override the default locale (preferred) set by your application using `Globalize.locale(locale)` when formatting the amount.
122140

123141
#### Usage
124142

125-
Below message JSON used in these examples:
126-
```JS
127-
{
128-
en: {
129-
salutations: {
130-
hi: "Hi",
131-
bye: "Bye"
132-
},
133-
variables: {
134-
hello: "Hello, {0} {1} {2}",
135-
hey: "Hey, {first} {middle} {last}"
136-
}
137-
},
138-
"pt": {
139-
salutations: {
140-
hi: "Oi",
141-
bye: "Tchau"
142-
},
143-
variables: {
144-
hello: "Olá, {0} {1} {2}",
145-
hey: "Ei, {first} {middle} {last}"
146-
}
147-
}
148-
}
149-
```
150-
- Simple salutation
151-
152-
Hi: `<FormatMessage>{"salutations/hi"}</FormatMessage>`
153-
154-
Result:
155-
156-
`<span>Hi</span>` when using the `en` (English) locale, or
157-
158-
`<span>Oi</span>` when using the `pt` (Portuguese) locale.
159-
160-
- Variable Replacement
161-
162-
Array: `<FormatMessage variables={["Wolfgang", "Amadeus", "Mozart"]}>{"variables/hello"}</FormatMessage>`
163-
164-
Result: `<span>Hello, Wolfgang Amadeus Mozart</span>` when using the `en` (English) locale.
165-
166-
Object in Portuguese: `<FormatMessage variables={{first:"Wolfgang", middle:"Amadeus", last:"Mozart"}}>{"variables/hey"}</FormatMessage>`
167-
168-
Result: `<span>Ei, Wolfgang Amadeus Mozart</span>` when using the `pt` (Portuguese) locale.
143+
Below translation message JSON used in these examples:
144+
```js
145+
{
146+
"pt": {
147+
"Hi": "Oi",
148+
"Hi, {0} {1} {2}": "Olá, {0} {1} {2}",
149+
"Hello, {first} {middle} {last}": "Ei, {first} {middle} {last}"
150+
}
151+
}
152+
```
153+
154+
Simple salutation.
155+
156+
```js
157+
<FormatMessage>Hi</FormatMessage>
158+
// Which would render:
159+
// <span>Hi</span> when using the default message, in this case `en` (English).
160+
// <span>Oi</span> when using the `pt` (Portuguese) locale and its translation messages.
161+
```
162+
163+
Variable Replacement.
164+
165+
```js
166+
// Using Array.
167+
<FormatMessage variables={["Wolfgang", "Amadeus", "Mozart"]}>
168+
{"Hi, {0} {1} {2}"}
169+
</FormatMessage>
170+
// Which would render:
171+
// <span>Hello, Wolfgang Amadeus Mozart</span> when using the default message, in this case `en` (English).
172+
// <span>Hello, Wolfgang Amadeus Mozart</span> when using the `en` (English) locale and its translation messages.
173+
174+
// Using Object.
175+
<FormatMessage variables={{first:"Wolfgang", middle:"Amadeus", last:"Mozart"}}>
176+
{"Hey, {first} {middle} {last}"}
177+
</FormatMessage>
178+
// Which would render:
179+
// <span>Hey, Wolfgang Amadeus Mozart</span> when using the default message, in this case `en` (English).
180+
// <span>Ei, Wolfgang Amadeus Mozart</span> when using the `pt` (Portuguese) locale and its translation messages.
181+
```
182+
183+
See [messageFormatter][] docs in Globalize for more message examples (e.g., pluralization or gender selection).
169184

170185
### FormatNumber
171186

@@ -188,17 +203,25 @@ The number to be formatted. Required.
188203

189204
#### Usage
190205

191-
- Default format pi in English
192-
193-
`<FormatNumber locale="en">{Math.PI}</FormatNumber>`
194-
195-
Result: `<span>3.142</span>` when using the `en` (English) locale.
196-
197-
- Show at least 2 decimal places in Portuguese
198-
199-
`<FormatNumber options={{ minimumFractionDigits: 2 }}>{10000}</FormatNumber>`
200-
201-
Result: `<span>10.000,00</span>` when using the `pt` (Portuguese) locale.
206+
Default format pi.
207+
208+
```js
209+
<FormatNumber locale="en">{Math.PI}</FormatNumber>
210+
// Which would render:
211+
// <span>3.142</span> when using the `en` (English) locale, or
212+
// <span>3,142</span> when using the `pt` (Portuguese) locale.
213+
```
214+
215+
Show at least 2 decimal places.
216+
217+
```js
218+
<FormatNumber options={{minimumFractionDigits: 2}}>
219+
{10000}
220+
</FormatNumber>
221+
// Which would render:
222+
// <span>10,000.00</span> when using the `en` (English) locale, or
223+
// <span>10.000,00</span> when using the `pt` (Portuguese) locale.
224+
```
202225

203226
## License
204227

src/generator.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function capitalizeFirstLetter(string) {
88
function generator(fn, argArray, options) {
99
var Fn = capitalizeFirstLetter(fn);
1010
options = options || {};
11+
var beforeFormat = options.beforeFormat || function() {};
1112
return {
1213
displayName: Fn,
1314
format: function() {
@@ -28,6 +29,7 @@ function generator(fn, argArray, options) {
2829
this.instance = Globalize(this.props["locale"]);
2930
}
3031

32+
beforeFormat.call(this);
3133
return React.DOM.span(null, this.format());
3234
}
3335
}

src/message.js

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,87 @@ import generator from "./generator";
44
import "globalize/message";
55
import "globalize/plural";
66

7-
export default React.createClass(generator("formatMessage", ["path", "variables"]));
7+
function messageSetup(componentProps, instance, args) {
8+
var defaultMessage, path;
9+
var children = componentProps.children;
10+
11+
function extractFromFnComments(fn) {
12+
return fn.toString().replace(/(function \(\) \{\/\*|\*\/\})/g, "");
13+
}
14+
15+
function getDefaultMessage(children) {
16+
if (typeof children === "string") {
17+
return children;
18+
} else if (typeof children === "function") {
19+
return extractFromFnComments(children);
20+
} else {
21+
throw new Error("Invalid default message type `" + typeof children + "`");
22+
}
23+
}
24+
25+
function getMessage() {
26+
return instance.cldr.get(["globalize-messages/{bundle}"].concat(path));
27+
}
28+
29+
function setMessage(message) {
30+
var data = {};
31+
function set(data, path, value) {
32+
var i;
33+
var node = data;
34+
var length = path.length;
35+
36+
for (i = 0; i < length - 1; i++) {
37+
if (!node[path[i]]) {
38+
node[path[i]] = {};
39+
}
40+
node = node[path[i]];
41+
}
42+
node[path[i]] = value;
43+
}
44+
set(data, [instance.cldr.attributes.bundle].concat(path), message);
45+
Globalize.loadMessages(data);
46+
}
47+
48+
// Set path.
49+
if (args[0]) {
50+
path = args[0].split("/");
51+
} else {
52+
defaultMessage = getDefaultMessage(children);
53+
args[0] = sanitizePath(defaultMessage);
54+
path = [args[0]];
55+
}
56+
57+
// Development mode only.
58+
if (instance.cldr) {
59+
if (!getMessage()) {
60+
defaultMessage = defaultMessage || getDefaultMessage(children);
61+
setMessage(defaultMessage);
62+
}
63+
}
64+
65+
}
66+
67+
function sanitizePath(pathString) {
68+
return pathString.trim().replace(/\{/g, "(").replace(/\}/g, ")").replace(/\//g, "|").replace(/\n/g, " ").replace(/ +/g, " ").replace(/"/g, "'");
69+
}
70+
71+
// Overload Globalize's `.formatMessage` to allow default message.
72+
var messageFormatterSuper = Globalize.messageFormatter;
73+
Globalize.messageFormatter =
74+
Globalize.prototype.messageFormatter = function(pathOrMessage) {
75+
var aux = {};
76+
var sanitizedPath = sanitizePath(pathOrMessage);
77+
if (this.cldr && this.cldr.get(["globalize-messages/{bundle}", sanitizedPath]) === undefined) {
78+
aux[this.cldr.attributes.bundle] = {};
79+
aux[this.cldr.attributes.bundle][sanitizedPath] = pathOrMessage;
80+
Globalize.loadMessages(aux);
81+
}
82+
arguments[0] = sanitizedPath;
83+
return messageFormatterSuper.apply(this, arguments);
84+
};
85+
86+
export default React.createClass(generator("formatMessage", ["path", "variables"], {
87+
beforeFormat: function() {
88+
messageSetup(this.props, this.instance, this.args);
89+
}
90+
}));

0 commit comments

Comments
 (0)