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 != "<") {
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