OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * Contains a parser for ICU format plural/gender/select format for localized | |
7 * messages. See extract_to_arb.dart and make_hardcoded_translation.dart. | |
8 */ | |
9 library icu_parser; | |
10 | |
11 import 'package:intl/src/intl_message.dart'; | |
12 import 'package:petitparser/petitparser.dart'; | |
13 | |
14 /** | |
15 * This defines a grammar for ICU MessageFormat syntax. Usage is | |
16 * new IcuParser.message.parse(<string>).value; | |
17 * The "parse" method will return a Success or Failure object which responds | |
18 * to "value". | |
19 */ | |
20 class IcuParser { | |
21 get openCurly => char("{"); | |
22 | |
23 get closeCurly => char("}"); | |
24 get quotedCurly => (string("'{'") | string("'}'")).map((x) => x[1]); | |
25 | |
26 get icuEscapedText => quotedCurly | twoSingleQuotes; | |
27 get curly => (openCurly | closeCurly); | |
28 get notAllowedInIcuText => curly | char("<"); | |
29 get icuText => notAllowedInIcuText.neg(); | |
30 get notAllowedInNormalText => char("{"); | |
31 get normalText => notAllowedInNormalText.neg(); | |
32 get messageText => (icuEscapedText | icuText) | |
33 .plus().map((x) => x.join()); | |
34 get nonIcuMessageText => normalText.plus().map((x) => x.join()); | |
35 get twoSingleQuotes => string("''").map((x) => "'"); | |
36 get number => digit().plus().flatten().trim().map(int.parse); | |
37 get id => (letter() & (word() | char("_")).star()).flatten(); | |
38 get comma => char(",").trim(); | |
39 | |
40 /** | |
41 * Given a list of possible keywords, return a rule that accepts any of them. | |
42 * e.g., given ["male", "female", "other"], accept any of them. | |
43 */ | |
44 asKeywords(list) => list.map(string).reduce((a, b) => a | b).flatten().trim(); | |
45 | |
46 get pluralKeyword => asKeywords( | |
47 ["=0", "=1", "=2", "zero", "one", "two", "few", "many", "other"]); | |
48 get genderKeyword => asKeywords( | |
49 ["female", "male", "other"]); | |
50 | |
51 var interiorText = undefined(); | |
52 | |
53 get preface => (openCurly & id & comma).map((values) => values[1]); | |
54 | |
55 get pluralLiteral => string("plural"); | |
56 get pluralClause => (pluralKeyword & openCurly & interiorText & closeCurly) | |
57 .trim().map((result) => [result[0], result[2]]); | |
58 get plural => | |
59 preface & pluralLiteral & comma & pluralClause.plus() & closeCurly; | |
60 get intlPlural => | |
61 plural.map((values) => new Plural.from(values.first, values[3], null)); | |
62 | |
63 get selectLiteral => string("select"); | |
64 get genderClause => (genderKeyword & openCurly & interiorText & closeCurly) | |
65 .trim().map((result) => [result[0], result[2]]); | |
66 get gender => | |
67 preface & selectLiteral & comma & genderClause.plus() & closeCurly; | |
68 get intlGender => | |
69 gender.map((values) => new Gender.from(values.first, values[3], null)); | |
70 get selectClause => (id & openCurly & interiorText & closeCurly).map( | |
71 (x) => [x.first, x[2]]); | |
72 get generalSelect => preface & selectLiteral & comma & | |
73 selectClause.plus() & closeCurly; | |
74 get intlSelect => generalSelect.map( | |
75 (values) => new Select.from(values.first, values[3], null)); | |
76 | |
77 get pluralOrGenderOrSelect => intlPlural | intlGender | intlSelect; | |
78 | |
79 get contents => pluralOrGenderOrSelect | parameter | messageText; | |
80 get simpleText => (nonIcuMessageText | parameter | openCurly).plus(); | |
81 get empty => epsilon().map((_) => ''); | |
82 | |
83 get parameter => (openCurly & id & closeCurly).map( | |
84 (param) => new VariableSubstitution.named(param[1], null)); | |
85 | |
86 /** | |
87 * The primary entry point for parsing. Accepts a string and produces | |
88 * a parsed representation of it as a Message. | |
89 */ | |
90 get message => (pluralOrGenderOrSelect | empty).map((chunk) => | |
91 Message.from(chunk, null)); | |
92 | |
93 /** | |
94 * Represents an ordinary message, i.e. not a plural/gender/select, although | |
95 * it may have parameters. | |
96 */ | |
97 get nonIcuMessage => (simpleText | empty).map((chunk) => | |
98 Message.from(chunk, null)); | |
99 | |
100 get stuff => (pluralOrGenderOrSelect | empty).map( | |
101 (chunk) => Message.from(chunk, null)); | |
102 | |
103 IcuParser() { | |
104 // There is a cycle here, so we need the explicit set to avoid | |
105 // infinite recursion. | |
106 interiorText.set(contents.plus() | empty); | |
107 } | |
108 } | |
OLD | NEW |