NativeString* AssetManagerGlue::getResourceName(int resid) { ResTable::resource_name name; if (!getResources().getResourceName(resid, &name)) { return NULL; } String16 str; if (name.package != NULL) { str.setTo(name.package, name.packageLen); } if (name.type != NULL) { if (str.size() > 0) { char16_t div = ':'; str.append(&div, 1); } str.append(name.type, name.typeLen); } if (name.name != NULL) { if (str.size() > 0) { char16_t div = '/'; str.append(&div, 1); } str.append(name.name, name.nameLen); } return new NativeString(str); }
TEST(Pseudolocales, RedefineMethod) { Pseudolocalizer pseudo(PSEUDO_ACCENTED); String16 result = pseudo.text(String16(String8("Hello, "))); pseudo.setMethod(NO_PSEUDOLOCALIZATION); result.append(pseudo.text(String16(String8("world!")))); ASSERT_EQ(String8("Ĥéļļö, world!"), String8(result)); }
String16 pseudobidi_string(const String16& source) { const char16_t* s = source.string(); String16 result; result += k_rlm; result += k_rlo; 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; } } result += k_pdf; result += k_rlm; return result; }
status_t parseStyledString(Bundle* bundle, const char* fileName, ResXMLTree* inXml, const String16& endTag, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool pseudolocalize) { Vector<StringPool::entry_style_span> spanStack; String16 curString; String16 rawString; const char* errorMsg; int xliffDepth = 0; bool firstTime = true; size_t len; ResXMLTree::event_code_t code; while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::TEXT) { String16 text(inXml->getText(&len)); if (firstTime && text.size() > 0) { firstTime = false; if (text.string()[0] == '@') { // If this is a resource reference, don't do the pseudoloc. pseudolocalize = false; } } if (xliffDepth == 0 && pseudolocalize) { std::string orig(String8(text).string()); std::string pseudo = pseudolocalize_string(orig); curString.append(String16(String8(pseudo.c_str()))); } else { curString.append(text); } } else if (code == ResXMLTree::START_TAG) { const String16 element16(inXml->getElementName(&len)); const String8 element8(element16); size_t nslen; const uint16_t* ns = inXml->getElementNamespace(&nslen); if (ns == NULL) { ns = (const uint16_t*)"\0\0"; nslen = 0; } const String8 nspace(String16(ns, nslen)); if (nspace == XLIFF_XMLNS) { const int N = sizeof(ALLOWED_XLIFF_ELEMENTS)/sizeof(ALLOWED_XLIFF_ELEMENTS[0]); for (int i=0; i<N; i++) { if (element8 == ALLOWED_XLIFF_ELEMENTS[i]) { xliffDepth++; // in this case, treat it like it was just text, in other words, do nothing // here and silently drop this element goto moveon; } } { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found unsupported XLIFF tag <%s>\n", element8.string()); return UNKNOWN_ERROR; } moveon: continue; } if (outSpans == NULL) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found style tag <%s> where styles are not allowed\n", element8.string()); return UNKNOWN_ERROR; } if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } rawString.append(curString); curString = String16(); StringPool::entry_style_span span; span.name = element16; for (size_t ai=0; ai<inXml->getAttributeCount(); ai++) { span.name.append(String16(";")); const char16_t* str = inXml->getAttributeName(ai, &len); span.name.append(str, len); span.name.append(String16("=")); str = inXml->getAttributeStringValue(ai, &len); span.name.append(str, len); } //printf("Span: %s\n", String8(span.name).string()); span.span.firstChar = span.span.lastChar = outString->size(); spanStack.push(span); } else if (code == ResXMLTree::END_TAG) { size_t nslen; const uint16_t* ns = inXml->getElementNamespace(&nslen); if (ns == NULL) { ns = (const uint16_t*)"\0\0"; nslen = 0; } const String8 nspace(String16(ns, nslen)); if (nspace == XLIFF_XMLNS) { xliffDepth--; continue; } if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } rawString.append(curString); curString = String16(); if (spanStack.size() == 0) { if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found tag %s where <%s> close is expected\n", String8(inXml->getElementName(&len)).string(), String8(endTag).string()); return UNKNOWN_ERROR; } break; } StringPool::entry_style_span span = spanStack.top(); String16 spanTag; ssize_t semi = span.name.findFirst(';'); if (semi >= 0) { spanTag.setTo(span.name.string(), semi); } else { spanTag.setTo(span.name); } if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found close tag %s where close tag %s is expected\n", String8(inXml->getElementName(&len)).string(), String8(spanTag).string()); return UNKNOWN_ERROR; } bool empty = true; if (outString->size() > 0) { span.span.lastChar = outString->size()-1; if (span.span.lastChar >= span.span.firstChar) { empty = false; outSpans->add(span); } } spanStack.pop(); /* * This warning seems to be just an irritation to most people, * since it is typically introduced by translators who then never * see the warning. */ if (0 && empty) { fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n", fileName, inXml->getLineNumber(), String8(spanTag).string(), String8(*outString).string()); } } else if (code == ResXMLTree::START_NAMESPACE) { // nothing } } if (code == ResXMLTree::BAD_DOCUMENT) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Error parsing XML\n"); } if (outSpans != NULL && outSpans->size() > 0) { if (curString.size() > 0) { if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } } } else { // There is no style information, so string processing will happen // later as part of the overall type conversion. Return to the // client the raw unprocessed text. rawString.append(curString); outString->setTo(rawString); } return NO_ERROR; }
/** * 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) { const char16_t* s = source.string(); String16 result; const size_t I = source.size(); for (size_t i=0; i<I; i++) { char16_t c = s[i]; if (c == '\\') { // Escape syntax, no need to pseudolocalize if (i<I-1) { result += String16("\\"); i++; c = s[i]; switch (c) { case 'u': // this one takes up 5 chars result += String16(s+i, 5); i += 4; break; case 't': case 'n': case '#': case '@': case '?': case '"': case '\'': case '\\': default: result.append(&c, 1); break; } } else { result.append(&c, 1); } } else if (c == '%') { // Placeholder syntax, no need to pseudolocalize result += k_placeholder_open; bool end = false; result.append(&c, 1); while (!end && i < I) { ++i; c = s[i]; result.append(&c, 1); if (is_possible_normal_placeholder_end(c)) { end = true; } else if (c == 't') { ++i; c = s[i]; result.append(&c, 1); end = true; } } result += k_placeholder_close; } else if (c == '<' || c == '&') { // html syntax, no need to pseudolocalize bool tag_closed = false; while (!tag_closed && i < I) { if (c == '&') { String16 escape_text; escape_text.append(&c, 1); bool end = false; size_t htmlCodePos = i; while (!end && htmlCodePos < I) { ++htmlCodePos; c = s[htmlCodePos]; escape_text.append(&c, 1); // Valid html code if (c == ';') { end = true; i = htmlCodePos; } // Wrong html code else if (!((c == '#' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) { end = true; } } result += escape_text; if (escape_text != String16("<")) { tag_closed = true; } continue; } if (c == '>') { tag_closed = true; result.append(&c, 1); continue; } result.append(&c, 1); i++; c = s[i]; } } else { // This is a pure text that should be pseudolocalized const char* p = pseudolocalize_char(c); if (p != NULL) { result += String16(p); } else { result.append(&c, 1); } } } return result; }