summaryrefslogtreecommitdiffstats
path: root/tools/aapt/pseudolocalize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt/pseudolocalize.cpp')
-rw-r--r--tools/aapt/pseudolocalize.cpp171
1 files changed, 145 insertions, 26 deletions
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 60aa2b2..c7fee2c 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -16,6 +16,80 @@ static const String16 k_pdf = String16("\xE2\x80\xac");
static const String16 k_placeholder_open = String16("\xc2\xbb");
static const String16 k_placeholder_close = String16("\xc2\xab");
+static const char16_t k_arg_start = '{';
+static const char16_t k_arg_end = '}';
+
+Pseudolocalizer::Pseudolocalizer(PseudolocalizationMethod m)
+ : mImpl(nullptr), mLastDepth(0) {
+ setMethod(m);
+}
+
+void Pseudolocalizer::setMethod(PseudolocalizationMethod m) {
+ if (mImpl) {
+ delete mImpl;
+ }
+ if (m == PSEUDO_ACCENTED) {
+ mImpl = new PseudoMethodAccent();
+ } else if (m == PSEUDO_BIDI) {
+ mImpl = new PseudoMethodBidi();
+ } else {
+ mImpl = new PseudoMethodNone();
+ }
+}
+
+String16 Pseudolocalizer::text(const String16& text) {
+ String16 out;
+ size_t depth = mLastDepth;
+ size_t lastpos, pos;
+ const size_t length= text.size();
+ const char16_t* str = text.string();
+ bool escaped = false;
+ for (lastpos = pos = 0; pos < length; pos++) {
+ char16_t c = str[pos];
+ if (escaped) {
+ escaped = false;
+ continue;
+ }
+ if (c == '\'') {
+ escaped = true;
+ continue;
+ }
+
+ if (c == k_arg_start) {
+ depth++;
+ } else if (c == k_arg_end && depth) {
+ depth--;
+ }
+
+ if (mLastDepth != depth || pos == length - 1) {
+ bool pseudo = ((mLastDepth % 2) == 0);
+ size_t nextpos = pos;
+ if (!pseudo || depth == mLastDepth) {
+ nextpos++;
+ }
+ size_t size = nextpos - lastpos;
+ if (size) {
+ String16 chunk = String16(text, size, lastpos);
+ if (pseudo) {
+ chunk = mImpl->text(chunk);
+ } else if (str[lastpos] == k_arg_start &&
+ str[nextpos - 1] == k_arg_end) {
+ chunk = mImpl->placeholder(chunk);
+ }
+ out.append(chunk);
+ }
+ if (pseudo && depth < mLastDepth) { // End of message
+ out.append(mImpl->end());
+ } else if (!pseudo && depth > mLastDepth) { // Start of message
+ out.append(mImpl->start());
+ }
+ lastpos = nextpos;
+ mLastDepth = depth;
+ }
+ }
+ return out;
+}
+
static const char*
pseudolocalize_char(const char16_t c)
{
@@ -78,8 +152,7 @@ pseudolocalize_char(const char16_t c)
}
}
-static bool
-is_possible_normal_placeholder_end(const char16_t c) {
+static bool is_possible_normal_placeholder_end(const char16_t c) {
switch (c) {
case 's': return true;
case 'S': return true;
@@ -106,8 +179,7 @@ is_possible_normal_placeholder_end(const char16_t c) {
}
}
-String16
-pseudo_generate_expansion(const unsigned int length) {
+static String16 pseudo_generate_expansion(const unsigned int length) {
String16 result = k_expansion_string;
const char16_t* s = result.string();
if (result.size() < length) {
@@ -127,18 +199,47 @@ pseudo_generate_expansion(const unsigned int length) {
return result;
}
+static bool is_space(const char16_t c) {
+ return (c == ' ' || c == '\t' || c == '\n');
+}
+
+String16 PseudoMethodAccent::start() {
+ String16 result;
+ if (mDepth == 0) {
+ result = String16(String8("["));
+ }
+ mWordCount = mLength = 0;
+ mDepth++;
+ return result;
+}
+
+String16 PseudoMethodAccent::end() {
+ String16 result;
+ if (mLength) {
+ result.append(String16(String8(" ")));
+ result.append(pseudo_generate_expansion(
+ mWordCount > 3 ? mLength : mLength / 2));
+ }
+ mWordCount = mLength = 0;
+ mDepth--;
+ if (mDepth == 0) {
+ result.append(String16(String8("]")));
+ }
+ return result;
+}
+
/**
* Converts characters so they look like they've been localized.
*
* Note: This leaves escape sequences untouched so they can later be
* processed by ResTable::collectString in the normal way.
*/
-String16
-pseudolocalize_string(const String16& source)
+String16 PseudoMethodAccent::text(const String16& source)
{
const char16_t* s = source.string();
String16 result;
const size_t I = source.size();
+ bool lastspace = true;
for (size_t i=0; i<I; i++) {
char16_t c = s[i];
if (c == '\\') {
@@ -170,23 +271,24 @@ pseudolocalize_string(const String16& source)
}
} else if (c == '%') {
// Placeholder syntax, no need to pseudolocalize
- result += k_placeholder_open;
+ String16 chunk;
bool end = false;
- result.append(&c, 1);
+ chunk.append(&c, 1);
while (!end && i < I) {
++i;
c = s[i];
- result.append(&c, 1);
+ chunk.append(&c, 1);
if (is_possible_normal_placeholder_end(c)) {
end = true;
} else if (c == 't') {
++i;
c = s[i];
- result.append(&c, 1);
+ chunk.append(&c, 1);
end = true;
}
}
- result += k_placeholder_close;
+ // Treat chunk as a placeholder unless it ends with %.
+ result += ((c == '%') ? chunk : placeholder(chunk));
} else if (c == '<' || c == '&') {
// html syntax, no need to pseudolocalize
bool tag_closed = false;
@@ -234,35 +336,52 @@ pseudolocalize_string(const String16& source)
if (p != NULL) {
result += String16(p);
} else {
+ bool space = is_space(c);
+ if (lastspace && !space) {
+ mWordCount++;
+ }
+ lastspace = space;
result.append(&c, 1);
}
+ // Count only pseudolocalizable chars and delimiters
+ mLength++;
}
}
return result;
}
+String16 PseudoMethodAccent::placeholder(const String16& source) {
+ // Surround a placeholder with brackets
+ return k_placeholder_open + source + k_placeholder_close;
+}
-String16
-pseudobidi_string(const String16& source)
+String16 PseudoMethodBidi::text(const String16& source)
{
const char16_t* s = source.string();
String16 result;
- result += k_rlm;
- result += k_rlo;
+ bool lastspace = true;
+ bool space = true;
for (size_t i=0; i<source.size(); i++) {
char16_t c = s[i];
- switch(c) {
- case ' ': result += k_pdf;
- result += k_rlm;
- result.append(&c, 1);
- result += k_rlm;
- result += k_rlo;
- break;
- default: result.append(&c, 1);
- break;
+ space = is_space(c);
+ if (lastspace && !space) {
+ // Word start
+ result += k_rlm + k_rlo;
+ } else if (!lastspace && space) {
+ // Word end
+ result += k_pdf + k_rlm;
}
+ lastspace = space;
+ result.append(&c, 1);
+ }
+ if (!lastspace) {
+ // End of last word
+ result += k_pdf + k_rlm;
}
- result += k_pdf;
- result += k_rlm;
return result;
}
+String16 PseudoMethodBidi::placeholder(const String16& source) {
+ // Surround a placeholder with directionality change sequence
+ return k_rlm + k_rlo + source + k_pdf + k_rlm;
+}
+