diff --git a/.gitignore b/.gitignore index 8a80e27f8..e27efabdf 100644 --- a/.gitignore +++ b/.gitignore @@ -115,4 +115,10 @@ vendor/ venv/ Podfile.lock config_settings.yaml -default_config/ \ No newline at end of file +default_config/ + +# Additions for specific folders +.venv/ +I18N/ +*.lproj/ +!en.lproj/ diff --git a/Authorization/Authorization/uk.lproj/Localizable.strings b/Authorization/Authorization/uk.lproj/Localizable.strings deleted file mode 100644 index 00ab874ce..000000000 --- a/Authorization/Authorization/uk.lproj/Localizable.strings +++ /dev/null @@ -1,49 +0,0 @@ -/* - Localizable.strings - Authorization - - Created by Vladimir Chekyrta on 13.09.2022. - -*/ - -"SIGN_IN.LOG_IN_TITLE" = "Увійти"; -"SIGN_IN.WELCOME_BACK" = "Welcome back! Sign in to access your courses."; -"SIGN_IN.EMAIL" = "Пошта"; -"SIGN_IN.PASSWORD" = "Пароль"; -"SIGN_IN.FORGOT_PASS_BTN" = "Забули пароль?"; -"SIGN_IN.AGREEMENT" = "By signing in to this app, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and you acknowledge that %@ and each Member process your personal data in -accordance with the [Privacy Policy.](%@)"; - -"ERROR.INVALID_EMAIL_ADDRESS" = "невірна адреса електронної пошти"; -"ERROR.INVALID_PASSWORD_LENGHT" = "Пароль занадто короткий або занадто довгий"; -"ERROR.ACCOUNT_NOT_REGISTERED" = "This %@ account is not linked with any %@ account. Please register."; -"ERROR.DISABLED_ACCOUNT" = "Your account is disabled. Please contact customer support for assistance."; - -"SIGN_UP.SUBTITLE" = "Create an account to start learning today!"; -"SIGN_UP.CREATE_ACCOUNT_BTN" = "Створити акаунт"; -"SIGN_UP.HIDE_FIELDS" = "Приховати необовʼязкові поля"; -"SIGN_UP.SHOW_FIELDS" = "Показати необовʼязкові поля"; -"SIGN_UP.SUCCESS_SIGNIN_LABEL" = "You've successfully signed in."; -"SIGN_UP.SUCCESS_SIGNIN_SUBLABEL" = "We just need a little more information before you start learning."; -"SIGN_UP.AGREEMENT" = "By creating an account, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and you acknowledge that %@ and each Member process your personal data inaccordance with the [Privacy Policy.](%@)"; -"SIGN_UP.MARKETING_EMAIL_TITLE" = "I agree that %@ may send me marketing messages."; - -"FORGOT.TITLE"= "Відновлення паролю"; -"FORGOT.DESCRIPTION" = "Будь ласка, введіть свою адресу електронної пошти для входу або відновлення нижче, і ми надішлемо вам електронний лист з інструкціями."; -"FORGOT.REQUEST" = "Відновити пароль"; -"FORGOT.CHECK_TITLE" = "Перевірте свою електронну пошту"; -"FORGOT.CHECK_Description" = "Ми надіслали інструкції щодо відновлення пароля на вашу електронну пошту "; - -"SIGN_IN_WITH" = "Sign in with"; -"REGISTER_WITH" = "Register with"; -"APPLE" = "Apple"; -"GOOGLE" = "Google"; -"FACEBOOK" = "Facebook"; -"MICROSOFT" = "Microsoft"; -"OR" = "Or"; - -"STARTUP.INFO_MESSAGE" = "Courses and programs from the world's best universities in your pocket."; -"STARTUP.SEARCH_TITLE" = "What do you want to learn?"; -"STARTUP.SEARCH_PLACEHOLDER" = "Search our 3000+ courses"; -"STARTUP.EXPLORE_ALL_COURSES" = "Explore all courses"; -"STARTUP.TITLE" = "Start"; diff --git a/Core/Core/uk.lproj/Localizable.strings b/Core/Core/uk.lproj/Localizable.strings deleted file mode 100644 index 1da07ab04..000000000 --- a/Core/Core/uk.lproj/Localizable.strings +++ /dev/null @@ -1,115 +0,0 @@ -/* - Localizable.strings - Core - - Created by Vladimir Chekyrta on 13.09.2022. - -*/ - -"MAINSCREEN.DISCOVERY" = "Всі курси"; -"MAINSCREEN.DASHBOARD" = "Мої курси"; -"MAINSCREEN.IN_DEVELOPING" = "В розробці"; -"MAINSCREEN.PROGRAMS" = "Програми"; -"MAINSCREEN.PROFILE" = "Профіль"; - -"VIEW.SNACKBAR.TRY_AGAIN_BTN" = "Спробувати ще"; - -"ERROR.INVALID_CREDENTIALS" = "Недійсні дані авторизації"; -"ERROR.SLOW_OR_NO_INTERNET_CONNECTION" = "Повільне або відсутнє з’єднання з Інтернетом"; -"ERROR.NO_CACHED_DATA" = "Немає збережених даних для автономного режиму"; -"ERROR.USER_NOT_ACTIVE" = "Обліковий запис користувача не активовано. Будь ласка, спочатку активуйте свій обліковий запис."; -"ERROR.UNKNOWN_ERROR" = "Щось пішло не так"; -"ERROR.WIFI" = "Завантажувати файли можна лише через Wi-Fi. Ви можете змінити це в налаштуваннях."; - -"ERROR.INTERNET.NO_INTERNET_TITLE" = "Немає підключення до Інтернету"; -"ERROR.INTERNET.NO_INTERNET_DESCRIPTION" = "Будь ласка, підключіться до Інтернету, щоб переглянути цей вміст."; - -"COURSEWARE.COURSE_CONTENT" = "Зміст курсу"; -"COURSEWARE.COURSE_CONTENT_NOT_AVAILABLE" = "This interactive component isn't yet available on mobile."; -"COURSEWARE.COURSE_UNITS" = "Модулі"; -"COURSEWARE.NEXT" = "Далі"; -"COURSEWARE.PREVIOUS" = "Назад"; -"COURSEWARE.FINISH" = "Завершити"; -"COURSEWARE.GOOD_WORK" = "Гарна робота!"; -"COURSEWARE.BACK_TO_OUTLINE" = "Повернутись до модуля"; -"COURSEWARE.SECTION_COMPLETED" = "Ви завершили “%@”."; -"COURSEWARE.CONTINUE" = "Продовжити"; -"COURSEWARE.RESUME" = "Resume"; -"COURSEWARE.RESUME_WITH" = "Продовжити далі:"; -"COURSEWARE.NEXT_SECTION" = "Наступний розділ"; - -"COURSEWARE.NEXT_SECTION_DESCRIPTION_FIRST" = "Щоб перейти до “"; -"COURSEWARE.NEXT_SECTION_DESCRIPTION_LAST" = "” натисніть “Наступний розділ”."; - -"ERROR.RELOAD" = "Перезавантажити"; - -"DATE.ENDED" = "Кінець"; -"DATE.START" = "Початок"; -"DATE.STARTED" = "Почався"; -"DATE.JUST_NOW" = "Прямо зараз"; - -"ALERT.ACCEPT" = "ТАК"; -"ALERT.CANCEL" = "СКАСУВАТИ"; -"ALERT.LOGOUT" = "Вийти"; -"ALERT.LEAVE" = "Покинути"; -"ALERT.KEEP_EDITING" = "Залишитись"; -"ALERT.ADD" = "Add"; -"ALERT.REMOVE" = "Remove"; -"ALERT.CALENDAR_SHIFT_PROMPT_REMOVE_COURSE_CALENDAR"="Remove course calendar"; - -"NO_INTERNET.OFFLINE" = "Офлайн режим"; -"NO_INTERNET.DISMISS" = "Сховати"; -"NO_INTERNET.RELOAD" = "Перезавантажити"; - -"DATE_FORMAT.MMMM_dd" = "dd MMMM"; -"DATE_FORMAT.MMM_DD_YYYY" = "dd MMMM yyyy"; - -"DOWNLOAD_MANAGER.DOWNLOAD" = "Скачати"; -"DOWNLOAD_MANAGER.DOWNLOADED" = "Скачано"; -"DOWNLOAD_MANAGER.COMPLETED" = "Завершено"; - -"SETTINGS.VIDEO_DOWNLOAD_QUALITY_TITLE" = "Video download quality"; -"SETTINGS.DOWNLOAD_QUALITY_AUTO_TITLE" = "Auto"; -"SETTINGS.DOWNLOAD_QUALITY_AUTO_DESCRIPTION" = "Recommended"; -"SETTINGS.DOWNLOAD_QUALITY_360_TITLE" = "360p"; -"SETTINGS.DOWNLOAD_QUALITY_360_DESCRIPTION" = "Lower data usage"; -"SETTINGS.DOWNLOAD_QUALITY_540_TITLE" = "540p"; -"SETTINGS.DOWNLOAD_QUALITY_720_TITLE" = "720p"; -"SETTINGS.DOWNLOAD_QUALITY_720_DESCRIPTION" = "Best quality"; - -"DONE" = "Зберегти"; - -"PICKER.SEARCH" = "Знайти"; -"PICKER.ACCEPT" = "Прийняти"; - -"WEBVIEW.ALERT.OK" = "Так"; -"WEBVIEW.ALERT.CANCEL" = "Скасувати"; -"WEBVIEW.ALERT.CONTINUE" = "Continue"; - - -"REVIEW.VOTE_TITLE" = "Вам подобається Open edX?"; -"REVIEW.VOTE_DESCRIPTION" = "Ваш відгук важливий для нас. Можливо, ви візьмете хвилинку, щоб оцінити додаток, натиснувши на зірку нижче? Дякуємо за вашу підтримку!"; -"REVIEW.FEEDBACK_TITLE" = "Залиште відгук"; -"REVIEW.FEEDBACK_DESCRIPTION" = "Нам шкода чути, що ваше навчання мало деякі проблеми. Ми вдячні за будь-який відгук."; -"REVIEW.THANKS_FOR_VOTE_TITLE" = "Дякуємо"; -"REVIEW.THANKS_FOR_VOTE_DESCRIPTION" = "Дякуємо, що поділилися своїми враженнями з нами. Бажаєте залишити свій відгук про цей додаток для інших користувачів в магазині додатків?"; -"REVIEW.THANKS_FOR_FEEDBACK_TITLE" = "Дякуємо"; -"REVIEW.THANKS_FOR_FEEDBACK_DESCRIPTION" = "Ми отримали ваш відгук і використовуватимемо його для покращення вашого навчального досвіду в майбутньому!"; -"REVIEW.BETTER" = "Що можна було б зробити краще?"; -"REVIEW.NOT_NOW" = "Не зараз"; - -"REVIEW.BUTTON.SUBMIT" = "Надіслати"; -"REVIEW.BUTTON.SHARE_FEEDBACK" = "Поділитися відгуком"; -"REVIEW.BUTTON.RATE_US" = "Оцінити нас"; -"REVIEW.EMAIL.TITLE" = "Виберіть поштового клієнта:"; - -"SOCIAL_SIGN_CANCELED" = "The user canceled the sign-in flow."; -"AUTHORIZATION_FAILED" = "Authorization failed."; - -"SIGN_IN.LOG_IN_BTN" = "Увійти"; -"REGISTER" = "Реєстрація"; - -"TOMORROW" = "Tomorrow"; -"YESTERDAY" = "Yesterday"; - -"OPEN_IN_BROWSER"="View in Safari"; diff --git a/Course/Course/uk.lproj/Localizable.strings b/Course/Course/uk.lproj/Localizable.strings deleted file mode 100644 index 59a57991a..000000000 --- a/Course/Course/uk.lproj/Localizable.strings +++ /dev/null @@ -1,121 +0,0 @@ -/* - Localizable.strings - Course - - Created by  Stepanok Ivan on 26.09.2022. - -*/ - -"OUTLINE.PASSED_THE_COURSE" = "Вітаємо, ви отримали сертифікат курсу “%@\.“"; -"OUTLINE.VIEW_CERTIFICATE" = "Переглянути сертифікат"; -"OUTLINE.CERTIFICATE" = "Сертифікат"; -"OUTLINE.COURSE_VIDEOS" = "Відео з курсу"; - -"COURSEWARE.COURSE_CONTENT" = "Зміст курсу"; -"COURSEWARE.COURSE_UNITS" = "Модулі"; -"COURSEWARE.NEXT" = "Далі"; -"COURSEWARE.PREVIOUS" = "Назад"; -"COURSEWARE.FINISH" = "Завершити"; -"COURSEWARE.GOOD_WORK" = "Гарна робота!"; -"COURSEWARE.BACK_TO_OUTLINE" = "Повернутись до модуля"; -"COURSEWARE.SECTION" = "Секція “"; -"COURSEWARE.IS_FINISHED" = "“ завершена."; -"COURSEWARE.CONTINUE" = "Продовжити"; -"COURSEWARE.RESUME_WITH" = "Продовжити далі:"; - -"ERROR.NO_INTERNET" = "Ви не підключені до Інтернету. Перевірте підключення до Інтернету і спробуйте ще."; -"ERROR.RELOAD" = "Перезавантажити"; -"ERROR.COMPONENT_NOT_FOUNT" = "Course component not found, please reload"; -"ERROR.NO_HANDOUTS" = "There are currently no handouts for this course"; - -"ALERT.ROTATE_DEVICE" = "Поверніть пристрій, щоб переглянути це відео на весь екран."; -"ALERT.ACCEPT" = "Accept"; -"ALERT.DELETE_ALL_VIDEOS" = "Are you sure you want to delete all video(s) for"; -"ALERT.DELETE_VIDEOS" = "Are you sure you want to delete video(s) for"; -"ALERT.STOP_DOWNLOADING" = "Turning off the switch will stop downloading and delete all downloaded videos for"; - -"COURSE_CONTAINER.COURSE" = "Курс"; -"COURSE_CONTAINER.VIDEOS" = "Всі відео"; -"COURSE_CONTAINER.DATES" = "Dates"; -"COURSE_CONTAINER.DISCUSSIONS" = "Дискусії"; -"COURSE_CONTAINER.HANDOUTS" = "Матеріали"; -"COURSE_CONTAINER.HANDOUTS_IN_DEVELOPING" = "Матеріали в процесі розробки"; - -"HANDOUTS_CELL_HANDOUTS.TITLE" = "Нотатки"; -"HANDOUTS_CELL_ANNOUNCEMENTS.TITLE" = "Оголошення"; -"HANDOUTS_CELL_HANDOUTS.DESCRIPTION" = "Знайдіть важливу інформацію про курс"; -"HANDOUTS_CELL_ANNOUNCEMENTS.DESCRIPTION" = "Будьте в курсі останніх новин"; - -"NOT_AVALIABLE.TITLE" = "Цей інтерактивний компонент не доступний"; -"NOT_AVALIABLE.DESCRIPTION" = "Досліджуйте інші частини цього курсу або перегляньте цю в Браузері."; -"NOT_AVALIABLE.BUTTON" = "Відкрити в браузері"; - -"SUBTITLES.TITLE" = "Субтитри"; - -"ACCESSIBILITY.DOWNLOAD" = "Скачати"; -"ACCESSIBILITY.CANCEL_DOWNLOAD" = "Скасувати завантаження"; -"ACCESSIBILITY.DELETE_DOWNLOAD" = "Видалити файл"; - -"DOWNLOAD.DOWNLOADS" = "Downloads"; -"DOWNLOAD.DOWNLOAD" = "Download"; -"DOWNLOAD.ALL_VIDEOS_DOWNLOADED" = "All videos downloaded"; -"DOWNLOAD.DOWNLOADING_VIDEOS" = "Downloading videos..."; -"DOWNLOAD.DOWNLOAD_TO_DEVICE" = "Download to device"; -"DOWNLOAD.VIDEOS" = "Videos"; -"DOWNLOAD.REMAINING" = "Remaining"; -"DOWNLOAD.UNTITLED"= "Untitled"; -"DOWNLOAD.TOTAL"= "Total"; - -"DOWNLOAD.CHANGE_QUALITY_ALERT" = "You cannot change the download video quality when all videos are downloading"; -"DOWNLOAD.DOWNLOAD_LARGE_FILE_MESSAGE" = "The videos you've selected are larger than 1 GB. Do you want to download these videos?"; -"DOWNLOAD.NO_WIFI_MESSAGE" = "Your current download settings only allow downloads over Wi-Fi.\nPlease connect to a Wi-Fi network or change your download settings."; - -"COURSE_DATES.TODAY" = "Today"; -"COURSE_DATES.COMPLETED" = "Completed"; -"COURSE_DATES.PAST_DUE" = "Past due"; -"COURSE_DATES.DUE_NEXT" = "Due next"; -"COURSE_DATES.UNRELEASED" = "Unreleased"; -"COURSE_DATES.VERIFIED_ONLY" = "Verified Only"; -"COURSE_DATES.ITEMS_HIDDEN" = "Items Hidden"; -"COURSE_DATES.ITEM_HIDDEN" = "Item Hidden"; -"COURSE_DATES.TOAST_SUCCESS_TITLE" = "Due dates shifted"; -"COURSE_DATES.TOAST_SUCCESS_MESSAGE" = "Your due dates have been successfully shifted to help you stay on track."; -"COURSE_DATES.VIEW_ALL_DATES" = "View all dates"; -"COURSE_DATES.SYNC_TO_CALENDAR" = "Sync to calendar"; -"COURSE_DATES.SYNC_TO_CALENDAR_MESSAGE" = "Automatically sync all deadlines and due dates for this course to your calendar."; -"COURSE_DATES.ADD_CALENDAR_TITLE"="Add calendar"; -"COURSE_DATES.REMOVE_CALENDAR_TITLE"="Remove calendar"; -"COURSE_DATES.ADD_CALENDAR_PROMPT"="Would you like to add the %@ calendar \"%@\" ? \n You can edit or remove the course calendar any time in Calendar or Settings"; -"COURSE_DATES.REMOVE_CALENDAR_PROMPT"="Would you like to remove the %@ calendar \"%@\" ?"; -"COURSE_DATES.DATES_ADDED_ALERT_MESSAGE" = "\"%@\" has been added to your calendar."; -"COURSE_DATES.CALENDAR_SYNC_MESSAGE"="Syncing calendar..."; -"COURSE_DATES.CALENDAR_VIEW_EVENTS"="View Events"; -"COURSE_DATES.CALENDAR_EVENTS_ADDED"="Your course calendar has been added."; -"COURSE_DATES.CALENDAR_EVENTS_REMOVED"="Your course calendar has been removed."; -"COURSE_DATES.CALENDAR_EVENTS"="Calendar events"; -"COURSE_DATES.CALENDAR_OUT_OF_DATE"="Your course calendar is out of date"; -"COURSE_DATES.CALENDAR_SHIFT_MESSAGE"="Your course dates have been shifted and your course calendar is no longer up to date with your new schedule."; -"COURSE_DATES.CALENDAR_SHIFT_PROMPT_UPDATE_NOW"="Update now"; -"COURSE_DATES.CALENDAR_EVENTS_UPDATED"="Your course calendar has been updated."; -"COURSE_DATES.CALENDAR_PERMISSION_NOT_DETERMINED"="%@ does not have calendar permission. Please go to settings and give calender permission."; -"COURSE_DATES.OPEN_SETTINGS"="Open Settings"; -"COURSE_DATES.SETTINGS" = "Settings"; - -"COURSE_DATES.RESET_DATE.RESET_DATE_BANNER.BODY" = "Don't worry - shift our suggested schedule to complete past due assignments without losing any progress."; -"COURSE_DATES.RESET_DATE.RESET_DATE_BANNER.BUTTON" = "Shift due dates"; -"COURSE_DATES.RESET_DATE.RESET_DATE_BANNER.HEADER" = "Missed some deadlines?"; - -"COURSE_DATES.RESET_DATE.TAB_INFO_BANNER.BODY" = "We built a suggested schedule to help you stay on track. But don’t worry – it’s flexible so you can learn at your own pace. If you happen to fall behind, you’ll be able to adjust the dates to keep yourself on track."; -"COURSE_DATES.RESET_DATE.TAB_INFO_BANNER.HEADER" = ""; - -"COURSE_DATES.RESET_DATE.UPGRADE_TO_COMPLETE_GRADED_BANNER.BODY" = "To complete graded assignments as part of this course, you can upgrade today."; -"COURSE_DATES.RESET_DATE.UPGRADE_TO_COMPLETE_GRADED_BANNER.BUTTON" = ""; -"COURSE_DATES.RESET_DATE.UPGRADE_TO_COMPLETE_GRADED_BANNER.HEADER" = ""; - -"COURSE_DATES.RESET_DATE.UPGRADE_TO_RESET_BANNER.BODY" = "You are auditing this course, which means that you are unable to participate in graded assignments. It looks like you missed some important deadlines based on our suggested schedule. To complete graded assignments as part of this course and shift the past due assignments into the future, you can upgrade today."; -"COURSE_DATES.RESET_DATE.UPGRADE_TO_RESET_BANNER.BUTTON" = ""; -"COURSE_DATES.RESET_DATE.UPGRADE_TO_RESET_BANNER.HEADER" = ""; - -"COURSE_DATES.RESET_DATE.ERROR_MESSAGE" = "Your dates could not be shifted. Please try again."; -"COURSE_DATES.RESET_DATE.SUCCESS_MESSAGE" = "Your dates have been successfully shifted."; -"COURSE_DATES.RESET_DATE.TITLE" = "Course Dates"; diff --git a/Dashboard/Dashboard/uk.lproj/Localizable.strings b/Dashboard/Dashboard/uk.lproj/Localizable.strings deleted file mode 100644 index 748f2c021..000000000 --- a/Dashboard/Dashboard/uk.lproj/Localizable.strings +++ /dev/null @@ -1,14 +0,0 @@ -/* - Localizable.strings - Dashboard - - Created by  Stepanok Ivan on 20.09.2022. - -*/ - -"TITLE" = "Мої курси"; -"HEADER.COURSES" = "Курси"; -"HEADER.WELCOME_BACK" = "З поверненням. Давайте продовжимо вчитись."; - -"EMPTY.TITLE" = "Нічого немає"; -"EMPTY.SUBTITLE" = "Ви не підписані на жодний курс."; diff --git a/Discovery/Discovery/uk.lproj/Localizable.strings b/Discovery/Discovery/uk.lproj/Localizable.strings deleted file mode 100644 index 25f73bf53..000000000 --- a/Discovery/Discovery/uk.lproj/Localizable.strings +++ /dev/null @@ -1,37 +0,0 @@ -/* - Localizable.strings - Discovery - - Created by  Stepanok Ivan on 19.09.2022. - -*/ - -"TITLE" = "Всі курси"; -"SEARCH" = "Пошук"; -"HEADER.TITLE_1" = "Всі курси"; -"HEADER.TITLE_2" = "Давайте знайдемо нові курси для вас"; - -"SEARCH.TITLE" = "Результати пошуку"; -"SEARCH.EMPTY_DESCRIPTION" = "Почніть вводити текст, щоб знайти курс"; - -"UPDATE_REQUIRED_TITLE" = "Потрібне оновлення додатка"; -"UPDATE_REQUIRED_DESCRIPTION" = "Ця версія додатка OpenEdX застаріла. Щоб продовжити навчання та отримати останні функції та виправлення, оновіться до останньої версії."; -"UPDATE_WHY_NEED" = "Чому я маю оновити програму?"; -"UPDATE_DEPRECATED_APP" = "Застаріла версія додатка"; -"UPDATE_BUTTON" = "Оновити"; -"UPDATE_ACCOUNT_SETTINGS" = "Налаштування"; - -"UPDATE_NEEDED_TITLE" = "Оновлення додатку"; -"UPDATE_NEEDED_DESCRIPTION" = "Ми рекомендуємо вам оновити додаток до останньої версії. Оновіть зараз, щоб отримати нові функції та виправлення."; -"UPDATE_NEEDED_NOT_NOW" = "Не зараз"; - -"UPDATE_NEW_AVALIABLE" = "Доступне нове оновлення! Оновіть зараз, щоб отримати найновіші функції та виправлення"; - -"ALERT.LEAVING_APP_TITLE" = "Leaving the app"; -"ALERT.LEAVING_APP_MESSAGE" = "You are now leaving the app and opening a browser"; - -"DETAILS.TITLE" = "Деталі курсу"; -"DETAILS.VIEW_COURSE" = "Переглянути курс"; -"DETAILS.ENROLL_NOW" = "Зареєструватися"; -"DETAILS.ENROLLMENT_DATE_IS_OVER" = "Ви не можете зареєструватися на цей курс, оскільки дата реєстрації закінчилася."; -"DETAILS.ENROLLMENT_NO_INTERNET" = "Щоб зареєструватися на цьому курсі, переконайтеся, що ви підключені до Інтернету."; diff --git a/Discussion/Discussion/uk.lproj/Localizable.strings b/Discussion/Discussion/uk.lproj/Localizable.strings deleted file mode 100644 index fc5223c14..000000000 --- a/Discussion/Discussion/uk.lproj/Localizable.strings +++ /dev/null @@ -1,59 +0,0 @@ -/* - Localizable.strings - Discussion - - Created by  Stepanok Ivan on 12.10.2022. - -*/ - -"TITLE" = "Дискусії"; -"BANNER.DISCUSSIONS_IS_DISABLED" = "Posting in discussions is disabled by the course team"; - -"TOPICS.SEARCH" = "Пошук по всім постам"; -"TOPICS.ALL_POSTS" = "Всі пости"; -"TOPICS.POST_IM_FOLLOWING" = "Улюблені пости"; -"TOPICS.MAIN_CATEGORIES" = "Основні категорії"; -"TOPICS.UNNAMED" = "Unnamed subcategory"; - -"POSTS.SORT.RECENT_ACTIVITY" = "Остання активність"; -"POSTS.SORT.MOST_ACTIVITY" = "Найактивниші"; -"POSTS.SORT.MOST_VOTES" = "Найбільше голосів"; - -"POSTS.FILTER.ALL_POSTS" = "Всі пости"; -"POSTS.FILTER.UNREAD" = "Непрочитаних"; -"POSTS.FILTER.UNANSWERED" = "Без відповіді"; -"POSTS.NO_DISCUSSION.TITLE" = "Ще немає дискусій"; -"POSTS.NO_DISCUSSION.DESCRIPTION" = "Натисніть кнопку нижче, щоб створити свою першу дискусію."; -"POSTS.NO_DISCUSSION.CREATEBUTTON" = "Створити дискусію"; -"POSTS.NO_DISCUSSION.ADD_POST" = "Add a post"; - -"POSTS.CREATE_NEW_POST" = "Створити новий пост"; -"POSTS.ALERT.MAKE_SELECTION" = "Оберіть"; - -"POST.LAST_POST" = "Останній пост:"; - -"THREAD.ALERT.COMMENT_ADDED" = "Коментарій додано"; -"THREAD.ADD_RESPONSE" = "Додати відповідь"; - -"CREATE_THREAD.NEW_POST" = "Створити новий пост"; -"CREATE_THREAD.SELECT_POST_TYPE" = "Обрати тип посту"; -"CREATE_THREAD.TOPIC" = "Тема"; -"CREATE_THREAD.TITLE" = "Назва"; -"CREATE_THREAD.FOLLOW_DISCUSSION" = "Підписатись на дискусію"; -"CREATE_THREAD.FOLLOW_QUESTION" = "Підписатись на питання"; -"CREATE_THREAD.CREATE_DISCUSSION" = "Створити нову дискусію"; -"CREATE_THREAD.CREATE_QUESTION" = "Створити нове питання"; - -"COMMENT.REPORT" = "Поскаржитись"; -"COMMENT.UNREPORT" = "Зняти скаргу"; -"COMMENT.FOLLOW" = "Слідкувати"; -"COMMENT.UNFOLLOW" = "Не слідкувати"; - -"RESPONSE.COMMENTS_RESPONSES" = "Коментарій"; -"RESPONSE.ALERT.COMMENT_ADDED" = "Коментарій додано"; -"RESPONSE.ADD_COMMENT" = "Додати коментарій"; - -"POST_TYPE.QUESTION" = "питання"; -"POST_TYPE.DISCUSSION" = "дискусія"; - -"ANONYMOUS" = "Анонім"; diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..a2545a73b --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +clean_translations_temp_directory: + rm -rf I18N/ + +create_virtual_env: + rm -rf .venv + python3 -m venv .venv + # TODO: Publish new version on pypi as `python3-localizable` + . .venv/bin/activate && pip install git+https://github.com/Amr-Nash/python-localizable.git + . .venv/bin/activate && pip install openedx-atlas + +pull_translations: clean_translations_temp_directory create_virtual_env + . .venv/bin/activate && atlas pull $(ATLAS_OPTIONS) translations/openedx-app-ios/I18N:I18N + . .venv/bin/activate && python i18n_scripts/translation_script.py --split + make clean_translations_temp_directory + + +combine_translations: clean_translations_temp_directory create_virtual_env + . .venv/bin/activate && python i18n_scripts/translation_script.py --combine diff --git a/OpenEdX/uk.lproj/Localizable.strings b/OpenEdX/uk.lproj/Localizable.strings deleted file mode 100644 index 8e7d62729..000000000 --- a/OpenEdX/uk.lproj/Localizable.strings +++ /dev/null @@ -1,7 +0,0 @@ -/* - Localizable.strings - OpenEdX - - Created by Vladimir Chekyrta on 13.09.2022. - -*/ diff --git a/Profile/Profile/uk.lproj/Localizable.strings b/Profile/Profile/uk.lproj/Localizable.strings deleted file mode 100644 index f0e4d0503..000000000 --- a/Profile/Profile/uk.lproj/Localizable.strings +++ /dev/null @@ -1,80 +0,0 @@ -/* - Localizable.strings - Profile - - Created by  Stepanok Ivan on 23.09.2022. - -*/ - -"TITLE" = "Профіль"; -"INFO" = "Дані профілю"; -"EDIT_PROFILE" = "Редагування"; -"YEAR_OF_BIRTH" = "Рік народження:"; -"BIO" = "Біо:"; -"SETTINGS" = "Налаштування"; -"SETTINGS_VIDEO" = "Налаштування відео"; -"SUPPORT_INFO" = "Інформація про підтримку"; -"CONTACT" = "Cлужби підтримки"; -"TERMS" = "Умови використання"; -"PRIVACY" = "Політика конфіденційності"; -"COOKIE_POLICY" = "Cookie policy"; -"DO_NOT_SELL_INFORMATION" = "Do not sell my personal information"; -"FAQ_TITLE" = "View FAQ"; -"LOGOUT" = "Вийти"; -"SWITCH_TO" = "Переключити на"; -"FULL_PROFILE" = "повний профіль"; -"LIMITED_PROFILE" = "обмежений профіль"; - -"LOGOUT_ALERT.TITLE" = "Підтвердження виходу"; -"LOGOUT_ALERT.TEXT" = "Ви впевнені, що бажаєте вийти?"; - -"DELETE_ALERT.TITLE" = "Увага!"; -"DELETE_ALERT.TEXT" = "Ви дійсно хочете видалити свій обліковий запис?"; - -"UNSAVED_DATA_ALERT.TITLE" = "Є незбережені дані"; -"UNSAVED_DATA_ALERT.TEXT" = "Ви дійсно хочете вийти без збереження?"; - -"EDIT.TOO_YONG_USER" = "Вам має бути більше 13 років, щоб мати профіль із повним доступом до інформації."; -"EDIT.LIMITED_PROFILE_DESCRIPTION" = "В обмеженому профілі доступні лише ваше ім’я користувача."; -"EDIT.DELETE_ACCOUNT" = "Видалити акаунт"; - -"EDIT.FIELDS.YEAR_OF_BIRTH" = "Рік народження:"; -"EDIT.FIELDS.LOCATION" = "Країна"; -"EDIT.FIELDS.SPOKEN_LANGUGAE" = "Мова спілкування"; -"EDIT.FIELDS.ABOUT_ME" = "Про мене:"; - -"EDIT.BOTTOM_SHEET.TITLE" = "Змінити фото профілю"; -"EDIT.BOTTOM_SHEET.SELECT" = "Обрати із галереї"; -"EDIT.BOTTOM_SHEET.REMOVE" = "Видалити зображення"; -"EDIT.BOTTOM_SHEET.CANCEL" = "Скасувати"; - -"DELETE_ACCOUNT.TITLE" = "Видалення акаунту"; -"DELETE_ACCOUNT.ARE_YOU_SURE" = "Ви впевнені, що хочете "; -"DELETE_ACCOUNT.WANT_TO_DELETE" = "видалити свій обліковий запис?"; -"DELETE_ACCOUNT.DESCRIPTION" = "Для підтвердження цієї дії необхідно ввести пароль свого облікового запису."; -"DELETE_ACCOUNT.PASSWORD" = "Пароль"; -"DELETE_ACCOUNT.PASSWORD_DESCRIPTION" = "Введіть пароль"; -"DELETE_ACCOUNT.COMFIRM" = "Так, видалити акаунт"; -"DELETE_ACCOUNT.BACK_TO_PROFILE" = "Повернутись до профілю"; -"DELETE_ACCOUNT.INCORRECT_PASSWORD" = "Пароль неправильний. Будь ласка спробуйте ще раз."; - -"SETTINGS.VIDEO_SETTINGS_TITLE" = "Налаштування відео"; -"SETTINGS.WIFI_TITLE" = "Тільки Wi-fi"; -"SETTINGS.WIFI_DESCRIPTION" = "Завантажувати відео, лише коли Wi-Fi увімкнено"; -"SETTINGS.VIDEO_QUALITY_TITLE" = "Якість потокового відео"; -"SETTINGS.VIDEO_QUALITY_DESCRIPTION" = "Авто (Рекомендовано)"; - -"SETTINGS.QUALITY_AUTO_TITLE" = "Авто"; -"SETTINGS.QUALITY_AUTO_DESCRIPTION" = "Рекомендовано"; -"SETTINGS.QUALITY_360_TITLE" = "360p"; -"SETTINGS.QUALITY_360_DESCRIPTION" = "економія трафіку"; -"SETTINGS.QUALITY_540_TITLE" = "540p"; -"SETTINGS.QUALITY_720_TITLE" = "720p"; -"SETTINGS.QUALITY_AUTO_DESCRIPTION" = "Найкраща якість"; - -"SETTINGS.VERSION" = "Версія:"; -"SETTINGS.UP_TO_DATE" = "Оновлено"; -"SETTINGS.TAP_TO_UPDATE" = "Клацніть, щоб оновити до версії"; -"SETTINGS.TAP_TO_INSTALL" = "Клацніть, щоб встановити обов'язкове оновлення програми"; - -"ERROR.CANNOT_SEND_EMAIL" = "Cannot send email. It seems your email client is not set up."; diff --git a/WhatsNew/WhatsNew/uk.lproj/Localizable.strings b/WhatsNew/WhatsNew/uk.lproj/Localizable.strings deleted file mode 100644 index a0194425c..000000000 --- a/WhatsNew/WhatsNew/uk.lproj/Localizable.strings +++ /dev/null @@ -1,12 +0,0 @@ -/* - Localizable.strings - WhatsNew - - Created by  Stepanok Ivan on 18.10.2023. - -*/ - -"TITLE" = "Що нового"; -"BUTTON_PREVIOUS" = "Назад"; -"BUTTON_NEXT" = "Далі"; -"BUTTON_DONE" = "Завершити"; diff --git a/i18n_scripts/translation_script.py b/i18n_scripts/translation_script.py new file mode 100644 index 000000000..12568f824 --- /dev/null +++ b/i18n_scripts/translation_script.py @@ -0,0 +1,277 @@ +""" +This script contains the necessary methods to accomplish two functions: + 1- Combine the English translations from all modules in the repository to the I18N directory. After the English + translation is combined, it will be pushed to the openedx-translations repository as described in OEP-58. + 2- Split all other Languages. After pulling the translations from the openedx-translations repository via atlas pull, + there will be a single strings file for each language, the "split_translation_files" method will run through each + language file in the I18N directory and split it into the modules. +""" + +import os +import localizable +from collections import OrderedDict +import argparse + + +def parse_arguments(): + """ + This function is the argument parser for this script. + The script takes only one of the two arguments --split or --combine as indicated below. + """ + parser = argparse.ArgumentParser(description='Split or combine translations.') + parser.add_argument('--split', action='store_true', + help='Split translations into separate files for each module and language.') + parser.add_argument('--combine', action='store_true', + help='Combine the English translations from all modules into a single file.') + return parser.parse_args() + + +def get_translation_file_path(modules_dir, module): + """ + Retrieves the path of the translation file from the module name + + Parameters: + modules_dir (str): The path to the directory containing all the modules. + module (str): The module's name that we want its translation. + + Returns: + file_path (str): The module's translation path. + """ + translation_file = os.path.join(modules_dir, module, module, 'en.lproj', 'Localizable.strings') + return translation_file + + +def get_modules_to_translate(modules_dir): + """ + Retrieve the names of modules that have translation files for a specified language. + + Parameters: + modules_dir (str): The path to the directory containing all the modules. + + Returns: + list of str: A list of module names that have translation files for the specified language. + """ + dirs = [ + directory for directory in os.listdir(modules_dir) + if os.path.isdir(os.path.join(modules_dir, directory)) + ] + + modules_list = [] + for module in dirs: + translation_file = get_translation_file_path(modules_dir, module) + if os.path.isfile(translation_file): + modules_list.append(module) + return modules_list + + +def get_translations(modules_dir): + """ + Retrieve the translations from all specified modules as OrderedDict. + + Parameters: + modules_dir (str): The directory containing the modules. + + Returns: + OrderedDict of dict: An ordered dict of dictionaries containing the 'key', 'value', and 'comment' for each + translation line. The key of the outer OrderedDict consists of the value of the translation key combined with + the name of the module containing the translation. + """ + ordered_dict_of_translations = OrderedDict() + modules = get_modules_to_translate(modules_dir) + for module in modules: + translation_file = get_translation_file_path(modules_dir, module) + module_translations = localizable.parse_strings(filename=translation_file) + + for entry in module_translations: + key_with_module = f"{module}.{entry['key']}" + ordered_dict_of_translations[key_with_module] = entry + + return ordered_dict_of_translations + + +def write_combined_translation_file(modules_dir, content_ordered_dict): + """ + Write the contents of an ordered dictionary to a Localizable.strings file. + + This function takes an ordered dictionary containing translation data and writes it to a Localizable.strings + file located in the 'I18N/en.lproj' directory within the specified modules directory. It creates the directory + if it doesn't exist. + + Parameters: + modules_dir (str): The path to the modules directory + where the I18N directory will be written. + content_ordered_dict (OrderedDict): An ordered dictionary containing translation data. The keys + are the translation keys, and the values are dictionaries with 'value' and 'comment' keys representing the + translation value and optional comments, respectively. + """ + combined_translation_dir = os.path.join(modules_dir, 'I18N', 'en.lproj') + os.makedirs(combined_translation_dir, exist_ok=True) + with open(os.path.join(combined_translation_dir, 'Localizable.strings'), 'w') as f: + for key, value in content_ordered_dict.items(): + write_line_and_comment(f, value, key=key) + + +def combine_translation_files(modules_dir=None): + """ + Combine translation files from different modules into a single file. + """ + if not modules_dir: + modules_dir = os.path.dirname(os.path.dirname(__file__)) + combined_translation_dict = get_translations(modules_dir) + write_combined_translation_file(modules_dir, combined_translation_dict) + + +def get_languages_dirs(modules_dir): + """ + Retrieve directories containing language files for translation. + + Args: + modules_dir (str): The directory containing all the modules. + + Returns: + list: A list of directories containing language files for translation. Each directory represents + a specific language and ends with the '.lproj' extension. + + Example: + Input: + get_languages_dirs('/path/to/modules') + Output: + ['ar.lproj', 'uk.lproj', ...] + """ + lang_parent_dir = os.path.join(modules_dir, 'I18N') + languages_dirs = [ + directory for directory in os.listdir(lang_parent_dir) + if directory.endswith('.lproj') and directory != "en.lproj" + ] + return languages_dirs + + +def separate_translation_to_modules(modules_dir, lang_dir): + """ + Separate translations from a translation file into modules. + + Args: + modules_dir (str): The directory containing all the modules. + lang_dir (str): The directory containing the translation file being split. + + Returns: + dict: A dictionary containing translations split by module. The keys are module names, + and the values are lists of dictionaries, each containing the 'key', 'value', and 'comment' + for each translation entry within the module. + + Example: + Input: + separate_translation_to_modules('/path/to/modules', 'uk.lproj') + Output: + { + 'module1': [ + {'key': 'translation_key', 'value': 'translation_value', 'comment': 'translation_comment'}, + ... + ], + 'module2': [ + ... + ], + ... + } + """ + translations = {} + file_path = os.path.join(modules_dir, 'I18N', lang_dir, 'Localizable.strings') + lang_list = localizable.parse_strings(filename=file_path) + for translation_entry in lang_list: + module_name, key_remainder = translation_entry['key'].split('.', maxsplit=1) + split_entry = { + 'key': key_remainder, + 'value': translation_entry['value'], + 'comment': translation_entry['comment'] + } + translations.setdefault(module_name, []).append(split_entry) + return translations + + +def write_translations_to_modules(modules_dir, lang_dir, modules_translations): + """ + Write translations to language files for each module. + + Args: + modules_dir (str): The directory containing all the modules. + lang_dir (str): The directory of the translation file being written. + modules_translations (dict): A dictionary containing translations for each module. + + Returns: + None + """ + for module, translation_list in modules_translations.items(): + combined_translation_dir = os.path.join(modules_dir, module, module, lang_dir) + os.makedirs(combined_translation_dir, exist_ok=True) + with open(os.path.join(combined_translation_dir, 'Localizable.strings'), 'w') as f: + for translation_entry in translation_list: + write_line_and_comment(f, translation_entry) + + +def _escape(s): + """ + Reverse the replacements performed by _unescape() in the localizable library + """ + s = s.replace('\n', r'\n').replace('\r', r'\r').replace('"', r'\"') + return s + + +def write_line_and_comment(file, entry, key=None): + """ + Write a translation line with an optional comment to a file. + + Args: + file (file object): The file object to write to. + entry (dict): A dictionary containing the translation entry with 'key', 'value', and optional 'comment'. + key (str, optional): The key to use in the translation line. If not provided, 'entry["key"]' is used. + + Returns: + None + """ + comment = entry.get('comment') # Retrieve the comment, if present + key = key if key else entry['key'] + if comment: + file.write(f"/* {comment} */\n") + file.write(f'"{key}" = "{_escape(entry["value"])}";\n') + + +def split_translation_files(modules_dir=None): + """ + Split translation files into separate files for each module and language. + + Args: + modules_dir (str, optional): The directory containing all the modules. If not provided, + it defaults to the parent directory of the directory containing this script. + + Returns: + None + + Example: + split_translation_files('/path/to/modules') + """ + if not modules_dir: + modules_dir = os.path.dirname(os.path.dirname(__file__)) + languages_dirs = get_languages_dirs(modules_dir) + for lang_dir in languages_dirs: + translations = separate_translation_to_modules(modules_dir, lang_dir) + write_translations_to_modules(modules_dir, lang_dir, translations) + + +def main(): + args = parse_arguments() + if args.split and args.combine: + # Call the function to split translations + print("You can specify either --split or --combine.") + elif args.split: + # Call the function to split translations + split_translation_files() + elif args.combine: + # Call the function to combine translations + combine_translation_files() + else: + print("Please specify either --split or --combine.") + + +if __name__ == "__main__": + main() +