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