1393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski/*
2393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * Copyright (C) 2015 The Android Open Source Project
3393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski *
4393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * you may not use this file except in compliance with the License.
6393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * You may obtain a copy of the License at
7393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski *
8393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski *
10393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * See the License for the specific language governing permissions and
14393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * limitations under the License.
15393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski */
16393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
17393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski#include "compile/Pseudolocalizer.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski#include "util/Util.h"
20393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
21d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinskiusing android::StringPiece;
22d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
23393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinskinamespace aapt {
24393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
25393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski// String basis to generate expansion
26ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kExpansionString =
27cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    "one two three "
28cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    "four five six seven eight nine ten eleven twelve thirteen "
29cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    "fourteen fiveteen sixteen seventeen nineteen twenty";
30393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
31393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski// Special unicode characters to override directionality of the words
32ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kRlm = "\u200f";
33ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kRlo = "\u202e";
34ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kPdf = "\u202c";
35393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
36393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski// Placeholder marks
37ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kPlaceholderOpen = "\u00bb";
38ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const std::string kPlaceholderClose = "\u00ab";
39393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
40ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const char kArgStart = '{';
41ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const char kArgEnd = '}';
42393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
43393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinskiclass PseudoMethodNone : public PseudoMethodImpl {
44cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
45d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  std::string Text(const StringPiece& text) override { return text.to_string(); }
46d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  std::string Placeholder(const StringPiece& text) override { return text.to_string(); }
47393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski};
48393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
49393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinskiclass PseudoMethodBidi : public PseudoMethodImpl {
50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
51ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string Text(const StringPiece& text) override;
52ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string Placeholder(const StringPiece& text) override;
53393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski};
54393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
55393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinskiclass PseudoMethodAccent : public PseudoMethodImpl {
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
57ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  PseudoMethodAccent() : depth_(0), word_count_(0), length_(0) {}
58ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string Start() override;
59ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string End() override;
60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string Text(const StringPiece& text) override;
61ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string Placeholder(const StringPiece& text) override;
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
63cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
64ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t depth_;
65ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t word_count_;
66ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t length_;
67393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski};
68393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
69ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiPseudolocalizer::Pseudolocalizer(Method method) : last_depth_(0) {
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  SetMethod(method);
71393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
72393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
73ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskivoid Pseudolocalizer::SetMethod(Method method) {
74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  switch (method) {
75393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    case Method::kNone:
76ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      impl_ = util::make_unique<PseudoMethodNone>();
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      break;
78393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    case Method::kAccent:
79ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      impl_ = util::make_unique<PseudoMethodAccent>();
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      break;
81393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    case Method::kBidi:
82ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      impl_ = util::make_unique<PseudoMethodBidi>();
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      break;
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
85393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
86393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
87ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string Pseudolocalizer::Text(const StringPiece& text) {
88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string out;
89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t depth = last_depth_;
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  size_t lastpos, pos;
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t length = text.size();
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const char* str = text.data();
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool escaped = false;
94cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (lastpos = pos = 0; pos < length; pos++) {
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    char16_t c = str[pos];
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (escaped) {
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      escaped = false;
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
99cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (c == '\'') {
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      escaped = true;
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
103cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
104393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (c == kArgStart) {
106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      depth++;
107ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (c == kArgEnd && depth) {
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      depth--;
109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
110393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (last_depth_ != depth || pos == length - 1) {
112ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      bool pseudo = ((last_depth_ % 2) == 0);
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      size_t nextpos = pos;
114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!pseudo || depth == last_depth_) {
115cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        nextpos++;
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      size_t size = nextpos - lastpos;
118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (size) {
119d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski        std::string chunk = text.substr(lastpos, size).to_string();
120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (pseudo) {
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          chunk = impl_->Text(chunk);
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        } else if (str[lastpos] == kArgStart && str[nextpos - 1] == kArgEnd) {
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          chunk = impl_->Placeholder(chunk);
124393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        }
125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        out.append(chunk);
126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
127ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (pseudo && depth < last_depth_) {  // End of message
128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        out.append(impl_->End());
129ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (!pseudo && depth > last_depth_) {  // Start of message
130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        out.append(impl_->Start());
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      lastpos = nextpos;
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      last_depth_ = depth;
134393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return out;
137393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
138393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
139ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic const char* PseudolocalizeChar(const char c) {
140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  switch (c) {
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'a':
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00e5";
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'b':
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0253";
145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'c':
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00e7";
147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'd':
148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00f0";
149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'e':
150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00e9";
151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'f':
152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0192";
153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'g':
154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u011d";
155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'h':
156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0125";
157cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'i':
158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00ee";
159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'j':
160cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0135";
161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'k':
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0137";
163cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'l':
164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u013c";
165cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'm':
166cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u1e3f";
167cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'n':
168cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00f1";
169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'o':
170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00f6";
171cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'p':
172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00fe";
173cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'q':
174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0051";
175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'r':
176cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0155";
177cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 's':
178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0161";
179cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 't':
180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0163";
181cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'u':
182cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00fb";
183cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'v':
184cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0056";
185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'w':
186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0175";
187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'x':
188cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0445";
189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'y':
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00fd";
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'z':
192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u017e";
193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'A':
194cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00c5";
195cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'B':
196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u03b2";
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'C':
198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00c7";
199cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'D':
200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00d0";
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'E':
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00c9";
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'G':
204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u011c";
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'H':
206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0124";
207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'I':
208cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00ce";
209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'J':
210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0134";
211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'K':
212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0136";
213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'L':
214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u013b";
215cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'M':
216cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u1e3e";
217cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'N':
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00d1";
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'O':
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00d6";
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'P':
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00de";
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'Q':
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0071";
225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'R':
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0154";
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'S':
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0160";
229cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'T':
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0162";
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'U':
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00db";
233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'V':
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u03bd";
235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'W':
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u0174";
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'X':
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00d7";
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'Y':
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00dd";
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'Z':
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u017d";
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case '!':
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00a1";
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case '?':
246cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u00bf";
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case '$':
248cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return "\u20ac";
249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    default:
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return nullptr;
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
252393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
253393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
254ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool IsPossibleNormalPlaceholderEnd(const char c) {
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  switch (c) {
256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 's':
257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'S':
259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
260cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'c':
261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'C':
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'd':
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'o':
267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'x':
269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'X':
271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'f':
273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'e':
275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'E':
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'g':
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'G':
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'a':
283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'A':
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'b':
287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'B':
289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
290cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'h':
291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'H':
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case '%':
295cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
296cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    case 'n':
297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return true;
298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    default:
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
301393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
302393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
303ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic std::string PseudoGenerateExpansion(const unsigned int length) {
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string result = kExpansionString;
305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const char* s = result.data();
306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (result.size() < length) {
307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result += " ";
308ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    result += PseudoGenerateExpansion(length - result.size());
309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    int ext = 0;
311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Should contain only whole words, so looking for a space
312cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    for (unsigned int i = length + 1; i < result.size(); ++i) {
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      ++ext;
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (s[i] == ' ') {
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
317393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result = result.substr(0, length + ext);
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
320cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
321393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
322393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
323ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodAccent::Start() {
324cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string result;
325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (depth_ == 0) {
326cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result = "[";
327cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
328ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  word_count_ = length_ = 0;
329ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  depth_++;
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
331393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
332393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodAccent::End() {
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string result;
335ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (length_) {
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result += " ";
337ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    result += PseudoGenerateExpansion(word_count_ > 3 ? length_ : length_ / 2);
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
339ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  word_count_ = length_ = 0;
340ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  depth_--;
341ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (depth_ == 0) {
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result += "]";
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
345393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
346393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
347393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski/**
348393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * Converts characters so they look like they've been localized.
349393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski *
350393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski * Note: This leaves placeholder syntax untouched.
351393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski */
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodAccent::Text(const StringPiece& source) {
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const char* s = source.data();
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string result;
355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t I = source.size();
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool lastspace = true;
357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (size_t i = 0; i < I; i++) {
358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    char c = s[i];
359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (c == '%') {
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Placeholder syntax, no need to pseudolocalize
361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::string chunk;
362cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      bool end = false;
363cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      chunk.append(&c, 1);
364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      while (!end && i + 1 < I) {
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        ++i;
366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        c = s[i];
367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        chunk.append(&c, 1);
368ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (IsPossibleNormalPlaceholderEnd(c)) {
369cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          end = true;
370cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else if (i + 1 < I && c == 't') {
371cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          ++i;
372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          c = s[i];
373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          chunk.append(&c, 1);
374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          end = true;
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
377cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Treat chunk as a placeholder unless it ends with %.
378ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      result += ((c == '%') ? chunk : Placeholder(chunk));
379cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else if (c == '<' || c == '&') {
380cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // html syntax, no need to pseudolocalize
381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      bool tag_closed = false;
382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      while (!tag_closed && i < I) {
383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (c == '&') {
384ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          std::string escape_text;
385ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          escape_text.append(&c, 1);
386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          bool end = false;
387ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          size_t html_code_pos = i;
388ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          while (!end && html_code_pos < I) {
389ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            ++html_code_pos;
390ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            c = s[html_code_pos];
391ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            escape_text.append(&c, 1);
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // Valid html code
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            if (c == ';') {
394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              end = true;
395ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              i = html_code_pos;
396393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski            }
397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // Wrong html code
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            else if (!((c == '#' || (c >= 'a' && c <= 'z') ||
399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                        (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) {
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              end = true;
401393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski            }
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
403ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          result += escape_text;
404ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (escape_text != "&lt;") {
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            tag_closed = true;
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          continue;
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (c == '>') {
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          tag_closed = true;
411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          result.append(&c, 1);
412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          continue;
413cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        result.append(&c, 1);
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        i++;
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        c = s[i];
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
419cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // This is a pure text that should be pseudolocalized
420ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const char* p = PseudolocalizeChar(c);
421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (p != nullptr) {
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        result += p;
423cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        bool space = isspace(c);
425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (lastspace && !space) {
426ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          word_count_++;
427393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        }
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        lastspace = space;
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        result.append(&c, 1);
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Count only pseudolocalizable chars and delimiters
432ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      length_++;
433393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
435cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
436393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
437393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
438ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodAccent::Placeholder(const StringPiece& source) {
439cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Surround a placeholder with brackets
440d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  return kPlaceholderOpen + source.to_string() + kPlaceholderClose;
441393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
442393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodBidi::Text(const StringPiece& source) {
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const char* s = source.data();
445cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string result;
446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool lastspace = true;
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool space = true;
448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (size_t i = 0; i < source.size(); i++) {
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    char c = s[i];
450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    space = isspace(c);
451cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (lastspace && !space) {
452cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Word start
453ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      result += kRlm + kRlo;
454cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else if (!lastspace && space) {
455cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Word end
456ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      result += kPdf + kRlm;
457393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    lastspace = space;
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    result.append(&c, 1);
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
461cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!lastspace) {
462cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // End of last word
463ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    result += kPdf + kRlm;
464cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
466393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
467393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
468ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::string PseudoMethodBidi::Placeholder(const StringPiece& source) {
469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Surround a placeholder with directionality change sequence
470d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  return kRlm + kRlo + source.to_string() + kPdf + kRlm;
471393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski}
472393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
473cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
474