• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ResourceTable.h"
18 #include "ResourceValues.h"
19 #include "ValueVisitor.h"
20 #include "compile/PseudolocaleGenerator.h"
21 #include "compile/Pseudolocalizer.h"
22 
23 #include <algorithm>
24 
25 namespace aapt {
26 
pseudolocalizeStyledString(StyledString * string,Pseudolocalizer::Method method,StringPool * pool)27 std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
28                                                          Pseudolocalizer::Method method,
29                                                          StringPool* pool) {
30     Pseudolocalizer localizer(method);
31 
32     const StringPiece16 originalText = *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(Span{ *span.name, span.firstChar, span.lastChar });
40     }
41 
42     // The ranges are all represented with a single value. This is the start of one range and
43     // end of another.
44     struct Range {
45         size_t start;
46 
47         // Once the new string is localized, these are the pointers to the spans to adjust.
48         // Since this struct represents the start of one range and end of another, we have
49         // the two pointers respectively.
50         uint32_t* updateStart;
51         uint32_t* updateEnd;
52     };
53 
54     auto cmp = [](const Range& r, size_t index) -> bool {
55         return r.start < index;
56     };
57 
58     // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
59     // The ranges are the spaces in between. In this example, with a total string length of 9,
60     // the vector represents: (0,1], (2,4], (5,6], (7,9]
61     //
62     std::vector<Range> ranges;
63     ranges.push_back(Range{ 0 });
64     ranges.push_back(Range{ originalText.size() - 1 });
65     for (size_t i = 0; i < string->value->spans.size(); i++) {
66         const StringPool::Span& span = string->value->spans[i];
67 
68         // Insert or update the Range marker for the start of this span.
69         auto iter = std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
70         if (iter != ranges.end() && iter->start == span.firstChar) {
71             iter->updateStart = &localized.spans[i].firstChar;
72         } else {
73             ranges.insert(iter,
74                           Range{ span.firstChar, &localized.spans[i].firstChar, nullptr });
75         }
76 
77         // Insert or update the Range marker for the end of this span.
78         iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
79         if (iter != ranges.end() && iter->start == span.lastChar) {
80             iter->updateEnd = &localized.spans[i].lastChar;
81         } else {
82             ranges.insert(iter,
83                           Range{ span.lastChar, nullptr, &localized.spans[i].lastChar });
84         }
85     }
86 
87     localized.str += localizer.start();
88 
89     // Iterate over the ranges and localize each section.
90     for (size_t i = 0; i < ranges.size(); i++) {
91         const size_t start = ranges[i].start;
92         size_t len = originalText.size() - start;
93         if (i + 1 < ranges.size()) {
94             len = ranges[i + 1].start - start;
95         }
96 
97         if (ranges[i].updateStart) {
98             *ranges[i].updateStart = localized.str.size();
99         }
100 
101         if (ranges[i].updateEnd) {
102             *ranges[i].updateEnd = localized.str.size();
103         }
104 
105         localized.str += localizer.text(originalText.substr(start, len));
106     }
107 
108     localized.str += localizer.end();
109 
110     std::unique_ptr<StyledString> localizedString = util::make_unique<StyledString>(
111             pool->makeRef(localized));
112     localizedString->setSource(string->getSource());
113     return localizedString;
114 }
115 
116 namespace {
117 
118 struct Visitor : public RawValueVisitor {
119     StringPool* mPool;
120     Pseudolocalizer::Method mMethod;
121     Pseudolocalizer mLocalizer;
122 
123     // Either value or item will be populated upon visiting the value.
124     std::unique_ptr<Value> mValue;
125     std::unique_ptr<Item> mItem;
126 
Visitoraapt::__anon9fc49c550211::Visitor127     Visitor(StringPool* pool, Pseudolocalizer::Method method) :
128             mPool(pool), mMethod(method), mLocalizer(method) {
129     }
130 
visitaapt::__anon9fc49c550211::Visitor131     void visit(Array* array) override {
132         std::unique_ptr<Array> localized = util::make_unique<Array>();
133         localized->items.resize(array->items.size());
134         for (size_t i = 0; i < array->items.size(); i++) {
135             Visitor subVisitor(mPool, mMethod);
136             array->items[i]->accept(&subVisitor);
137             if (subVisitor.mItem) {
138                 localized->items[i] = std::move(subVisitor.mItem);
139             } else {
140                 localized->items[i] = std::unique_ptr<Item>(array->items[i]->clone(mPool));
141             }
142         }
143         localized->setSource(array->getSource());
144         localized->setWeak(true);
145         mValue = std::move(localized);
146     }
147 
visitaapt::__anon9fc49c550211::Visitor148     void visit(Plural* plural) override {
149         std::unique_ptr<Plural> localized = util::make_unique<Plural>();
150         for (size_t i = 0; i < plural->values.size(); i++) {
151             Visitor subVisitor(mPool, mMethod);
152             if (plural->values[i]) {
153                 plural->values[i]->accept(&subVisitor);
154                 if (subVisitor.mValue) {
155                     localized->values[i] = std::move(subVisitor.mItem);
156                 } else {
157                     localized->values[i] = std::unique_ptr<Item>(plural->values[i]->clone(mPool));
158                 }
159             }
160         }
161         localized->setSource(plural->getSource());
162         localized->setWeak(true);
163         mValue = std::move(localized);
164     }
165 
visitaapt::__anon9fc49c550211::Visitor166     void visit(String* string) override {
167         if (!string->isTranslateable()) {
168             return;
169         }
170 
171         std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) +
172                 mLocalizer.end();
173         std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result));
174         localized->setSource(string->getSource());
175         localized->setWeak(true);
176         mItem = std::move(localized);
177     }
178 
visitaapt::__anon9fc49c550211::Visitor179     void visit(StyledString* string) override {
180         if (!string->isTranslateable()) {
181             return;
182         }
183 
184         mItem = pseudolocalizeStyledString(string, mMethod, mPool);
185         mItem->setWeak(true);
186     }
187 };
188 
modifyConfigForPseudoLocale(const ConfigDescription & base,Pseudolocalizer::Method m)189 ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
190                                               Pseudolocalizer::Method m) {
191     ConfigDescription modified = base;
192     switch (m) {
193     case Pseudolocalizer::Method::kAccent:
194         modified.language[0] = 'e';
195         modified.language[1] = 'n';
196         modified.country[0] = 'X';
197         modified.country[1] = 'A';
198         break;
199 
200     case Pseudolocalizer::Method::kBidi:
201         modified.language[0] = 'a';
202         modified.language[1] = 'r';
203         modified.country[0] = 'X';
204         modified.country[1] = 'B';
205         break;
206     default:
207         break;
208     }
209     return modified;
210 }
211 
pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,ResourceConfigValue * originalValue,StringPool * pool,ResourceEntry * entry)212 void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
213                             ResourceConfigValue* originalValue,
214                             StringPool* pool,
215                             ResourceEntry* entry) {
216     Visitor visitor(pool, method);
217     originalValue->value->accept(&visitor);
218 
219     std::unique_ptr<Value> localizedValue;
220     if (visitor.mValue) {
221         localizedValue = std::move(visitor.mValue);
222     } else if (visitor.mItem) {
223         localizedValue = std::move(visitor.mItem);
224     }
225 
226     if (!localizedValue) {
227         return;
228     }
229 
230     ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
231             originalValue->config, method);
232 
233     ResourceConfigValue* newConfigValue = entry->findOrCreateValue(
234             configWithAccent, originalValue->product);
235     if (!newConfigValue->value) {
236         // Only use auto-generated pseudo-localization if none is defined.
237         newConfigValue->value = std::move(localizedValue);
238     }
239 }
240 
241 } // namespace
242 
consume(IAaptContext * context,ResourceTable * table)243 bool PseudolocaleGenerator::consume(IAaptContext* context, 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 = entry->findAllValues(
248                         ConfigDescription::defaultConfig());
249                 for (ResourceConfigValue* value : values) {
250                     pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
251                                            &table->stringPool, entry.get());
252                     pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
253                                            &table->stringPool, entry.get());
254                 }
255             }
256         }
257     }
258     return true;
259 }
260 
261 } // namespace aapt
262