Skip to content

Instantly share code, notes, and snippets.

@Pash237
Last active March 13, 2018 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pash237/786bfada4b6aeecfdb98acbc77e061f9 to your computer and use it in GitHub Desktop.
Save Pash237/786bfada4b6aeecfdb98acbc77e061f9 to your computer and use it in GitHub Desktop.
My Alarm Localization Script
#!/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("&#160;", "\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.")
@Pash237
Copy link
Author

Pash237 commented Feb 15, 2018

Strings not present in Android app:

    <!-- iOS specific strings -->
    <string name="next">Далее</string>
    <string name="edit">Изменить</string>
    <string name="okay">OK</string>
    <string name="close">Закрыть</string>
    <string name="cancel_action">Отменить</string>
    <string name="enter_code">Введите код</string>
    <string name="rename_screen_title">Переименовать</string>
    <string name="object_part_users_title">Доступ к помещению</string>
    <string name="temperature_sensor_info_screen_header">Температура</string>
    <string name="settings_log_out">Выйти из приложения</string>
    <string name="app_settings_lock_pin_screen_title">Смена пин-кода</string>
    <string name="settings_security_subtitle">Безопасность</string>

    <string name="administrators_title">Администраторы</string>
    <string name="settings_pushes_sound_other">Другой</string>
    <string name="about_and_version_string">Мобильное приложение %s\nВерсия %s (%s)</string>

    <string name="user_object_parts_title">Доступ к помещениям:</string>
    <string name="call_user">Позвонить</string>
    <string name="settings_user_invite_description_short">Управление через мобильное приложение</string>
    <string name="edit_user_screen_title_full">Редактирование</string>
    <string name="settings_user_how_to_change_phone_title">Как изменить # телефона</string>
    <string name="settings_user_how_to_change_phone_details">Откройте профиль пользователя и отключите от личного кабинета. Затем отредактируйте # телефона и снова подключите к личному кабинету.</string>
    <string name="settings_user_edit_user_access_feature_is_coming_soon">Скоро! В ближайшем будущем мы реализуем редактирование доступа.</string>
    <string name="settings_user_this_is_administrator">Это администратор</string>
    <string name="settings_user_this_is_you">Это вы</string>
    <string name="settings_user_deleting_user_account_on_this_object_not_possible">Нельзя отзывать доступ к объекту</string>

    <string name="user_identification_title">Идентификация</string>
    <string name="user_identification_about_subtitle">Что такое идентификация пользователя на объекте</string>
    <string name="user_identification_about_details">После приглашения в личный кабинет, пользователь сможет управлять охраной объекта. Но что делать, если вы случайно ошиблись и отправили приглашение не на тот номер?\nДля этого мы просим каждого пользователя ввести его личный код взятия / снятия.</string>
    <string name="user_identification_about_user_not_identified_subtitle">Что делать, если пользователь не прошел идентификацию?</string>
    <string name="user_identification_about_user_not_identified_details">1. У него руки не дошли. Попинайте.\n
2. Он забыл код взятия / снятия.
\n2. Вы отправили приглашение не тому.</string>
    <string name="user_identification_required">Пройдите процедуру идентификации</string>

    <string name="onboarding_screen_1_title">Доступ к помещениям</string>
    <string name="onboarding_screen_1_details">В новом приложении видно, какие помещения под охраной и к каким помещениям у вас есть доступ.</string>
    <string name="onboarding_screen_2_title">Пользователи</string>
    <string name="onboarding_screen_2_details">Все пользователи, занесённые в контрольную панель, видны в мобильном приложении.</string>
    <string name="onboarding_screen_3_title">Датчики</string>
    <string name="onboarding_screen_3_details">Датчики возгорания, протечки воды, утечки газа, показания температуры на объекте в реальном времени.</string>
    <string name="onboarding_screen_4_title">Пуш-уведомления</string>
    <string name="onboarding_screen_4_details">Будьте в курсе важных событий: тревоги, срабатывания технологических датчиков; взятия / снятия.</string>
    <string name="onboarding_screen_5_title">Защита пин-кодом</string>
    <string name="onboarding_screen_5_details">Активируйте в настройках приложения «Защиту пин-кодом». Разблокировка по отпечатку пальца тоже работает!</string>

    <string name="copy_phone_number_to_clipboard">Скопировать телефон</string>
    <string name="welcome_screen_add_photo_explanation">С фото лента событий интересней:</string>

    <string name="log_out_confirmation_text">Вы уверены, что хотите выйти из учётной записи?</string>
    <string name="confirm_log_out">Да, выйти</string>

    <string name="sensor_not_supported">Не поддерживается</string>

    <string name="login_screen_sms_retry_beginning">Не получили код?</string>
    <string name="login_screen_sms_will_sent_to_user_named">Мы отправим СМС %s</string>
    <string name="login_screen_sms_have_sent_to_user_named_to_phone_number">Мы отправили СМС %s на номер: %s</string>

    <string name="app_settings_lock_finger_lock_offer">Приложите палец для входа</string>

    <string name="show_temperature_sensor_details">Подробнее</string>

    <!-- iOS time formatting -->
    <string name="time_yesterday">Вчера</string>
    <string name="time_today">Сегодня</string>
    <string name="time_tomorrow">Завтра</string>
    <string name="time_long">d MMMM в HH:mm</string>
    <string name="time_yesterday_at">Вчера в %s</string>
    <string name="time_today_at">Сегодня в %s</string>
    <string name="time_tomorrow_at">Завтра в %s</string>
    <string name="time_seconds_ago">%d с назад</string>
    <string name="time_minutes_ago">%d мин. назад</string>
    <string name="time_hours_ago">%lg ч назад</string>
    <string name="time_just_now">Только что</string>

    <string name="balance_screen_title">Баланс</string>
    <string name="balance_screen_balance_number">Договор №\u00a0%s</string>
    <string name="balance_screen_monthly_charge">Абонентская плата: %.0f\u00a0\u20BD</string>
    <string name="balance_screen_billing_date">Списание: %s</string>

    <!-- iOS more general errors -->
    <string name="unhandled_error">Операция недоступна. Попробуйте позже.</string>
    <string name="wrong_phone_number_format_error">Неверный формат номера телефона.</string>
    <string name="please_fill_required_fields_error">Заполните необходимые поля</string>
    <string name="timeout_error">Время ожидания истекло.</string>
    <string name="server_error">Ошибка сервера. Попробуйте повторить позже.</string>
    <string name="connection_error">Ошибка соединения. Проверьте подключение к Интернету.</string>

    <string name="application_name">MyAlarm</string>
    <string name="application_tagline">Простая и удобная\nсистема охраны</string>
    <string name="camera_usage_description">Фото для профиля пользователя</string>
    <string name="photo_library_usage_description">Фото для профиля пользователя</string>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment