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