-
Notifications
You must be signed in to change notification settings - Fork 1k
v3.2.2 #405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rongxin-liu
wants to merge
14
commits into
main
Choose a base branch
from
develop
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
v3.2.2 #405
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2f867c2
vietnamese support
bxngyn b2ebb8c
add authentication flag and debug support
ivanharvard e836ff1
improve error handling for GitHub authentication failures in codespaces
rongxin-liu 50c0d5f
Merge pull request #404 from cs50/feat/add-auth-method-flags
rongxin-liu 6ee8c4b
bump version to 3.2.2
rongxin-liu f4ee8f0
add compiled file
bxngyn 0e5d03f
simplified error handling for GitHub authentication
rongxin-liu 20ac530
Merge pull request #403 from cs50/feat/issue-41-internationalize
rongxin-liu cab12d6
Merge branch 'main' into develop
rongxin-liu 9781b47
update workflow to set output for version using new syntax
rongxin-liu 215b870
update GitHub Actions workflow to use latest versions of checkout, se…
rongxin-liu 24a67d3
require lib50>=3.1.2 and improve forced-auth error handling
rongxin-liu f056333
complete vietnamese catalog and fix concatenated msgstr spacing
rongxin-liu 1327b9c
add smoke tests and run them against the minimum lib50 in CI
rongxin-liu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
Binary file not shown.
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| # Vietnamese translations for submit50. | ||
| # Copyright (C) 2025 ORGANIZATION | ||
| # This file is distributed under the same license as the submit50 project. | ||
| # FIRST AUTHOR <EMAIL@ADDRESS>, 2025. | ||
| # | ||
| msgid "" | ||
| msgstr "" | ||
| "Project-Id-Version: submit50 3.2.2\n" | ||
| "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||
| "POT-Creation-Date: 2026-09-04 23:47-0400\n" | ||
| "PO-Revision-Date: 2026-09-04 23:50-0400\n" | ||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||
| "Language: vi\n" | ||
| "Language-Team: vi <LL@li.org>\n" | ||
| "Plural-Forms: nplurals=1; plural=0;\n" | ||
| "MIME-Version: 1.0\n" | ||
| "Content-Type: text/plain; charset=utf-8\n" | ||
| "Content-Transfer-Encoding: 8bit\n" | ||
| "Generated-By: Babel 2.18.0\n" | ||
|
|
||
| #: submit50/__main__.py:71 | ||
| msgid "" | ||
| "Could not connect to submit.cs50.io. Please visit our status page " | ||
| "https://cs50.statuspage.io for more information." | ||
| msgstr "" | ||
| "Không kết nối đến submit.cs50.io được. Vui lòng xem " | ||
| "https://cs50.statuspage.io để biết thêm thông tin." | ||
|
|
||
| #: submit50/__main__.py:135 | ||
| msgid "Files that will be submitted:" | ||
| msgstr "Các tập tin sẽ nộp:" | ||
|
|
||
| #: submit50/__main__.py:139 | ||
| msgid "No files in this directory are expected for submission." | ||
| msgstr "Không có tập tin nào trong thư mục này để nộp." | ||
|
|
||
| #: submit50/__main__.py:143 | ||
| msgid "Files that won't be submitted:" | ||
| msgstr "Các tập tin sẽ không nộp:" | ||
|
|
||
| #: submit50/__main__.py:156 | ||
| msgid "" | ||
| "Keeping in mind the course's policy on academic honesty, including its " | ||
| "restrictions on AI use, are you sure you want to submit these files " | ||
| "(yes/no)? " | ||
| msgstr "" | ||
| "Cân nhắc quy định về tính trung thực trong học thuật, bao gồm cả các hạn " | ||
| "chế về việc sử dụng AI, bạn có chắc chắn muốn nộp các tập tin này không " | ||
| "(có/không)? " | ||
|
|
||
| #: submit50/__main__.py:174 submit50/__main__.py:194 | ||
| msgid "y|yes" | ||
| msgstr "c|co|có" | ||
|
|
||
| #: submit50/__main__.py:189 | ||
| msgid "" | ||
| "You are submitting to a previous year's CS50x course. Your submission " | ||
| "will not be counted towards this year's course." | ||
| msgstr "" | ||
| "Bạn đang nộp bài cho khóa học CS50x của năm trước. Bài này sẽ không được " | ||
| "tính vào khóa học năm nay." | ||
|
|
||
| #: submit50/__main__.py:190 | ||
| msgid "" | ||
| "If you are looking to submit to this year's course, please use the " | ||
| "following slug:" | ||
| msgstr "Nếu bạn muốn nộp bài cho khóa học năm nay, vui lòng sử dụng slug này:" | ||
|
|
||
| #: submit50/__main__.py:194 | ||
| msgid "Do you want to continue with this submission (yes/no)? " | ||
| msgstr "Bạn có muốn tiếp tục nộp bài này không (có/không)? " | ||
|
|
||
| #: submit50/__main__.py:195 | ||
| msgid "User aborted submission." | ||
| msgstr "Người dùng đã hủy nộp bài." | ||
|
|
||
| #: submit50/__main__.py:207 | ||
| msgid "" | ||
| "Sorry, something's wrong, please try again. If the problem persists, " | ||
| "please visit our status page https://cs50.statuspage.io for more " | ||
| "information." | ||
| msgstr "" | ||
| "Rất tiếc, có gì xảy ra, vui lòng thử nộp lại. Nếu vấn đề này còn tiếp " | ||
| "nữa, vui lòng xem https://cs50.statuspage.io để biết thêm thông tin." | ||
|
|
||
| #: submit50/__main__.py:212 | ||
| msgid "Submission cancelled." | ||
| msgstr "Đã hủy nộp bài." | ||
|
|
||
| #: submit50/__main__.py:218 | ||
| msgid "logout of submit50" | ||
| msgstr "đăng xuất khỏi submit50" | ||
|
|
||
| #: submit50/__main__.py:225 | ||
| msgid "failed to logout" | ||
| msgstr "không đăng xuất được" | ||
|
|
||
| #: submit50/__main__.py:227 | ||
| msgid "logged out successfully" | ||
| msgstr "đăng xuất thành công" | ||
|
|
||
| #: submit50/__main__.py:242 | ||
| msgid "" | ||
| "warning: displays usage warnings.\n" | ||
| "info: adds all commands run.\n" | ||
| "debug: adds the output of all commands run." | ||
| msgstr "" | ||
| "warning: hiển thị các cảnh báo về cách sử dụng.\n" | ||
| "info: thêm tất cả các lệnh đã chạy.\n" | ||
| "debug: thêm kết quả của tất cả các lệnh đã chạy." | ||
|
|
||
| #: submit50/__main__.py:248 | ||
| msgid "force authentication via HTTPS" | ||
| msgstr "buộc xác thực qua HTTPS" | ||
|
|
||
| #: submit50/__main__.py:251 | ||
| msgid "force authentication via SSH" | ||
| msgstr "buộc xác thực qua SSH" | ||
|
|
||
| #: submit50/__main__.py:259 | ||
| msgid "prescribed identifier of work to submit" | ||
| msgstr "định danh được chỉ định của bài cần nộp" | ||
|
|
||
| #: submit50/__main__.py:279 | ||
| msgid "" | ||
| "SSH authentication failed. Make sure your SSH key is added to your GitHub" | ||
| " account and loaded in ssh-agent, or omit --ssh to authenticate via HTTPS" | ||
| " instead." | ||
| msgstr "" | ||
|
rongxin-liu marked this conversation as resolved.
rongxin-liu marked this conversation as resolved.
|
||
| "Xác thực SSH thất bại. Hãy chắc chắn rằng khóa SSH của bạn đã được thêm " | ||
| "vào tài khoản GitHub và đã được nạp vào ssh-agent, hoặc bỏ --ssh để xác " | ||
| "thực qua HTTPS." | ||
|
|
||
| #: submit50/__main__.py:291 | ||
| msgid "--https and --ssh have no effect when used together" | ||
| msgstr "--https và --ssh không có hiệu lực khi dùng cùng nhau" | ||
|
|
||
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| """Consistency checks for the gettext catalogs under submit50/locale.""" | ||
| import ast | ||
| import gettext | ||
| import io | ||
| import pathlib | ||
| import re | ||
|
|
||
| import pytest | ||
| from babel.messages.mofile import write_mo | ||
| from babel.messages.pofile import read_po | ||
|
|
||
| ROOT = pathlib.Path(__file__).resolve().parent.parent | ||
| SOURCE = ROOT / "submit50" / "__main__.py" | ||
| LOCALE_DIR = ROOT / "submit50" / "locale" | ||
| LOCALES = sorted(p.name for p in LOCALE_DIR.iterdir() if (p / "LC_MESSAGES" / "submit50.po").is_file()) | ||
|
|
||
| # Catalogs known to be incomplete before the completeness check existed. | ||
| KNOWN_INCOMPLETE = {"es"} | ||
|
|
||
|
|
||
| def source_strings(): | ||
| """Every string literal passed to _() in __main__.py.""" | ||
| literals = set() | ||
| for node in ast.walk(ast.parse(SOURCE.read_text())): | ||
| if ( | ||
| isinstance(node, ast.Call) | ||
| and isinstance(node.func, ast.Name) | ||
| and node.func.id == "_" | ||
| and node.args | ||
| and isinstance(node.args[0], ast.Constant) | ||
| and isinstance(node.args[0].value, str) | ||
| ): | ||
| literals.add(node.args[0].value) | ||
| assert literals, "no _() literals found -- extraction is broken" | ||
| return literals | ||
|
|
||
|
|
||
| def load_po(locale): | ||
| with open(LOCALE_DIR / locale / "LC_MESSAGES" / "submit50.po", "rb") as f: | ||
| return read_po(f, locale=locale) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("locale", LOCALES) | ||
| def test_every_source_string_is_translated(locale, request): | ||
| if locale in KNOWN_INCOMPLETE: | ||
| request.applymarker(pytest.mark.xfail(reason=f"{locale} catalog is known to be incomplete", strict=True)) | ||
| catalog = load_po(locale) | ||
| translations = {m.id: m.string for m in catalog if m.id} | ||
| missing = sorted(s for s in source_strings() if s not in translations) | ||
| empty = sorted(s for s in source_strings() if s in translations and not translations[s]) | ||
| fuzzy = sorted(m.id for m in catalog if m.id and m.fuzzy) | ||
| assert not missing, f"{locale}: strings missing from catalog: {missing}" | ||
| assert not empty, f"{locale}: untranslated strings: {empty}" | ||
| assert not fuzzy, f"{locale}: fuzzy entries are skipped by compile_catalog: {fuzzy}" | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("locale", LOCALES) | ||
| def test_prompt_translations_keep_trailing_space(locale): | ||
| """input() prompts end with a space in the source; a translation that drops it glues the cursor to the text.""" | ||
| for message in load_po(locale): | ||
| if message.id and message.string and message.id.endswith(" "): | ||
| assert message.string.endswith(" "), f"{locale}: translation of {message.id!r} lost its trailing space" | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("locale", LOCALES) | ||
| def test_yes_regex_translation_is_valid(locale): | ||
| """`y|yes` is interpolated into a regex; the translation must compile and accept its own affirmative.""" | ||
| catalog = load_po(locale) | ||
| translated = catalog.get("y|yes") | ||
| if translated is None or not translated.string: | ||
| pytest.skip(f"{locale}: y|yes not translated") | ||
| pattern = re.compile(rf"^\s*(?:{translated.string})\s*$", re.I) | ||
| first_alternative = translated.string.split("|")[0] | ||
| assert pattern.match(first_alternative), f"{locale}: regex rejects its own first alternative" | ||
| assert not pattern.match("no"), f"{locale}: regex accepts 'no'" | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("locale", LOCALES) | ||
| def test_committed_mo_matches_po(locale): | ||
| """A committed .mo must be the compiled form of the committed .po (`*.mo` is gitignored, so it drifts silently).""" | ||
| mo_path = LOCALE_DIR / locale / "LC_MESSAGES" / "submit50.mo" | ||
| if not mo_path.is_file(): | ||
| pytest.skip(f"{locale}: no .mo committed (CI compiles it at build time)") | ||
| buf = io.BytesIO() | ||
| write_mo(buf, load_po(locale)) | ||
| expected = gettext.GNUTranslations(io.BytesIO(buf.getvalue()))._catalog | ||
| with open(mo_path, "rb") as f: | ||
| actual = gettext.GNUTranslations(f)._catalog | ||
| expected.pop("", None) | ||
| actual.pop("", None) | ||
| assert actual == expected, f"{locale}: submit50.mo is stale -- run `python setup.py compile_catalog` and recommit" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.