xref: /aosp_15_r20/frameworks/base/tools/aapt2/compile/Pseudolocalizer.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "compile/Pseudolocalizer.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
20*d57664e9SAndroid Build Coastguard Worker 
21*d57664e9SAndroid Build Coastguard Worker using android::StringPiece;
22*d57664e9SAndroid Build Coastguard Worker 
23*d57664e9SAndroid Build Coastguard Worker using namespace std::literals;
24*d57664e9SAndroid Build Coastguard Worker 
25*d57664e9SAndroid Build Coastguard Worker namespace aapt {
26*d57664e9SAndroid Build Coastguard Worker 
27*d57664e9SAndroid Build Coastguard Worker // String basis to generate expansion
28*d57664e9SAndroid Build Coastguard Worker static constexpr auto kExpansionString =
29*d57664e9SAndroid Build Coastguard Worker     "one two three "
30*d57664e9SAndroid Build Coastguard Worker     "four five six seven eight nine ten eleven twelve thirteen "
31*d57664e9SAndroid Build Coastguard Worker     "fourteen fiveteen sixteen seventeen nineteen twenty"sv;
32*d57664e9SAndroid Build Coastguard Worker 
33*d57664e9SAndroid Build Coastguard Worker // Special unicode characters to override directionality of the words
34*d57664e9SAndroid Build Coastguard Worker static constexpr auto kRlm = "\u200f"sv;
35*d57664e9SAndroid Build Coastguard Worker static constexpr auto kRlo = "\u202e"sv;
36*d57664e9SAndroid Build Coastguard Worker static constexpr auto kPdf = "\u202c"sv;
37*d57664e9SAndroid Build Coastguard Worker 
38*d57664e9SAndroid Build Coastguard Worker // Placeholder marks
39*d57664e9SAndroid Build Coastguard Worker static constexpr auto kPlaceholderOpen = "\u00bb"sv;
40*d57664e9SAndroid Build Coastguard Worker static constexpr auto kPlaceholderClose = "\u00ab"sv;
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker static const char kArgStart = '{';
43*d57664e9SAndroid Build Coastguard Worker static const char kArgEnd = '}';
44*d57664e9SAndroid Build Coastguard Worker 
45*d57664e9SAndroid Build Coastguard Worker class PseudoMethodNone : public PseudoMethodImpl {
46*d57664e9SAndroid Build Coastguard Worker  public:
Text(StringPiece text)47*d57664e9SAndroid Build Coastguard Worker   std::string Text(StringPiece text) override {
48*d57664e9SAndroid Build Coastguard Worker     return std::string(text);
49*d57664e9SAndroid Build Coastguard Worker   }
Placeholder(StringPiece text)50*d57664e9SAndroid Build Coastguard Worker   std::string Placeholder(StringPiece text) override {
51*d57664e9SAndroid Build Coastguard Worker     return std::string(text);
52*d57664e9SAndroid Build Coastguard Worker   }
53*d57664e9SAndroid Build Coastguard Worker };
54*d57664e9SAndroid Build Coastguard Worker 
55*d57664e9SAndroid Build Coastguard Worker class PseudoMethodBidi : public PseudoMethodImpl {
56*d57664e9SAndroid Build Coastguard Worker  public:
57*d57664e9SAndroid Build Coastguard Worker   std::string Text(StringPiece text) override;
58*d57664e9SAndroid Build Coastguard Worker   std::string Placeholder(StringPiece text) override;
59*d57664e9SAndroid Build Coastguard Worker };
60*d57664e9SAndroid Build Coastguard Worker 
61*d57664e9SAndroid Build Coastguard Worker class PseudoMethodAccent : public PseudoMethodImpl {
62*d57664e9SAndroid Build Coastguard Worker  public:
PseudoMethodAccent()63*d57664e9SAndroid Build Coastguard Worker   PseudoMethodAccent() : depth_(0), word_count_(0), length_(0) {}
64*d57664e9SAndroid Build Coastguard Worker   std::string Start() override;
65*d57664e9SAndroid Build Coastguard Worker   std::string End() override;
66*d57664e9SAndroid Build Coastguard Worker   std::string Text(StringPiece text) override;
67*d57664e9SAndroid Build Coastguard Worker   std::string Placeholder(StringPiece text) override;
68*d57664e9SAndroid Build Coastguard Worker 
69*d57664e9SAndroid Build Coastguard Worker  private:
70*d57664e9SAndroid Build Coastguard Worker   size_t depth_;
71*d57664e9SAndroid Build Coastguard Worker   size_t word_count_;
72*d57664e9SAndroid Build Coastguard Worker   size_t length_;
73*d57664e9SAndroid Build Coastguard Worker };
74*d57664e9SAndroid Build Coastguard Worker 
Pseudolocalizer(Method method)75*d57664e9SAndroid Build Coastguard Worker Pseudolocalizer::Pseudolocalizer(Method method) : last_depth_(0) {
76*d57664e9SAndroid Build Coastguard Worker   SetMethod(method);
77*d57664e9SAndroid Build Coastguard Worker }
78*d57664e9SAndroid Build Coastguard Worker 
SetMethod(Method method)79*d57664e9SAndroid Build Coastguard Worker void Pseudolocalizer::SetMethod(Method method) {
80*d57664e9SAndroid Build Coastguard Worker   switch (method) {
81*d57664e9SAndroid Build Coastguard Worker     case Method::kNone:
82*d57664e9SAndroid Build Coastguard Worker       impl_ = util::make_unique<PseudoMethodNone>();
83*d57664e9SAndroid Build Coastguard Worker       break;
84*d57664e9SAndroid Build Coastguard Worker     case Method::kAccent:
85*d57664e9SAndroid Build Coastguard Worker       impl_ = util::make_unique<PseudoMethodAccent>();
86*d57664e9SAndroid Build Coastguard Worker       break;
87*d57664e9SAndroid Build Coastguard Worker     case Method::kBidi:
88*d57664e9SAndroid Build Coastguard Worker       impl_ = util::make_unique<PseudoMethodBidi>();
89*d57664e9SAndroid Build Coastguard Worker       break;
90*d57664e9SAndroid Build Coastguard Worker   }
91*d57664e9SAndroid Build Coastguard Worker }
92*d57664e9SAndroid Build Coastguard Worker 
Text(StringPiece text)93*d57664e9SAndroid Build Coastguard Worker std::string Pseudolocalizer::Text(StringPiece text) {
94*d57664e9SAndroid Build Coastguard Worker   std::string out;
95*d57664e9SAndroid Build Coastguard Worker   size_t depth = last_depth_;
96*d57664e9SAndroid Build Coastguard Worker   size_t lastpos, pos;
97*d57664e9SAndroid Build Coastguard Worker   const size_t length = text.size();
98*d57664e9SAndroid Build Coastguard Worker   const char* str = text.data();
99*d57664e9SAndroid Build Coastguard Worker   bool escaped = false;
100*d57664e9SAndroid Build Coastguard Worker   for (lastpos = pos = 0; pos < length; pos++) {
101*d57664e9SAndroid Build Coastguard Worker     char16_t c = str[pos];
102*d57664e9SAndroid Build Coastguard Worker     if (escaped) {
103*d57664e9SAndroid Build Coastguard Worker       escaped = false;
104*d57664e9SAndroid Build Coastguard Worker       continue;
105*d57664e9SAndroid Build Coastguard Worker     }
106*d57664e9SAndroid Build Coastguard Worker     if (c == '\'') {
107*d57664e9SAndroid Build Coastguard Worker       escaped = true;
108*d57664e9SAndroid Build Coastguard Worker       continue;
109*d57664e9SAndroid Build Coastguard Worker     }
110*d57664e9SAndroid Build Coastguard Worker 
111*d57664e9SAndroid Build Coastguard Worker     if (c == kArgStart) {
112*d57664e9SAndroid Build Coastguard Worker       depth++;
113*d57664e9SAndroid Build Coastguard Worker     } else if (c == kArgEnd && depth) {
114*d57664e9SAndroid Build Coastguard Worker       depth--;
115*d57664e9SAndroid Build Coastguard Worker     }
116*d57664e9SAndroid Build Coastguard Worker 
117*d57664e9SAndroid Build Coastguard Worker     if (last_depth_ != depth || pos == length - 1) {
118*d57664e9SAndroid Build Coastguard Worker       bool pseudo = ((last_depth_ % 2) == 0);
119*d57664e9SAndroid Build Coastguard Worker       size_t nextpos = pos;
120*d57664e9SAndroid Build Coastguard Worker       if (!pseudo || depth == last_depth_) {
121*d57664e9SAndroid Build Coastguard Worker         nextpos++;
122*d57664e9SAndroid Build Coastguard Worker       }
123*d57664e9SAndroid Build Coastguard Worker       size_t size = nextpos - lastpos;
124*d57664e9SAndroid Build Coastguard Worker       if (size) {
125*d57664e9SAndroid Build Coastguard Worker         std::string chunk(text.substr(lastpos, size));
126*d57664e9SAndroid Build Coastguard Worker         if (pseudo) {
127*d57664e9SAndroid Build Coastguard Worker           chunk = impl_->Text(chunk);
128*d57664e9SAndroid Build Coastguard Worker         } else if (str[lastpos] == kArgStart && str[nextpos - 1] == kArgEnd) {
129*d57664e9SAndroid Build Coastguard Worker           chunk = impl_->Placeholder(chunk);
130*d57664e9SAndroid Build Coastguard Worker         }
131*d57664e9SAndroid Build Coastguard Worker         out.append(chunk);
132*d57664e9SAndroid Build Coastguard Worker       }
133*d57664e9SAndroid Build Coastguard Worker       if (pseudo && depth < last_depth_) {  // End of message
134*d57664e9SAndroid Build Coastguard Worker         out.append(impl_->End());
135*d57664e9SAndroid Build Coastguard Worker       } else if (!pseudo && depth > last_depth_) {  // Start of message
136*d57664e9SAndroid Build Coastguard Worker         out.append(impl_->Start());
137*d57664e9SAndroid Build Coastguard Worker       }
138*d57664e9SAndroid Build Coastguard Worker       lastpos = nextpos;
139*d57664e9SAndroid Build Coastguard Worker       last_depth_ = depth;
140*d57664e9SAndroid Build Coastguard Worker     }
141*d57664e9SAndroid Build Coastguard Worker   }
142*d57664e9SAndroid Build Coastguard Worker   return out;
143*d57664e9SAndroid Build Coastguard Worker }
144*d57664e9SAndroid Build Coastguard Worker 
PseudolocalizeChar(const char c)145*d57664e9SAndroid Build Coastguard Worker static const char* PseudolocalizeChar(const char c) {
146*d57664e9SAndroid Build Coastguard Worker   switch (c) {
147*d57664e9SAndroid Build Coastguard Worker     case 'a':
148*d57664e9SAndroid Build Coastguard Worker       return "\u00e5";
149*d57664e9SAndroid Build Coastguard Worker     case 'b':
150*d57664e9SAndroid Build Coastguard Worker       return "\u0253";
151*d57664e9SAndroid Build Coastguard Worker     case 'c':
152*d57664e9SAndroid Build Coastguard Worker       return "\u00e7";
153*d57664e9SAndroid Build Coastguard Worker     case 'd':
154*d57664e9SAndroid Build Coastguard Worker       return "\u00f0";
155*d57664e9SAndroid Build Coastguard Worker     case 'e':
156*d57664e9SAndroid Build Coastguard Worker       return "\u00e9";
157*d57664e9SAndroid Build Coastguard Worker     case 'f':
158*d57664e9SAndroid Build Coastguard Worker       return "\u0192";
159*d57664e9SAndroid Build Coastguard Worker     case 'g':
160*d57664e9SAndroid Build Coastguard Worker       return "\u011d";
161*d57664e9SAndroid Build Coastguard Worker     case 'h':
162*d57664e9SAndroid Build Coastguard Worker       return "\u0125";
163*d57664e9SAndroid Build Coastguard Worker     case 'i':
164*d57664e9SAndroid Build Coastguard Worker       return "\u00ee";
165*d57664e9SAndroid Build Coastguard Worker     case 'j':
166*d57664e9SAndroid Build Coastguard Worker       return "\u0135";
167*d57664e9SAndroid Build Coastguard Worker     case 'k':
168*d57664e9SAndroid Build Coastguard Worker       return "\u0137";
169*d57664e9SAndroid Build Coastguard Worker     case 'l':
170*d57664e9SAndroid Build Coastguard Worker       return "\u013c";
171*d57664e9SAndroid Build Coastguard Worker     case 'm':
172*d57664e9SAndroid Build Coastguard Worker       return "\u1e3f";
173*d57664e9SAndroid Build Coastguard Worker     case 'n':
174*d57664e9SAndroid Build Coastguard Worker       return "\u00f1";
175*d57664e9SAndroid Build Coastguard Worker     case 'o':
176*d57664e9SAndroid Build Coastguard Worker       return "\u00f6";
177*d57664e9SAndroid Build Coastguard Worker     case 'p':
178*d57664e9SAndroid Build Coastguard Worker       return "\u00fe";
179*d57664e9SAndroid Build Coastguard Worker     case 'q':
180*d57664e9SAndroid Build Coastguard Worker       return "\u0051";
181*d57664e9SAndroid Build Coastguard Worker     case 'r':
182*d57664e9SAndroid Build Coastguard Worker       return "\u0155";
183*d57664e9SAndroid Build Coastguard Worker     case 's':
184*d57664e9SAndroid Build Coastguard Worker       return "\u0161";
185*d57664e9SAndroid Build Coastguard Worker     case 't':
186*d57664e9SAndroid Build Coastguard Worker       return "\u0163";
187*d57664e9SAndroid Build Coastguard Worker     case 'u':
188*d57664e9SAndroid Build Coastguard Worker       return "\u00fb";
189*d57664e9SAndroid Build Coastguard Worker     case 'v':
190*d57664e9SAndroid Build Coastguard Worker       return "\u0056";
191*d57664e9SAndroid Build Coastguard Worker     case 'w':
192*d57664e9SAndroid Build Coastguard Worker       return "\u0175";
193*d57664e9SAndroid Build Coastguard Worker     case 'x':
194*d57664e9SAndroid Build Coastguard Worker       return "\u0445";
195*d57664e9SAndroid Build Coastguard Worker     case 'y':
196*d57664e9SAndroid Build Coastguard Worker       return "\u00fd";
197*d57664e9SAndroid Build Coastguard Worker     case 'z':
198*d57664e9SAndroid Build Coastguard Worker       return "\u017e";
199*d57664e9SAndroid Build Coastguard Worker     case 'A':
200*d57664e9SAndroid Build Coastguard Worker       return "\u00c5";
201*d57664e9SAndroid Build Coastguard Worker     case 'B':
202*d57664e9SAndroid Build Coastguard Worker       return "\u03b2";
203*d57664e9SAndroid Build Coastguard Worker     case 'C':
204*d57664e9SAndroid Build Coastguard Worker       return "\u00c7";
205*d57664e9SAndroid Build Coastguard Worker     case 'D':
206*d57664e9SAndroid Build Coastguard Worker       return "\u00d0";
207*d57664e9SAndroid Build Coastguard Worker     case 'E':
208*d57664e9SAndroid Build Coastguard Worker       return "\u00c9";
209*d57664e9SAndroid Build Coastguard Worker     case 'G':
210*d57664e9SAndroid Build Coastguard Worker       return "\u011c";
211*d57664e9SAndroid Build Coastguard Worker     case 'H':
212*d57664e9SAndroid Build Coastguard Worker       return "\u0124";
213*d57664e9SAndroid Build Coastguard Worker     case 'I':
214*d57664e9SAndroid Build Coastguard Worker       return "\u00ce";
215*d57664e9SAndroid Build Coastguard Worker     case 'J':
216*d57664e9SAndroid Build Coastguard Worker       return "\u0134";
217*d57664e9SAndroid Build Coastguard Worker     case 'K':
218*d57664e9SAndroid Build Coastguard Worker       return "\u0136";
219*d57664e9SAndroid Build Coastguard Worker     case 'L':
220*d57664e9SAndroid Build Coastguard Worker       return "\u013b";
221*d57664e9SAndroid Build Coastguard Worker     case 'M':
222*d57664e9SAndroid Build Coastguard Worker       return "\u1e3e";
223*d57664e9SAndroid Build Coastguard Worker     case 'N':
224*d57664e9SAndroid Build Coastguard Worker       return "\u00d1";
225*d57664e9SAndroid Build Coastguard Worker     case 'O':
226*d57664e9SAndroid Build Coastguard Worker       return "\u00d6";
227*d57664e9SAndroid Build Coastguard Worker     case 'P':
228*d57664e9SAndroid Build Coastguard Worker       return "\u00de";
229*d57664e9SAndroid Build Coastguard Worker     case 'Q':
230*d57664e9SAndroid Build Coastguard Worker       return "\u0071";
231*d57664e9SAndroid Build Coastguard Worker     case 'R':
232*d57664e9SAndroid Build Coastguard Worker       return "\u0154";
233*d57664e9SAndroid Build Coastguard Worker     case 'S':
234*d57664e9SAndroid Build Coastguard Worker       return "\u0160";
235*d57664e9SAndroid Build Coastguard Worker     case 'T':
236*d57664e9SAndroid Build Coastguard Worker       return "\u0162";
237*d57664e9SAndroid Build Coastguard Worker     case 'U':
238*d57664e9SAndroid Build Coastguard Worker       return "\u00db";
239*d57664e9SAndroid Build Coastguard Worker     case 'V':
240*d57664e9SAndroid Build Coastguard Worker       return "\u03bd";
241*d57664e9SAndroid Build Coastguard Worker     case 'W':
242*d57664e9SAndroid Build Coastguard Worker       return "\u0174";
243*d57664e9SAndroid Build Coastguard Worker     case 'X':
244*d57664e9SAndroid Build Coastguard Worker       return "\u00d7";
245*d57664e9SAndroid Build Coastguard Worker     case 'Y':
246*d57664e9SAndroid Build Coastguard Worker       return "\u00dd";
247*d57664e9SAndroid Build Coastguard Worker     case 'Z':
248*d57664e9SAndroid Build Coastguard Worker       return "\u017d";
249*d57664e9SAndroid Build Coastguard Worker     case '!':
250*d57664e9SAndroid Build Coastguard Worker       return "\u00a1";
251*d57664e9SAndroid Build Coastguard Worker     case '?':
252*d57664e9SAndroid Build Coastguard Worker       return "\u00bf";
253*d57664e9SAndroid Build Coastguard Worker     case '$':
254*d57664e9SAndroid Build Coastguard Worker       return "\u20ac";
255*d57664e9SAndroid Build Coastguard Worker     default:
256*d57664e9SAndroid Build Coastguard Worker       return nullptr;
257*d57664e9SAndroid Build Coastguard Worker   }
258*d57664e9SAndroid Build Coastguard Worker }
259*d57664e9SAndroid Build Coastguard Worker 
IsPossibleNormalPlaceholderEnd(const char c)260*d57664e9SAndroid Build Coastguard Worker static bool IsPossibleNormalPlaceholderEnd(const char c) {
261*d57664e9SAndroid Build Coastguard Worker   switch (c) {
262*d57664e9SAndroid Build Coastguard Worker     case 's':
263*d57664e9SAndroid Build Coastguard Worker       return true;
264*d57664e9SAndroid Build Coastguard Worker     case 'S':
265*d57664e9SAndroid Build Coastguard Worker       return true;
266*d57664e9SAndroid Build Coastguard Worker     case 'c':
267*d57664e9SAndroid Build Coastguard Worker       return true;
268*d57664e9SAndroid Build Coastguard Worker     case 'C':
269*d57664e9SAndroid Build Coastguard Worker       return true;
270*d57664e9SAndroid Build Coastguard Worker     case 'd':
271*d57664e9SAndroid Build Coastguard Worker       return true;
272*d57664e9SAndroid Build Coastguard Worker     case 'o':
273*d57664e9SAndroid Build Coastguard Worker       return true;
274*d57664e9SAndroid Build Coastguard Worker     case 'x':
275*d57664e9SAndroid Build Coastguard Worker       return true;
276*d57664e9SAndroid Build Coastguard Worker     case 'X':
277*d57664e9SAndroid Build Coastguard Worker       return true;
278*d57664e9SAndroid Build Coastguard Worker     case 'f':
279*d57664e9SAndroid Build Coastguard Worker       return true;
280*d57664e9SAndroid Build Coastguard Worker     case 'e':
281*d57664e9SAndroid Build Coastguard Worker       return true;
282*d57664e9SAndroid Build Coastguard Worker     case 'E':
283*d57664e9SAndroid Build Coastguard Worker       return true;
284*d57664e9SAndroid Build Coastguard Worker     case 'g':
285*d57664e9SAndroid Build Coastguard Worker       return true;
286*d57664e9SAndroid Build Coastguard Worker     case 'G':
287*d57664e9SAndroid Build Coastguard Worker       return true;
288*d57664e9SAndroid Build Coastguard Worker     case 'a':
289*d57664e9SAndroid Build Coastguard Worker       return true;
290*d57664e9SAndroid Build Coastguard Worker     case 'A':
291*d57664e9SAndroid Build Coastguard Worker       return true;
292*d57664e9SAndroid Build Coastguard Worker     case 'b':
293*d57664e9SAndroid Build Coastguard Worker       return true;
294*d57664e9SAndroid Build Coastguard Worker     case 'B':
295*d57664e9SAndroid Build Coastguard Worker       return true;
296*d57664e9SAndroid Build Coastguard Worker     case 'h':
297*d57664e9SAndroid Build Coastguard Worker       return true;
298*d57664e9SAndroid Build Coastguard Worker     case 'H':
299*d57664e9SAndroid Build Coastguard Worker       return true;
300*d57664e9SAndroid Build Coastguard Worker     case '%':
301*d57664e9SAndroid Build Coastguard Worker       return true;
302*d57664e9SAndroid Build Coastguard Worker     case 'n':
303*d57664e9SAndroid Build Coastguard Worker       return true;
304*d57664e9SAndroid Build Coastguard Worker     default:
305*d57664e9SAndroid Build Coastguard Worker       return false;
306*d57664e9SAndroid Build Coastguard Worker   }
307*d57664e9SAndroid Build Coastguard Worker }
308*d57664e9SAndroid Build Coastguard Worker 
PseudoGenerateExpansion(const unsigned int length)309*d57664e9SAndroid Build Coastguard Worker static std::string PseudoGenerateExpansion(const unsigned int length) {
310*d57664e9SAndroid Build Coastguard Worker   std::string result(kExpansionString);
311*d57664e9SAndroid Build Coastguard Worker   if (result.size() < length) {
312*d57664e9SAndroid Build Coastguard Worker     result += " ";
313*d57664e9SAndroid Build Coastguard Worker     result += PseudoGenerateExpansion(length - result.size());
314*d57664e9SAndroid Build Coastguard Worker   } else {
315*d57664e9SAndroid Build Coastguard Worker     int ext = 0;
316*d57664e9SAndroid Build Coastguard Worker     // Should contain only whole words, so looking for a space
317*d57664e9SAndroid Build Coastguard Worker     {
318*d57664e9SAndroid Build Coastguard Worker       const char* const s = result.data();
319*d57664e9SAndroid Build Coastguard Worker       for (unsigned int i = length + 1; i < result.size(); ++i) {
320*d57664e9SAndroid Build Coastguard Worker         ++ext;
321*d57664e9SAndroid Build Coastguard Worker         if (s[i] == ' ') {
322*d57664e9SAndroid Build Coastguard Worker           break;
323*d57664e9SAndroid Build Coastguard Worker         }
324*d57664e9SAndroid Build Coastguard Worker       }
325*d57664e9SAndroid Build Coastguard Worker     }
326*d57664e9SAndroid Build Coastguard Worker     result.resize(length + ext);
327*d57664e9SAndroid Build Coastguard Worker   }
328*d57664e9SAndroid Build Coastguard Worker   return result;
329*d57664e9SAndroid Build Coastguard Worker }
330*d57664e9SAndroid Build Coastguard Worker 
Start()331*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodAccent::Start() {
332*d57664e9SAndroid Build Coastguard Worker   std::string result;
333*d57664e9SAndroid Build Coastguard Worker   if (depth_ == 0) {
334*d57664e9SAndroid Build Coastguard Worker     result = "[";
335*d57664e9SAndroid Build Coastguard Worker   }
336*d57664e9SAndroid Build Coastguard Worker   word_count_ = length_ = 0;
337*d57664e9SAndroid Build Coastguard Worker   depth_++;
338*d57664e9SAndroid Build Coastguard Worker   return result;
339*d57664e9SAndroid Build Coastguard Worker }
340*d57664e9SAndroid Build Coastguard Worker 
End()341*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodAccent::End() {
342*d57664e9SAndroid Build Coastguard Worker   std::string result;
343*d57664e9SAndroid Build Coastguard Worker   if (length_) {
344*d57664e9SAndroid Build Coastguard Worker     result += " ";
345*d57664e9SAndroid Build Coastguard Worker     result += PseudoGenerateExpansion(word_count_ > 3 ? length_ : length_ / 2);
346*d57664e9SAndroid Build Coastguard Worker   }
347*d57664e9SAndroid Build Coastguard Worker   word_count_ = length_ = 0;
348*d57664e9SAndroid Build Coastguard Worker   depth_--;
349*d57664e9SAndroid Build Coastguard Worker   if (depth_ == 0) {
350*d57664e9SAndroid Build Coastguard Worker     result += "]";
351*d57664e9SAndroid Build Coastguard Worker   }
352*d57664e9SAndroid Build Coastguard Worker   return result;
353*d57664e9SAndroid Build Coastguard Worker }
354*d57664e9SAndroid Build Coastguard Worker 
355*d57664e9SAndroid Build Coastguard Worker /**
356*d57664e9SAndroid Build Coastguard Worker  * Converts characters so they look like they've been localized.
357*d57664e9SAndroid Build Coastguard Worker  *
358*d57664e9SAndroid Build Coastguard Worker  * Note: This leaves placeholder syntax untouched.
359*d57664e9SAndroid Build Coastguard Worker  */
Text(StringPiece source)360*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodAccent::Text(StringPiece source) {
361*d57664e9SAndroid Build Coastguard Worker   const char* s = source.data();
362*d57664e9SAndroid Build Coastguard Worker   std::string result;
363*d57664e9SAndroid Build Coastguard Worker   const size_t I = source.size();
364*d57664e9SAndroid Build Coastguard Worker   bool lastspace = true;
365*d57664e9SAndroid Build Coastguard Worker   for (size_t i = 0; i < I; i++) {
366*d57664e9SAndroid Build Coastguard Worker     char c = s[i];
367*d57664e9SAndroid Build Coastguard Worker     if (c == '%') {
368*d57664e9SAndroid Build Coastguard Worker       // Placeholder syntax, no need to pseudolocalize
369*d57664e9SAndroid Build Coastguard Worker       std::string chunk;
370*d57664e9SAndroid Build Coastguard Worker       bool end = false;
371*d57664e9SAndroid Build Coastguard Worker       chunk.append(&c, 1);
372*d57664e9SAndroid Build Coastguard Worker       while (!end && i + 1 < I) {
373*d57664e9SAndroid Build Coastguard Worker         ++i;
374*d57664e9SAndroid Build Coastguard Worker         c = s[i];
375*d57664e9SAndroid Build Coastguard Worker         chunk.append(&c, 1);
376*d57664e9SAndroid Build Coastguard Worker         if (IsPossibleNormalPlaceholderEnd(c)) {
377*d57664e9SAndroid Build Coastguard Worker           end = true;
378*d57664e9SAndroid Build Coastguard Worker         } else if (i + 1 < I && c == 't') {
379*d57664e9SAndroid Build Coastguard Worker           ++i;
380*d57664e9SAndroid Build Coastguard Worker           c = s[i];
381*d57664e9SAndroid Build Coastguard Worker           chunk.append(&c, 1);
382*d57664e9SAndroid Build Coastguard Worker           end = true;
383*d57664e9SAndroid Build Coastguard Worker         }
384*d57664e9SAndroid Build Coastguard Worker       }
385*d57664e9SAndroid Build Coastguard Worker       // Treat chunk as a placeholder unless it ends with %.
386*d57664e9SAndroid Build Coastguard Worker       result += ((c == '%') ? chunk : Placeholder(chunk));
387*d57664e9SAndroid Build Coastguard Worker     } else if (c == '<' || c == '&') {
388*d57664e9SAndroid Build Coastguard Worker       // html syntax, no need to pseudolocalize
389*d57664e9SAndroid Build Coastguard Worker       bool tag_closed = false;
390*d57664e9SAndroid Build Coastguard Worker       while (!tag_closed && i < I) {
391*d57664e9SAndroid Build Coastguard Worker         if (c == '&') {
392*d57664e9SAndroid Build Coastguard Worker           std::string escape_text;
393*d57664e9SAndroid Build Coastguard Worker           escape_text.append(&c, 1);
394*d57664e9SAndroid Build Coastguard Worker           bool end = false;
395*d57664e9SAndroid Build Coastguard Worker           size_t html_code_pos = i;
396*d57664e9SAndroid Build Coastguard Worker           while (!end && html_code_pos < I) {
397*d57664e9SAndroid Build Coastguard Worker             ++html_code_pos;
398*d57664e9SAndroid Build Coastguard Worker             c = s[html_code_pos];
399*d57664e9SAndroid Build Coastguard Worker             escape_text.append(&c, 1);
400*d57664e9SAndroid Build Coastguard Worker             // Valid html code
401*d57664e9SAndroid Build Coastguard Worker             if (c == ';') {
402*d57664e9SAndroid Build Coastguard Worker               end = true;
403*d57664e9SAndroid Build Coastguard Worker               i = html_code_pos;
404*d57664e9SAndroid Build Coastguard Worker             }
405*d57664e9SAndroid Build Coastguard Worker             // Wrong html code
406*d57664e9SAndroid Build Coastguard Worker             else if (!((c == '#' || (c >= 'a' && c <= 'z') ||
407*d57664e9SAndroid Build Coastguard Worker                         (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) {
408*d57664e9SAndroid Build Coastguard Worker               end = true;
409*d57664e9SAndroid Build Coastguard Worker             }
410*d57664e9SAndroid Build Coastguard Worker           }
411*d57664e9SAndroid Build Coastguard Worker           result += escape_text;
412*d57664e9SAndroid Build Coastguard Worker           if (escape_text != "&lt;") {
413*d57664e9SAndroid Build Coastguard Worker             tag_closed = true;
414*d57664e9SAndroid Build Coastguard Worker           }
415*d57664e9SAndroid Build Coastguard Worker           continue;
416*d57664e9SAndroid Build Coastguard Worker         }
417*d57664e9SAndroid Build Coastguard Worker         if (c == '>') {
418*d57664e9SAndroid Build Coastguard Worker           tag_closed = true;
419*d57664e9SAndroid Build Coastguard Worker           result.append(&c, 1);
420*d57664e9SAndroid Build Coastguard Worker           continue;
421*d57664e9SAndroid Build Coastguard Worker         }
422*d57664e9SAndroid Build Coastguard Worker         result.append(&c, 1);
423*d57664e9SAndroid Build Coastguard Worker         i++;
424*d57664e9SAndroid Build Coastguard Worker         c = s[i];
425*d57664e9SAndroid Build Coastguard Worker       }
426*d57664e9SAndroid Build Coastguard Worker     } else {
427*d57664e9SAndroid Build Coastguard Worker       // This is a pure text that should be pseudolocalized
428*d57664e9SAndroid Build Coastguard Worker       const char* p = PseudolocalizeChar(c);
429*d57664e9SAndroid Build Coastguard Worker       if (p != nullptr) {
430*d57664e9SAndroid Build Coastguard Worker         result += p;
431*d57664e9SAndroid Build Coastguard Worker       } else {
432*d57664e9SAndroid Build Coastguard Worker         bool space = isspace(c);
433*d57664e9SAndroid Build Coastguard Worker         if (lastspace && !space) {
434*d57664e9SAndroid Build Coastguard Worker           word_count_++;
435*d57664e9SAndroid Build Coastguard Worker         }
436*d57664e9SAndroid Build Coastguard Worker         lastspace = space;
437*d57664e9SAndroid Build Coastguard Worker         result.append(&c, 1);
438*d57664e9SAndroid Build Coastguard Worker       }
439*d57664e9SAndroid Build Coastguard Worker       // Count only pseudolocalizable chars and delimiters
440*d57664e9SAndroid Build Coastguard Worker       length_++;
441*d57664e9SAndroid Build Coastguard Worker     }
442*d57664e9SAndroid Build Coastguard Worker   }
443*d57664e9SAndroid Build Coastguard Worker   return result;
444*d57664e9SAndroid Build Coastguard Worker }
445*d57664e9SAndroid Build Coastguard Worker 
Placeholder(StringPiece source)446*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodAccent::Placeholder(StringPiece source) {
447*d57664e9SAndroid Build Coastguard Worker   // Surround a placeholder with brackets
448*d57664e9SAndroid Build Coastguard Worker   return (std::string(kPlaceholderOpen) += source) += kPlaceholderClose;
449*d57664e9SAndroid Build Coastguard Worker }
450*d57664e9SAndroid Build Coastguard Worker 
Text(StringPiece source)451*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodBidi::Text(StringPiece source) {
452*d57664e9SAndroid Build Coastguard Worker   const char* s = source.data();
453*d57664e9SAndroid Build Coastguard Worker   std::string result;
454*d57664e9SAndroid Build Coastguard Worker   bool lastspace = true;
455*d57664e9SAndroid Build Coastguard Worker   bool space = true;
456*d57664e9SAndroid Build Coastguard Worker   bool escape = false;
457*d57664e9SAndroid Build Coastguard Worker   const char ESCAPE_CHAR = '\\';
458*d57664e9SAndroid Build Coastguard Worker   for (size_t i = 0; i < source.size(); i++) {
459*d57664e9SAndroid Build Coastguard Worker     char c = s[i];
460*d57664e9SAndroid Build Coastguard Worker     if (!escape && c == ESCAPE_CHAR) {
461*d57664e9SAndroid Build Coastguard Worker       escape = true;
462*d57664e9SAndroid Build Coastguard Worker       continue;
463*d57664e9SAndroid Build Coastguard Worker     }
464*d57664e9SAndroid Build Coastguard Worker     space = (!escape && isspace(c)) || (escape && (c == 'n' || c == 't'));
465*d57664e9SAndroid Build Coastguard Worker     if (lastspace && !space) {
466*d57664e9SAndroid Build Coastguard Worker       // Word start
467*d57664e9SAndroid Build Coastguard Worker       (result += kRlm) += kRlo;
468*d57664e9SAndroid Build Coastguard Worker     } else if (!lastspace && space) {
469*d57664e9SAndroid Build Coastguard Worker       // Word end
470*d57664e9SAndroid Build Coastguard Worker       (result += kPdf) += kRlm;
471*d57664e9SAndroid Build Coastguard Worker     }
472*d57664e9SAndroid Build Coastguard Worker     lastspace = space;
473*d57664e9SAndroid Build Coastguard Worker     if (escape) {
474*d57664e9SAndroid Build Coastguard Worker       result.append(&ESCAPE_CHAR, 1);
475*d57664e9SAndroid Build Coastguard Worker       escape=false;
476*d57664e9SAndroid Build Coastguard Worker     }
477*d57664e9SAndroid Build Coastguard Worker     result.append(&c, 1);
478*d57664e9SAndroid Build Coastguard Worker   }
479*d57664e9SAndroid Build Coastguard Worker   if (!lastspace) {
480*d57664e9SAndroid Build Coastguard Worker     // End of last word
481*d57664e9SAndroid Build Coastguard Worker     (result += kPdf) += kRlm;
482*d57664e9SAndroid Build Coastguard Worker   }
483*d57664e9SAndroid Build Coastguard Worker   return result;
484*d57664e9SAndroid Build Coastguard Worker }
485*d57664e9SAndroid Build Coastguard Worker 
Placeholder(StringPiece source)486*d57664e9SAndroid Build Coastguard Worker std::string PseudoMethodBidi::Placeholder(StringPiece source) {
487*d57664e9SAndroid Build Coastguard Worker   // Surround a placeholder with directionality change sequence
488*d57664e9SAndroid Build Coastguard Worker   return (((std::string(kRlm) += kRlo) += source) += kPdf) += kRlm;
489*d57664e9SAndroid Build Coastguard Worker }
490*d57664e9SAndroid Build Coastguard Worker 
491*d57664e9SAndroid Build Coastguard Worker }  // namespace aapt
492