Skip to content

Commit bf4e7ba

Browse files
Added support for ICU message format (#2745)
1 parent d85e30d commit bf4e7ba

11 files changed

+866
-2
lines changed

components.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components.json

+4
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,10 @@
528528
"title": "Icon",
529529
"owner": "Golmote"
530530
},
531+
"icu-message-format": {
532+
"title": "ICU Message Format",
533+
"owner": "RunDevelopment"
534+
},
531535
"idris": {
532536
"title": "Idris",
533537
"alias": "idr",
+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// https://unicode-org.github.io/icu/userguide/format_parse/messages/
2+
// https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/text/MessageFormat.html
3+
4+
(function (Prism) {
5+
6+
/**
7+
* @param {string} source
8+
* @param {number} level
9+
* @returns {string}
10+
*/
11+
function nested(source, level) {
12+
if (level <= 0) {
13+
return /[]/.source;
14+
} else {
15+
return source.replace(/<SELF>/g, function () { return nested(source, level - 1) });
16+
}
17+
}
18+
19+
var stringPattern = /'[{}:=,](?:[^']|'')*'(?!')/;
20+
21+
var escape = {
22+
pattern: /''/,
23+
greedy: true,
24+
alias: 'operator'
25+
};
26+
var string = {
27+
pattern: stringPattern,
28+
greedy: true,
29+
inside: {
30+
'escape': escape
31+
}
32+
};
33+
34+
var argumentSource = nested(
35+
/\{(?:[^{}']|'(?![{},'])|''|<STR>|<SELF>)*\}/.source
36+
.replace(/<STR>/g, function () { return stringPattern.source; }),
37+
8
38+
);
39+
40+
var nestedMessage = {
41+
pattern: RegExp(argumentSource),
42+
inside: {
43+
'message': {
44+
pattern: /^(\{)[\s\S]+(?=\}$)/,
45+
lookbehind: true,
46+
inside: null // see below
47+
},
48+
'message-delimiter': {
49+
pattern: /./,
50+
alias: 'punctuation'
51+
}
52+
}
53+
};
54+
55+
Prism.languages['icu-message-format'] = {
56+
'argument': {
57+
pattern: RegExp(argumentSource),
58+
greedy: true,
59+
inside: {
60+
'content': {
61+
pattern: /^(\{)[\s\S]+(?=\}$)/,
62+
lookbehind: true,
63+
inside: {
64+
'argument-name': {
65+
pattern: /^(\s*)[^{}:=,\s]+/,
66+
lookbehind: true
67+
},
68+
'choice-style': {
69+
// https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1ChoiceFormat.html#details
70+
pattern: /^(\s*,\s*choice\s*,\s*)\S(?:[\s\S]*\S)?/,
71+
lookbehind: true,
72+
inside: {
73+
'punctuation': /\|/,
74+
'range': {
75+
pattern: /^(\s*)[+-]?(?:\d+(?:\.\d*)?|\u221e)\s*[<#\u2264]/,
76+
lookbehind: true,
77+
inside: {
78+
'operator': /[<#\u2264]/,
79+
'number': /\S+/
80+
}
81+
},
82+
rest: null // see below
83+
}
84+
},
85+
'plural-style': {
86+
// https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/text/PluralFormat.html#:~:text=Patterns%20and%20Their%20Interpretation
87+
pattern: /^(\s*,\s*(?:plural|selectordinal)\s*,\s*)\S(?:[\s\S]*\S)?/,
88+
lookbehind: true,
89+
inside: {
90+
'offset': /^offset:\s*\d+/,
91+
'nested-message': nestedMessage,
92+
'selector': {
93+
pattern: /=\d+|[^{}:=,\s]+/,
94+
inside: {
95+
'keyword': /^(?:zero|one|two|few|many|other)$/
96+
}
97+
}
98+
}
99+
},
100+
'select-style': {
101+
// https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/text/SelectFormat.html#:~:text=Patterns%20and%20Their%20Interpretation
102+
pattern: /^(\s*,\s*select\s*,\s*)\S(?:[\s\S]*\S)?/,
103+
lookbehind: true,
104+
inside: {
105+
'nested-message': nestedMessage,
106+
'selector': {
107+
pattern: /[^{}:=,\s]+/,
108+
inside: {
109+
'keyword': /^other$/
110+
}
111+
}
112+
}
113+
},
114+
'keyword': /\b(?:choice|plural|select|selectordinal)\b/,
115+
'arg-type': {
116+
pattern: /\b(?:number|date|time|spellout|ordinal|duration)\b/,
117+
alias: 'keyword'
118+
},
119+
'arg-skeleton': {
120+
pattern: /(,\s*)::[^{}:=,\s]+/,
121+
lookbehind: true
122+
},
123+
'arg-style': {
124+
pattern: /(,\s*)(?:short|medium|long|full|integer|currency|percent)(?=\s*$)/,
125+
lookbehind: true
126+
},
127+
'arg-style-text': {
128+
pattern: RegExp(/(^\s*,\s*(?=\S))/.source + nested(/(?:[^{}']|'[^']*'|\{(?:<SELF>)?\})+/.source, 8) + '$'),
129+
lookbehind: true,
130+
alias: 'string'
131+
},
132+
'punctuation': /,/
133+
}
134+
},
135+
'argument-delimiter': {
136+
pattern: /./,
137+
alias: 'operator'
138+
}
139+
}
140+
},
141+
'escape': escape,
142+
'string': string
143+
};
144+
145+
nestedMessage.inside.message.inside = Prism.languages['icu-message-format'];
146+
Prism.languages['icu-message-format'].argument.inside.content.inside['choice-style'].inside.rest = Prism.languages['icu-message-format'];
147+
148+
}(Prism));

components/prism-icu-message-format.min.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<h2>Full example</h2>
2+
<pre><code>https://unicode-org.github.io/icu/userguide/format_parse/messages/
3+
4+
{gender_of_host, select,
5+
female {
6+
{num_guests, plural, offset:1
7+
=0 {{host} does not give a party.}
8+
=1 {{host} invites {guest} to her party.}
9+
=2 {{host} invites {guest} and one other person to her party.}
10+
other {{host} invites {guest} and # other people to her party.}}}
11+
male {
12+
{num_guests, plural, offset:1
13+
=0 {{host} does not give a party.}
14+
=1 {{host} invites {guest} to his party.}
15+
=2 {{host} invites {guest} and one other person to his party.}
16+
other {{host} invites {guest} and # other people to his party.}}}
17+
other {
18+
{num_guests, plural, offset:1
19+
=0 {{host} does not give a party.}
20+
=1 {{host} invites {guest} to their party.}
21+
=2 {{host} invites {guest} and one other person to their party.}
22+
other {{host} invites {guest} and # other people to their party.}}}}</code></pre>

plugins/show-language/prism-show-language.js

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"hpkp": "HTTP Public-Key-Pins",
9090
"hsts": "HTTP Strict-Transport-Security",
9191
"ichigojam": "IchigoJam",
92+
"icu-message-format": "ICU Message Format",
9293
"idr": "Idris",
9394
"ignore": ".ignore",
9495
"gitignore": ".gitignore",

plugins/show-language/prism-show-language.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
At {1,time,::jmm} on {1,date,::dMMMM}, there was {2} on planet {3,number,integer}.
2+
3+
----------------------------------------------------
4+
5+
[
6+
"At ",
7+
["argument", [
8+
["argument-delimiter", "{"],
9+
["content", [
10+
["argument-name", "1"],
11+
["punctuation", ","],
12+
["arg-type", "time"],
13+
["punctuation", ","],
14+
["arg-skeleton", "::jmm"]
15+
]],
16+
["argument-delimiter", "}"]
17+
]],
18+
" on ",
19+
["argument", [
20+
["argument-delimiter", "{"],
21+
["content", [
22+
["argument-name", "1"],
23+
["punctuation", ","],
24+
["arg-type", "date"],
25+
["punctuation", ","],
26+
["arg-skeleton", "::dMMMM"]
27+
]],
28+
["argument-delimiter", "}"]
29+
]],
30+
", there was ",
31+
["argument", [
32+
["argument-delimiter", "{"],
33+
["content", [
34+
["argument-name", "2"]
35+
]],
36+
["argument-delimiter", "}"]
37+
]],
38+
" on planet ",
39+
["argument", [
40+
["argument-delimiter", "{"],
41+
["content", [
42+
["argument-name", "3"],
43+
["punctuation", ","],
44+
["arg-type", "number"],
45+
["punctuation", ","],
46+
["arg-style", "integer"]
47+
]],
48+
["argument-delimiter", "}"]
49+
]],
50+
"."
51+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
The disk {1} contains {0,choice,0#no files|1#one file|1<{0,number,integer} files}
2+
3+
{3, choice, -1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+ |2#is two |2<is more than 2.}
4+
5+
----------------------------------------------------
6+
7+
[
8+
"The disk ",
9+
["argument", [
10+
["argument-delimiter", "{"],
11+
["content", [
12+
["argument-name", "1"]
13+
]],
14+
["argument-delimiter", "}"]
15+
]],
16+
" contains ",
17+
["argument", [
18+
["argument-delimiter", "{"],
19+
["content", [
20+
["argument-name", "0"],
21+
["punctuation", ","],
22+
["keyword", "choice"],
23+
["punctuation", ","],
24+
["choice-style", [
25+
["range", [
26+
["number", "0"],
27+
["operator", "#"]
28+
]],
29+
"no files",
30+
["punctuation", "|"],
31+
["range", [
32+
["number", "1"],
33+
["operator", "#"]
34+
]],
35+
"one file",
36+
["punctuation", "|"],
37+
["range", [
38+
["number", "1"],
39+
["operator", "<"]
40+
]],
41+
["argument", [
42+
["argument-delimiter", "{"],
43+
["content", [
44+
["argument-name", "0"],
45+
["punctuation", ","],
46+
["arg-type", "number"],
47+
["punctuation", ","],
48+
["arg-style", "integer"]
49+
]],
50+
["argument-delimiter", "}"]
51+
]],
52+
" files"
53+
]]
54+
]],
55+
["argument-delimiter", "}"]
56+
]],
57+
58+
["argument", [
59+
["argument-delimiter", "{"],
60+
["content", [
61+
["argument-name", "3"],
62+
["punctuation", ","],
63+
["keyword", "choice"],
64+
["punctuation", ","],
65+
["choice-style", [
66+
["range", [
67+
["number", "-1"],
68+
["operator", "#"]
69+
]],
70+
"is negative",
71+
["punctuation", "|"],
72+
["range", [
73+
["number", "0"],
74+
["operator", "#"]
75+
]],
76+
"is zero or fraction ",
77+
["punctuation", "|"],
78+
["range", [
79+
["number", "1"],
80+
["operator", "#"]
81+
]],
82+
"is one ",
83+
["punctuation", "|"],
84+
["range", [
85+
["number", "1.0"],
86+
["operator", "<"]
87+
]],
88+
"is 1+ ",
89+
["punctuation", "|"],
90+
["range", [
91+
["number", "2"],
92+
["operator", "#"]
93+
]],
94+
"is two ",
95+
["punctuation", "|"],
96+
["range", [
97+
["number", "2"],
98+
["operator", "<"]
99+
]],
100+
"is more than 2."
101+
]]
102+
]],
103+
["argument-delimiter", "}"]
104+
]]
105+
]

0 commit comments

Comments
 (0)