Last active
March 13, 2018 09:18
-
-
Save Pash237/786bfada4b6aeecfdb98acbc77e061f9 to your computer and use it in GitHub Desktop.
My Alarm Localization Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
from xml.dom import minidom | |
import sys | |
import re | |
import os | |
import fnmatch | |
reload(sys) | |
sys.setdefaultencoding("utf-8") | |
if len(sys.argv) < 2: | |
print("Usage: localize.py android-strings.xml [outputDirectory]") | |
inputFilename = sys.argv[1] | |
if len(sys.argv) >= 3: | |
outputDirectory = sys.argv[2] | |
else: | |
outputDirectory = "" | |
print("Parsing file " + inputFilename + "...") | |
language = os.path.basename(outputDirectory).split(".")[0] | |
print("Language: " + language) | |
xmldoc = minidom.parse(inputFilename) | |
strings = xmldoc.getElementsByTagName('string') | |
stringArrays = xmldoc.getElementsByTagName('string-array') | |
print("Found %d strings" % len(strings)) | |
def stringByRemovingDot(string): | |
if string.endswith("."): | |
return string[:-1] | |
return string | |
def stringWithId(id): | |
for string in strings: | |
if string.attributes['name'].value == id: | |
value = string.childNodes[0].nodeValue | |
value = value.strip("\"") | |
value = value.replace("\n", "\\n") | |
value = value.replace(" \\n ", "\\n") | |
value = value.replace(" \\n", "\\n") | |
value = value.replace("%s", "%@") | |
value = value.replace(" ", "\u00A0") | |
value = value.replace("\u00A0", " ") | |
value = value.replace("\u20BD", "₽") | |
return value | |
print("Error! Can't find string with identifier \"%s\"" % id) | |
sys.exit(1) | |
return None | |
def stringArrayWithId(id): | |
for stringArray in stringArrays: | |
if stringArray.attributes['name'].value == id: | |
strings = [] | |
for string in stringArray.getElementsByTagName("item"): | |
strings.append(string.childNodes[0].nodeValue.strip("\"")) | |
return strings | |
return None | |
def localizableStringsFromDict(strings): | |
output = "" | |
for key in strings: | |
keyString = key.replace("\n", "\\n") | |
if strings[key] == None: | |
strings[key] = key | |
value = "%s" % strings[key] | |
output += "\"%s\" = \"%s\";\n" % (keyString, value) | |
return output | |
def writeFile(dictionary, filename): | |
path = outputDirectory + "/" + filename | |
print("Writing %s..." % path) | |
if not os.path.exists(outputDirectory): | |
os.makedirs(outputDirectory) | |
with open(path, "w") as file: | |
file.write("%s" % localizableStringsFromDict(dictionary)) | |
def findFile(directory, pattern): | |
print("Finding %s in %s" % (pattern, directory)) | |
for root, dirnames, filenames in os.walk(directory): | |
for filename in fnmatch.filter(filenames, pattern): | |
return os.path.join(root, filename) | |
return None | |
writeFile({ | |
"J6T-g6-Kuc.title": stringWithId("done"), | |
"TbF-Bs-mfM.placeholder": stringWithId("phone_code_default") + "(000) 000-00-00", | |
"pjW-dj-OaV.title": stringWithId("next"), | |
"qVg-I0-9pV.text": stringWithId("settings_user_invite_header") | |
}, "Invites.strings") | |
writeFile({ | |
"8QN-nk-O5e.text": stringByRemovingDot(stringWithId("login_screen_subheader")), | |
"Fif-Vm-oh0.title": stringWithId("login_screen_title"), | |
"WQu-nJ-FfU.title": stringWithId("login_screen_title"), | |
"OKw-IQ-oWp.placeholder": stringWithId("login_screen_sms_code"), | |
"Sfu-7i-O3c.text": stringWithId("login_screen_header"), | |
"knp-MQ-oGb.title": stringWithId("next"), | |
"rHV-Ko-3R5.normalTitle": stringWithId("login_screen_sms_retry_colorized_part"), | |
"xtG-Er-ETb.placeholder": stringWithId("phone_code_default") + "(000) 000-00-00" | |
}, "LogIn.strings") | |
writeFile({ | |
"DwG-ao-M7D.text": stringWithId("application_tagline"), | |
"KGp-YG-Uih.text": stringWithId("pin_code_step_unlock"), | |
"ZrV-Y1-zB2.text": stringWithId("application_name") | |
}, "Main.strings") | |
writeFile({ | |
"DSe-Ul-vK1.title": stringWithId("next"), | |
"Fhe-z7-HXN.text": stringWithId("arm_disarm_title_arm"), | |
"XZb-Rq-tRp.placeholder": stringWithId("enter_code"), | |
"ahr-PB-LeS.title": stringWithId("video_title"), | |
"sc8-4R-3C1.text": stringWithId("no_areas_header"), | |
"vxh-dd-UWh.text": stringWithId("no_areas_subheader"), | |
"zIR-Iq-IjJ.title": stringWithId("balance_screen_title") | |
}, "Objects.strings") | |
writeFile({ | |
"Ct7-0M-tUH.title": stringWithId("settings"), | |
"FDw-V3-V8v.text": stringWithId("settings_pushes_sound"), | |
"GpK-YH-Qv1.text": stringWithId("settings_pushes_label"), | |
"Md7-bu-hd0.title": stringWithId("rename_screen_title"), | |
"Ncw-e0-2Bw.text": stringWithId("settings_parts_label"), | |
"gFX-jd-qQ9.text": stringWithId("settings_pushes_sound_label"), | |
"i2U-Z2-510.text": "A", | |
"lSx-tx-IeD.title": stringWithId("settings"), | |
"lsx-yx-CGf.text": stringWithId("object_part_users_title"), | |
"qGb-30-IYB.text": stringWithId("settings_users_label"), | |
"rLe-BE-JVM.normalTitle": stringWithId("edit"), | |
"vUX-0l-5uQ.title": stringWithId("save") | |
}, "ObjectSettings.strings") | |
writeFile({ | |
"3G6-iy-3F9.title": stringWithId("edit"), | |
"GaG-dV-p0m.text": stringWithId("temperature_sensor_info_header"), | |
"pFf-Vm-ZCe.title": stringWithId("temperature_sensor_info_screen_header"), | |
}, "Sensors.strings") | |
writeFile({ | |
"2cN-0h-0E1.text": stringWithId("settings_log_out"), | |
"5eX-mu-CK3.title": stringWithId("app_settings_lock_pin_screen_title"), | |
"88B-4S-7Cf.text": stringWithId("settings_security_subtitle"), | |
"IjG-9c-CB1.text": stringWithId("pin_code_step_create"), | |
"Zmo-jV-bYi.title": stringWithId("app_settings_lock_pin_screen_title"), | |
"fH9-96-t99.title": stringWithId("settings"), | |
"gJH-Cm-s05.text": stringWithId("pin_code_step_enable_confirm"), | |
"xK8-T9-il2.text": stringWithId("app_settings_lock_pin_change") | |
}, "Settings.strings") | |
writeFile({ | |
"7VC-Pr-CQI.text": stringWithId("user_identification_about_user_not_identified_subtitle"), | |
"9K2-Op-Qif.text": stringWithId("user_identification_about_user_not_identified_details"), | |
"FSH-Nh-hFK.text": stringWithId("user_identification_about_subtitle"), | |
"HfE-wA-daU.text": stringWithId("user_identification_about_details"), | |
"Qb3-a5-Q6b.title": stringWithId("user_identification_title"), | |
"Lly-Ut-eiL.text": stringWithId("settings_user_this_is_administrator"), | |
"W8l-Pi-buM.normalTitle": stringWithId("welcome_screen_change_photo"), | |
"ZNn-Eq-zrG.text": stringWithId("settings_user_edit_user_access_feature_is_coming_soon"), | |
"i0u-xT-aNl.text": stringWithId("settings_user_how_to_change_phone_details"), | |
"iso-fu-OqW.text": stringWithId("user_object_parts_title"), | |
"j05-sD-Plv.normalTitle": stringWithId("call_user"), | |
"kKs-jO-0S1.text": stringWithId("settings_user_invite_description_short"), | |
"loR-N3-C5O.text": stringWithId("settings_user_how_to_change_phone_title"), | |
"mfI-Tc-EdG.title": stringWithId("save"), | |
"msc-h4-iMT.text": stringWithId("settings_user_invite_label"), | |
"nzR-w5-thc.title": stringWithId("edit_user_screen_title_full") | |
}, "User.strings") | |
writeFile({ | |
"3sR-2V-XlX.title": stringWithId("welcome_screen_photo_title"), | |
"5PT-gE-uP5.text": stringWithId("onboarding_screen_5_title"), | |
"F1e-LH-ral.placeholder": stringWithId("first_name"), | |
"FGl-tM-ikp.title": stringWithId("skip"), | |
"IRY-Ks-1ac.text": stringWithId("onboarding_screen_5_details"), | |
"Kq7-0L-pzm.placeholder": stringWithId("last_name"), | |
"PkB-zJ-Agj.text": stringWithId("onboarding_screen_1_title"), | |
"Qfy-N6-sEv.text": stringWithId("onboarding_screen_2_details"), | |
"Rjs-dx-78q.normalTitle": stringWithId("welcome_screen_add_photo"), | |
"T0N-gE-q21.text": stringWithId("onboarding_screen_3_title"), | |
"Yg4-U5-lTo.title": stringWithId("next"), | |
"cvQ-rM-LCP.title": stringWithId("welcome_screen_name_title"), | |
"cvk-2w-QIf.text": stringWithId("onboarding_screen_2_title"), | |
"fsP-uX-At4.text": stringWithId("onboarding_screen_4_title"), | |
"ixC-lw-4jz.text": stringWithId("welcome_screen_add_photo_explanation"), | |
"kLZ-9f-4rq.text": stringWithId("onboarding_screen_1_details"), | |
"mpv-9i-Ydw.normalTitle": stringWithId("skip"), | |
"ruu-y2-0nT.text": stringWithId("onboarding_screen_3_details"), | |
"vPw-fA-Qo4.text": stringWithId("onboarding_screen_4_details"), | |
}, "Welcome.strings") | |
writeFile({ | |
"Unhandled error": stringWithId("unhandled_error"), | |
"Wrong phone format": stringWithId("wrong_phone_number_format_error"), | |
"required field": stringWithId("please_fill_required_fields_error"), | |
"Время ожидания истекло.": stringWithId("timeout_error"), | |
"Ошибка сервера. Попробуйте повторить позже.": stringWithId("server_error"), | |
"Ошибка соединения. Проверьте подключение к Интернету.": stringWithId("connection_error"), | |
"Договор №\u00a0%@": stringWithId("balance_screen_balance_number"), | |
"Пользователи": stringWithId("users_title"), | |
"Списание: %@": stringWithId("balance_screen_billing_date"), | |
"Абонентская плата: %.0f\u00a0\u20BD": stringWithId("balance_screen_monthly_charge"), | |
"Администраторы": stringWithId("administrators_title"), | |
"Рита": stringWithId("settings_pushes_sound_rita"), | |
"Системный": stringWithId("settings_pushes_sound_default"), | |
"Без звука": stringWithId("settings_pushes_sound_mute"), | |
"Другой": stringWithId("settings_pushes_sound_other"), | |
"Сфотографировать": stringArrayWithId("change_photo_doalog_items")[0], | |
"Выбрать из библиотеки": stringArrayWithId("change_photo_doalog_items")[1], | |
"Отменить": stringWithId("cancel_action"), | |
"Снять с охраны": stringWithId("arm_disarm_title_disarm"), | |
"Взять под охрану": stringWithId("arm_disarm_title_arm"), | |
"Только что": stringWithId("time_just_now"), | |
"%d с назад": stringWithId("time_seconds_ago"), | |
"%d мин. назад": stringWithId("time_minutes_ago"), | |
"%lg ч назад": stringWithId("time_hours_ago"), | |
"Вчера в %@": stringWithId("time_yesterday_at"), | |
"Сегодня в %@": stringWithId("time_today_at"), | |
"Завтра в %@": stringWithId("time_tomorrow_at"), | |
"d MMMM в HH:mm": stringWithId("time_long"), | |
"Вчера": stringWithId("time_yesterday"), | |
"Сегодня": stringWithId("time_today"), | |
"Завтра": stringWithId("time_tomorrow"), | |
"Видео": stringWithId("sensor_video"), | |
"Нельзя отзывать доступ к объекту": stringWithId("settings_user_deleting_user_account_on_this_object_not_possible"), | |
"Вы уверены, что хотите отключить пользователя от личного кабинета?": stringWithId("settings_user_revoke_access_dialog_message"), | |
"Да, отключить": stringWithId("settings_user_revoke_access_dialog_ok"), | |
"Это вы": stringWithId("settings_user_this_is_you"), | |
"Это администратор": stringWithId("settings_user_this_is_administrator"), | |
"Отмена": stringWithId("cancel"), | |
"Звук уведомлений": stringWithId("settings_pushes_sound"), | |
"Взятия под охрану": stringWithId("settings_pushes_arms"), | |
"Снятия с охраны": stringWithId("settings_pushes_disarms"), | |
"Охранные тревоги": stringWithId("settings_pushes_security_alarm"), | |
"Пожарные тревоги": stringWithId("settings_pushes_fire_alarm"), | |
"Технологические тревоги": stringWithId("settings_pushes_aux"), | |
"Температурные датчики": stringWithId("settings_pushes_temperature"), | |
"Неисправности": stringWithId("settings_pushes_faults"), | |
"Возгорание": stringWithId("sensor_fire"), | |
"Протечка": stringWithId("sensor_water"), | |
"Утечка газа": stringWithId("sensor_gas"), | |
"Протечки воды и утечки газа": stringWithId("settings_pushes_aux_description"), | |
"Не поддерживается": stringWithId("sensor_not_supported"), | |
"Вы уверены, что хотите выйти из учётной записи?": stringWithId("log_out_confirmation_text"), | |
"Да, выйти": stringWithId("confirm_log_out"), | |
"Добро пожаловать": stringWithId("main_screen_default_title"), | |
"Если тревога не актуальна, мы остановим пуш-уведомления и уберём красный значок с иконки датчика.": stringWithId("stop_sensor_dialog_message"), | |
"Да, я видел тревогу": stringWithId("stop_sensor_dialog_positive_button"), #Нужно в Android поменять, сейчас там просто «Да» | |
"Подробнее": stringWithId("show_temperature_sensor_details"), | |
"Закрыть": stringWithId("close"), | |
"Пройдите процедуру идентификации": stringWithId("user_identification_required"), | |
"Не получили код? Сможем отправить заново через %d секунд.": stringWithId("login_screen_sms_retry_after").replace("%1$d", "%d"), | |
"Не получили код?": stringWithId("login_screen_sms_retry_beginning"), | |
"Приложите палец для входа": stringWithId("app_settings_lock_finger_lock_offer"), | |
"Мы отправили СМС %@ на номер: %@": stringWithId("login_screen_sms_have_sent_to_user_named_to_phone_number"), | |
"Мы отправим СМС %@": stringWithId("login_screen_sms_will_sent_to_user_named"), | |
"Удаленное управление в настоящий момент недоступно, попробуйте позже": stringWithId("arm_disarm_not_ready"), | |
"Повторить": stringWithId("retry"), | |
"№ %ld": stringWithId("settings_part_number").replace("%s", "%ld"), | |
"Нет доступа к разделам": stringWithId("settings_user_no_parts"), | |
"Скопировать телефон": stringWithId("copy_phone_number_to_clipboard"), | |
"Тревожная кнопка": stringWithId("panic_button_name"), | |
"Сохранить": stringWithId("save"), | |
"Мобильное приложение %@\nВерсия %@ (%@)": stringWithId("about_and_version_string"), | |
"Датчик температуры": stringWithId("temperature_sensor_info_header"), | |
"Изменить": stringWithId("edit"), | |
"Если температура упадёт {#037AFF|ниже\u00a0LOW_THRESHOLD˚} или поднимется {#FF3B30|выше\u00a0HIGH_THRESHOLD˚}, мы отправим пуш-уведомление. Значения порогов задаёт инженер при настройке оборудования.": re.sub(ur"(.*?)(\S+)\s*\\[Uu]00[aA]0\s*%1\$s˚(.*?)(\S+)\s*\\[Uu]00[aA]0\s*%2\$s˚(.*)", ur"\1{#037AFF|\2\\u00a0LOW_THRESHOLD˚}\3{#FF3B30|\4\\u00a0HIGH_THRESHOLD˚}\5", stringWithId("temperature_sensor_info_description_both_thresholds")), | |
"Если температура упадёт {#037AFF|ниже\u00a0LOW_THRESHOLD˚}, мы отправим пуш-уведомление. Значения порогов задаёт инженер при настройке оборудования.": re.sub(ur"(.*?)(\S+)\s*\\[Uu]00[aA]0\s*%s˚(.*)", ur"\1{#037AFF|\2\\u00a0LOW_THRESHOLD˚}\3", stringWithId("temperature_sensor_info_description_low_threshold")), | |
"Если температура поднимется {#FF3B30|выше\u00a0HIGH_THRESHOLD˚}, мы отправим пуш-уведомление. Значения порогов задаёт инженер при настройке оборудования.": re.sub(ur"(.*?)(\S+)\s*\\[Uu]00[aA]0\s*%s˚(.*)", ur"\1{#FF3B30|\2\\u00a0HIGH_THRESHOLD˚}\3", stringWithId("temperature_sensor_info_description_high_threshold")), | |
"Нижний и верхний пороги температуры не заданы. Это может сделать инженер при настройке оборудования.": stringWithId("temperature_sensor_info_description_no_thresholds"), | |
"Переименовать": stringWithId("rename_screen_title"), | |
"Далее": stringWithId("next"), | |
"Пропустить": stringWithId("skip"), | |
"OK": stringWithId("okay"), | |
"Личный кабинет": stringWithId("settings_user_invite_label"), | |
"Вход по пин-коду": stringWithId("app_settings_lock_pin_lock"), | |
"Вход по отпечатку пальца": stringWithId("app_settings_lock_finger_lock"), | |
"3-х минутный вход без кода": stringWithId("app_settings_lock_timeout") | |
}, "Localizable.strings") | |
writeFile({ | |
"CFBundleDisplayName": stringWithId("application_name"), | |
"UILaunchStoryboardName": "LaunchScreen_%s" % language, | |
"NSCameraUsageDescription": stringWithId("camera_usage_description"), | |
"NSPhotoLibraryUsageDescription": stringWithId("photo_library_usage_description") | |
}, "InfoPlist.strings") | |
print("Done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Strings not present in Android app: