PseudolocaleGenerator.cpp revision d5083f6f6b9bc76bbe64052bcec639eee752a321
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "compile/PseudolocaleGenerator.h" 18 19#include <algorithm> 20 21#include "ResourceTable.h" 22#include "ResourceValues.h" 23#include "ValueVisitor.h" 24#include "compile/Pseudolocalizer.h" 25 26using android::StringPiece; 27 28namespace aapt { 29 30std::unique_ptr<StyledString> PseudolocalizeStyledString( 31 StyledString* string, Pseudolocalizer::Method method, StringPool* pool) { 32 Pseudolocalizer localizer(method); 33 34 const StringPiece original_text = *string->value->str; 35 36 StyleString localized; 37 38 // Copy the spans. We will update their offsets when we localize. 39 localized.spans.reserve(string->value->spans.size()); 40 for (const StringPool::Span& span : string->value->spans) { 41 localized.spans.push_back( 42 Span{*span.name, span.first_char, span.last_char}); 43 } 44 45 // The ranges are all represented with a single value. This is the start of 46 // one range and 47 // end of another. 48 struct Range { 49 size_t start; 50 51 // Once the new string is localized, these are the pointers to the spans to 52 // adjust. 53 // Since this struct represents the start of one range and end of another, 54 // we have 55 // the two pointers respectively. 56 uint32_t* update_start; 57 uint32_t* update_end; 58 }; 59 60 auto cmp = [](const Range& r, size_t index) -> bool { 61 return r.start < index; 62 }; 63 64 // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7] 65 // The ranges are the spaces in between. In this example, with a total string 66 // length of 9, 67 // the vector represents: (0,1], (2,4], (5,6], (7,9] 68 // 69 std::vector<Range> ranges; 70 ranges.push_back(Range{0}); 71 ranges.push_back(Range{original_text.size() - 1}); 72 for (size_t i = 0; i < string->value->spans.size(); i++) { 73 const StringPool::Span& span = string->value->spans[i]; 74 75 // Insert or update the Range marker for the start of this span. 76 auto iter = 77 std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp); 78 if (iter != ranges.end() && iter->start == span.first_char) { 79 iter->update_start = &localized.spans[i].first_char; 80 } else { 81 ranges.insert(iter, Range{span.first_char, &localized.spans[i].first_char, 82 nullptr}); 83 } 84 85 // Insert or update the Range marker for the end of this span. 86 iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp); 87 if (iter != ranges.end() && iter->start == span.last_char) { 88 iter->update_end = &localized.spans[i].last_char; 89 } else { 90 ranges.insert( 91 iter, Range{span.last_char, nullptr, &localized.spans[i].last_char}); 92 } 93 } 94 95 localized.str += localizer.Start(); 96 97 // Iterate over the ranges and localize each section. 98 for (size_t i = 0; i < ranges.size(); i++) { 99 const size_t start = ranges[i].start; 100 size_t len = original_text.size() - start; 101 if (i + 1 < ranges.size()) { 102 len = ranges[i + 1].start - start; 103 } 104 105 if (ranges[i].update_start) { 106 *ranges[i].update_start = localized.str.size(); 107 } 108 109 if (ranges[i].update_end) { 110 *ranges[i].update_end = localized.str.size(); 111 } 112 113 localized.str += localizer.Text(original_text.substr(start, len)); 114 } 115 116 localized.str += localizer.End(); 117 118 std::unique_ptr<StyledString> localized_string = 119 util::make_unique<StyledString>(pool->MakeRef(localized)); 120 localized_string->SetSource(string->GetSource()); 121 return localized_string; 122} 123 124namespace { 125 126class Visitor : public RawValueVisitor { 127 public: 128 // Either value or item will be populated upon visiting the value. 129 std::unique_ptr<Value> value; 130 std::unique_ptr<Item> item; 131 132 Visitor(StringPool* pool, Pseudolocalizer::Method method) 133 : pool_(pool), method_(method), localizer_(method) {} 134 135 void Visit(Plural* plural) override { 136 std::unique_ptr<Plural> localized = util::make_unique<Plural>(); 137 for (size_t i = 0; i < plural->values.size(); i++) { 138 Visitor sub_visitor(pool_, method_); 139 if (plural->values[i]) { 140 plural->values[i]->Accept(&sub_visitor); 141 if (sub_visitor.value) { 142 localized->values[i] = std::move(sub_visitor.item); 143 } else { 144 localized->values[i] = 145 std::unique_ptr<Item>(plural->values[i]->Clone(pool_)); 146 } 147 } 148 } 149 localized->SetSource(plural->GetSource()); 150 localized->SetWeak(true); 151 value = std::move(localized); 152 } 153 154 void Visit(String* string) override { 155 std::string result = 156 localizer_.Start() + localizer_.Text(*string->value) + localizer_.End(); 157 std::unique_ptr<String> localized = 158 util::make_unique<String>(pool_->MakeRef(result)); 159 localized->SetSource(string->GetSource()); 160 localized->SetWeak(true); 161 item = std::move(localized); 162 } 163 164 void Visit(StyledString* string) override { 165 item = PseudolocalizeStyledString(string, method_, pool_); 166 item->SetWeak(true); 167 } 168 169 private: 170 DISALLOW_COPY_AND_ASSIGN(Visitor); 171 172 StringPool* pool_; 173 Pseudolocalizer::Method method_; 174 Pseudolocalizer localizer_; 175}; 176 177ConfigDescription ModifyConfigForPseudoLocale(const ConfigDescription& base, 178 Pseudolocalizer::Method m) { 179 ConfigDescription modified = base; 180 switch (m) { 181 case Pseudolocalizer::Method::kAccent: 182 modified.language[0] = 'e'; 183 modified.language[1] = 'n'; 184 modified.country[0] = 'X'; 185 modified.country[1] = 'A'; 186 break; 187 188 case Pseudolocalizer::Method::kBidi: 189 modified.language[0] = 'a'; 190 modified.language[1] = 'r'; 191 modified.country[0] = 'X'; 192 modified.country[1] = 'B'; 193 break; 194 default: 195 break; 196 } 197 return modified; 198} 199 200void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method, 201 ResourceConfigValue* original_value, 202 StringPool* pool, ResourceEntry* entry) { 203 Visitor visitor(pool, method); 204 original_value->value->Accept(&visitor); 205 206 std::unique_ptr<Value> localized_value; 207 if (visitor.value) { 208 localized_value = std::move(visitor.value); 209 } else if (visitor.item) { 210 localized_value = std::move(visitor.item); 211 } 212 213 if (!localized_value) { 214 return; 215 } 216 217 ConfigDescription config_with_accent = 218 ModifyConfigForPseudoLocale(original_value->config, method); 219 220 ResourceConfigValue* new_config_value = 221 entry->FindOrCreateValue(config_with_accent, original_value->product); 222 if (!new_config_value->value) { 223 // Only use auto-generated pseudo-localization if none is defined. 224 new_config_value->value = std::move(localized_value); 225 } 226} 227 228/** 229 * A value is pseudolocalizable if it does not define a locale (or is the 230 * default locale) 231 * and is translateable. 232 */ 233static bool IsPseudolocalizable(ResourceConfigValue* config_value) { 234 const int diff = 235 config_value->config.diff(ConfigDescription::DefaultConfig()); 236 if (diff & ConfigDescription::CONFIG_LOCALE) { 237 return false; 238 } 239 return config_value->value->IsTranslateable(); 240} 241 242} // namespace 243 244bool PseudolocaleGenerator::Consume(IAaptContext* context, 245 ResourceTable* table) { 246 for (auto& package : table->packages) { 247 for (auto& type : package->types) { 248 for (auto& entry : type->entries) { 249 std::vector<ResourceConfigValue*> values = 250 entry->FindValuesIf(IsPseudolocalizable); 251 252 for (ResourceConfigValue* value : values) { 253 PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, 254 &table->string_pool, entry.get()); 255 PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, 256 &table->string_pool, entry.get()); 257 } 258 } 259 } 260 } 261 return true; 262} 263 264} // namespace aapt 265