1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
#!/usr/bin/env python3
# This script generates dists/android/res/values-<qualifier>/strings.xml files
# to add multilanguage support for android strings in res/values/strings.xml file.
# It considers dists/android/res/values/strings.xml file as a base template to generate
# those files. Relevant translated strings.xml file will be automatically used based on
# the android system's language.
#
# Also, this script generates a fake cpp file (dists/android/strings.xml.cpp) with strings
# from dists/android/res/values/strings.xml wrapped inside _() to be picked up by
# gettext for weblate translations
import polib
import os
import re
import xml.etree.ElementTree as ET
BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
def generate_fake_cpp():
cpp_text = '''/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This is an auto generated dummy file used for sticking strings from
* dists/android/res/values/strings.xml into our translation system
*
*/
#include "common/translation.h" // For catching the file during POTFILES reviews\n
'''
with open(os.path.join(BASE_PATH, 'dists/android.strings.xml.cpp'), 'w') as file:
file.write(cpp_text)
tree = ET.parse(os.path.join(BASE_PATH, 'dists/android/res/values/strings.xml'))
root = tree.getroot()
for string in root.findall('string'):
if string.attrib.get('translatable') != 'false':
file.write(
f'static Common::U32String {string.attrib.get("name")} = _("{string.text}");\n')
def extract_translations(file):
po_file = polib.pofile(os.path.join(BASE_PATH, 'po', file + '.po'))
translations = {}
for entry in po_file:
if entry.msgid and entry.msgstr:
translations[entry.msgid] = entry.msgstr
return translations
def escape_special_characters(translated_string):
'''Some characters like single quote (') have special usage in XML and hence must be escaped.\n
See: https://developer.android.com/guide/topics/resources/string-resource.html#escaping_quotes
'''
escaped = translated_string.replace('@', '\\@')
escaped = escaped.replace('?', '\\?')
escaped = escaped.replace('\'', '\\\'')
escaped = escaped.replace('\"', '\\\"')
escaped = escaped.replace('\n', '\\n')
escaped = escaped.replace('\t', '\\t')
return escaped
def is_regional_language_code(language_code):
pattern = r'^[a-zA-Z]{2}([-_][a-zA-Z0-9]{2})?$'
return re.match(pattern, language_code)
def is_bcp47_language_code(language_code):
pattern = r'^[a-zA-Z]{1,8}([-_][a-zA-Z0-9]{1,8})*$'
return re.match(pattern, language_code)
def get_lang_qualifier(file):
'''Generates <qualifier> for res/values-<qualifier> directory as per the specs given here:
https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources
'''
if is_regional_language_code(file):
lang_qualifier = file.replace('_', '-r')
elif is_bcp47_language_code(file):
subtags = re.split('[-_]', file)
lang_qualifier = '+'.join(subtags)
lang_qualifier = 'b+' + lang_qualifier
else:
raise Exception(f"Invalid language code: {file}")
return lang_qualifier
def generate_translated_xml(file):
tree = ET.parse(os.path.join(BASE_PATH, 'dists/android/res/values/strings.xml'))
root = tree.getroot()
translations = extract_translations(file)
for string in root.findall('string'):
if string.text in translations:
string.text = escape_special_characters(translations[string.text])
else:
root.remove(string)
ET.indent(tree, ' ')
dir = os.path.join(BASE_PATH, 'dists/android/res/values-' + get_lang_qualifier(file))
if not os.path.exists(dir):
os.makedirs(dir)
tree.write(os.path.join(dir, 'strings.xml'), encoding='utf-8', xml_declaration=True)
def get_po_files():
po_file_names = []
for filename in os.listdir(os.path.join(BASE_PATH, 'po')):
if not filename.endswith('.po'):
continue
# This skips be-tarask file because there is a bug with be-tarask that gives this compile error:
# AAPT: error: failed to deserialize resource table: configuration has invalid locale 'be-'.
# See the open issue here: https://issuetracker.google.com/issues/234820481
if (filename == 'be-tarask.po'):
continue
po_file_names.append(os.path.splitext(filename)[0])
return po_file_names
def main():
generate_fake_cpp()
po_file_names = get_po_files()
for file in po_file_names:
generate_translated_xml(file)
if __name__ == '__main__':
main()
|