Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 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 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 17 | #include "compile/PseudolocaleGenerator.h" |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 18 | |
| 19 | #include <algorithm> |
| 20 | |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 21 | #include "ResourceTable.h" |
| 22 | #include "ResourceValues.h" |
| 23 | #include "ValueVisitor.h" |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 24 | #include "compile/Pseudolocalizer.h" |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 25 | |
Adam Lesinski | d5083f6 | 2017-01-16 15:07:21 -0800 | [diff] [blame] | 26 | using android::StringPiece; |
| 27 | |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 28 | namespace aapt { |
| 29 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 30 | std::unique_ptr<StyledString> PseudolocalizeStyledString( |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 31 | StyledString* string, Pseudolocalizer::Method method, StringPool* pool) { |
| 32 | Pseudolocalizer localizer(method); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 33 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 34 | const StringPiece original_text = *string->value->str; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 35 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 36 | StyleString localized; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 37 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 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) { |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 41 | localized.spans.push_back( |
| 42 | Span{*span.name, span.first_char, span.last_char}); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | // The ranges are all represented with a single value. This is the start of |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 46 | // one range and end of another. |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 47 | struct Range { |
| 48 | size_t start; |
| 49 | |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 50 | // If set to true, toggles the state of translatability. |
| 51 | bool toggle_translatability; |
| 52 | |
| 53 | // Once the new string is localized, these are the pointers to the spans to adjust. |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 54 | // Since this struct represents the start of one range and end of another, |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 55 | // we have the two pointers respectively. |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 56 | uint32_t* update_start; |
| 57 | uint32_t* update_end; |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 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 |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 66 | // length of 9, the vector represents: (0,1], (2,4], (5,6], (7,9] |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 67 | // |
| 68 | std::vector<Range> ranges; |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 69 | ranges.push_back(Range{0, false, nullptr, nullptr}); |
| 70 | ranges.push_back(Range{original_text.size() - 1, false, nullptr, nullptr}); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 71 | for (size_t i = 0; i < string->value->spans.size(); i++) { |
| 72 | const StringPool::Span& span = string->value->spans[i]; |
| 73 | |
| 74 | // Insert or update the Range marker for the start of this span. |
| 75 | auto iter = |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 76 | std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp); |
| 77 | if (iter != ranges.end() && iter->start == span.first_char) { |
| 78 | iter->update_start = &localized.spans[i].first_char; |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 79 | } else { |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 80 | ranges.insert(iter, Range{span.first_char, false, &localized.spans[i].first_char, nullptr}); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 81 | } |
| 82 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 83 | // Insert or update the Range marker for the end of this span. |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 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; |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 87 | } else { |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 88 | ranges.insert(iter, Range{span.last_char, false, nullptr, &localized.spans[i].last_char}); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | // Parts of the string may be untranslatable. Merge those ranges |
| 93 | // in as well, so that we have continuous sections of text to |
| 94 | // feed into the pseudolocalizer. |
| 95 | // We do this by marking the beginning of a range as either toggling |
| 96 | // the translatability state or not. |
| 97 | for (const UntranslatableSection& section : string->untranslatable_sections) { |
| 98 | auto iter = std::lower_bound(ranges.begin(), ranges.end(), section.start, cmp); |
| 99 | if (iter != ranges.end() && iter->start == section.start) { |
| 100 | // An existing span starts (or ends) here. We just need to mark that |
| 101 | // the translatability should toggle here. If translatability was |
| 102 | // already being toggled, then that means we have two adjacent ranges of untranslatable |
| 103 | // text, so remove the toggle and only toggle at the end of this range, |
| 104 | // effectively merging these ranges. |
| 105 | iter->toggle_translatability = !iter->toggle_translatability; |
| 106 | } else { |
| 107 | // Insert a new range that specifies to toggle the translatability. |
| 108 | iter = ranges.insert(iter, Range{section.start, true, nullptr, nullptr}); |
| 109 | } |
| 110 | |
| 111 | // Update/create an end to the untranslatable section. |
| 112 | iter = std::lower_bound(iter, ranges.end(), section.end, cmp); |
| 113 | if (iter != ranges.end() && iter->start == section.end) { |
| 114 | iter->toggle_translatability = true; |
| 115 | } else { |
| 116 | iter = ranges.insert(iter, Range{section.end, true, nullptr, nullptr}); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 117 | } |
| 118 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 119 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 120 | localized.str += localizer.Start(); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 121 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 122 | // Iterate over the ranges and localize each section. |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 123 | // The text starts as translatable, and each time a range has toggle_translatability |
| 124 | // set to true, we toggle whether to translate or not. |
| 125 | // This assumes no untranslatable ranges overlap. |
| 126 | bool translatable = true; |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 127 | for (size_t i = 0; i < ranges.size(); i++) { |
| 128 | const size_t start = ranges[i].start; |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 129 | size_t len = original_text.size() - start; |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 130 | if (i + 1 < ranges.size()) { |
| 131 | len = ranges[i + 1].start - start; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 132 | } |
| 133 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 134 | if (ranges[i].update_start) { |
| 135 | *ranges[i].update_start = localized.str.size(); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 136 | } |
| 137 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 138 | if (ranges[i].update_end) { |
| 139 | *ranges[i].update_end = localized.str.size(); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 140 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 141 | |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 142 | if (ranges[i].toggle_translatability) { |
| 143 | translatable = !translatable; |
| 144 | } |
| 145 | |
| 146 | if (translatable) { |
| 147 | localized.str += localizer.Text(original_text.substr(start, len)); |
| 148 | } else { |
| 149 | localized.str += original_text.substr(start, len); |
| 150 | } |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 151 | } |
| 152 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 153 | localized.str += localizer.End(); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 154 | |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 155 | return util::make_unique<StyledString>(pool->MakeRef(localized)); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | namespace { |
| 159 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 160 | class Visitor : public RawValueVisitor { |
| 161 | public: |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 162 | // Either value or item will be populated upon visiting the value. |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 163 | std::unique_ptr<Value> value; |
| 164 | std::unique_ptr<Item> item; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 165 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 166 | Visitor(StringPool* pool, Pseudolocalizer::Method method) |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 167 | : pool_(pool), method_(method), localizer_(method) {} |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 168 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 169 | void Visit(Plural* plural) override { |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 170 | std::unique_ptr<Plural> localized = util::make_unique<Plural>(); |
| 171 | for (size_t i = 0; i < plural->values.size(); i++) { |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 172 | Visitor sub_visitor(pool_, method_); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 173 | if (plural->values[i]) { |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 174 | plural->values[i]->Accept(&sub_visitor); |
| 175 | if (sub_visitor.value) { |
| 176 | localized->values[i] = std::move(sub_visitor.item); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 177 | } else { |
| 178 | localized->values[i] = |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 179 | std::unique_ptr<Item>(plural->values[i]->Clone(pool_)); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 180 | } |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 181 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 182 | } |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 183 | localized->SetSource(plural->GetSource()); |
| 184 | localized->SetWeak(true); |
| 185 | value = std::move(localized); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 186 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 187 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 188 | void Visit(String* string) override { |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 189 | const StringPiece original_string = *string->value; |
| 190 | std::string result = localizer_.Start(); |
| 191 | |
| 192 | // Pseudolocalize only the translatable sections. |
| 193 | size_t start = 0u; |
| 194 | for (const UntranslatableSection& section : string->untranslatable_sections) { |
| 195 | // Pseudolocalize the content before the untranslatable section. |
| 196 | const size_t len = section.start - start; |
| 197 | if (len > 0u) { |
| 198 | result += localizer_.Text(original_string.substr(start, len)); |
| 199 | } |
| 200 | |
| 201 | // Copy the untranslatable content. |
| 202 | result += original_string.substr(section.start, section.end - section.start); |
| 203 | start = section.end; |
| 204 | } |
| 205 | |
| 206 | // Pseudolocalize the content after the last untranslatable section. |
| 207 | if (start != original_string.size()) { |
| 208 | const size_t len = original_string.size() - start; |
| 209 | result += localizer_.Text(original_string.substr(start, len)); |
| 210 | } |
| 211 | result += localizer_.End(); |
| 212 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 213 | std::unique_ptr<String> localized = |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 214 | util::make_unique<String>(pool_->MakeRef(result)); |
| 215 | localized->SetSource(string->GetSource()); |
| 216 | localized->SetWeak(true); |
| 217 | item = std::move(localized); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 218 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 219 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 220 | void Visit(StyledString* string) override { |
| 221 | item = PseudolocalizeStyledString(string, method_, pool_); |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 222 | item->SetSource(string->GetSource()); |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 223 | item->SetWeak(true); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 224 | } |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 225 | |
| 226 | private: |
| 227 | DISALLOW_COPY_AND_ASSIGN(Visitor); |
| 228 | |
| 229 | StringPool* pool_; |
| 230 | Pseudolocalizer::Method method_; |
| 231 | Pseudolocalizer localizer_; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 232 | }; |
| 233 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 234 | ConfigDescription ModifyConfigForPseudoLocale(const ConfigDescription& base, |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 235 | Pseudolocalizer::Method m) { |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 236 | ConfigDescription modified = base; |
| 237 | switch (m) { |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 238 | case Pseudolocalizer::Method::kAccent: |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 239 | modified.language[0] = 'e'; |
| 240 | modified.language[1] = 'n'; |
| 241 | modified.country[0] = 'X'; |
| 242 | modified.country[1] = 'A'; |
| 243 | break; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 244 | |
| 245 | case Pseudolocalizer::Method::kBidi: |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 246 | modified.language[0] = 'a'; |
| 247 | modified.language[1] = 'r'; |
| 248 | modified.country[0] = 'X'; |
| 249 | modified.country[1] = 'B'; |
| 250 | break; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 251 | default: |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 252 | break; |
| 253 | } |
| 254 | return modified; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 255 | } |
| 256 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 257 | void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method, |
| 258 | ResourceConfigValue* original_value, |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 259 | StringPool* pool, ResourceEntry* entry) { |
| 260 | Visitor visitor(pool, method); |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 261 | original_value->value->Accept(&visitor); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 262 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 263 | std::unique_ptr<Value> localized_value; |
| 264 | if (visitor.value) { |
| 265 | localized_value = std::move(visitor.value); |
| 266 | } else if (visitor.item) { |
| 267 | localized_value = std::move(visitor.item); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 268 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 269 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 270 | if (!localized_value) { |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 271 | return; |
| 272 | } |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 273 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 274 | ConfigDescription config_with_accent = |
| 275 | ModifyConfigForPseudoLocale(original_value->config, method); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 276 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 277 | ResourceConfigValue* new_config_value = |
| 278 | entry->FindOrCreateValue(config_with_accent, original_value->product); |
| 279 | if (!new_config_value->value) { |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 280 | // Only use auto-generated pseudo-localization if none is defined. |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 281 | new_config_value->value = std::move(localized_value); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 282 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 283 | } |
| 284 | |
Adam Lesinski | 458b877 | 2016-04-25 14:20:21 -0700 | [diff] [blame] | 285 | /** |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 286 | * A value is pseudolocalizable if it does not define a locale (or is the |
| 287 | * default locale) |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 288 | * and is translatable. |
Adam Lesinski | 458b877 | 2016-04-25 14:20:21 -0700 | [diff] [blame] | 289 | */ |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 290 | static bool IsPseudolocalizable(ResourceConfigValue* config_value) { |
| 291 | const int diff = |
| 292 | config_value->config.diff(ConfigDescription::DefaultConfig()); |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 293 | if (diff & ConfigDescription::CONFIG_LOCALE) { |
| 294 | return false; |
| 295 | } |
Adam Lesinski | 7542162 | 2017-01-06 15:20:04 -0800 | [diff] [blame] | 296 | return config_value->value->IsTranslatable(); |
Adam Lesinski | 458b877 | 2016-04-25 14:20:21 -0700 | [diff] [blame] | 297 | } |
| 298 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 299 | } // namespace |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 300 | |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 301 | bool PseudolocaleGenerator::Consume(IAaptContext* context, |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 302 | ResourceTable* table) { |
| 303 | for (auto& package : table->packages) { |
| 304 | for (auto& type : package->types) { |
| 305 | for (auto& entry : type->entries) { |
| 306 | std::vector<ResourceConfigValue*> values = |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 307 | entry->FindValuesIf(IsPseudolocalizable); |
Adam Lesinski | 458b877 | 2016-04-25 14:20:21 -0700 | [diff] [blame] | 308 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 309 | for (ResourceConfigValue* value : values) { |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 310 | PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, |
| 311 | &table->string_pool, entry.get()); |
| 312 | PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, |
| 313 | &table->string_pool, entry.get()); |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 314 | } |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 315 | } |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 316 | } |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 317 | } |
| 318 | return true; |
Adam Lesinski | 393b5f0 | 2015-12-17 13:03:11 -0800 | [diff] [blame] | 319 | } |
| 320 | |
Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 321 | } // namespace aapt |