from pathlib import Path import re from tests.test_issue2147_profile_concept_help import PROFILE_CONCEPT_KEYS REPO = Path(__file__).resolve().parent.parent PROFILE_CONCEPT_FALLBACK_KEYS = { *PROFILE_CONCEPT_KEYS, "workspace_artifact_source_session", } def read(path: Path) -> str: return path.read_text(encoding="utf-8") def locale_block(src: str, marker: str) -> str: start = src.index(marker) open_brace = src.index("{", start) pos = open_brace + 1 depth = 1 in_single = False in_double = False in_template = False escaped = False line_comment = False block_comment = False while pos < len(src): char = src[pos] next_char = src[pos + 1] if pos + 1 < len(src) else "" if line_comment: if char == "\n": line_comment = False elif block_comment: if char == "*" and next_char == "/": block_comment = False pos += 1 elif escaped: escaped = False elif in_single: if char == "\\": escaped = True elif char == "'": in_single = False elif in_double: if char == "\\": escaped = True elif char == '"': in_double = False elif in_template: if char == "\\": escaped = True elif char == "`": in_template = False elif char == "/" and next_char == "/": line_comment = True pos += 1 elif char == "/" and next_char == "*": block_comment = True pos += 1 elif char == "'": in_single = True elif char == '"': in_double = True elif char == "`": in_template = True elif char == "{": depth += 1 elif char == "}": depth -= 1 if depth == 0: return src[open_brace + 1 : pos] pos += 1 raise AssertionError(f"Locale block not closed: {marker}") def entries(block: str) -> list[tuple[str, str]]: return re.findall( r"^\s+([a-zA-Z0-9_]+):\s*(.*?)(?:,\s*)?$", block, re.MULTILINE, ) def keys(block: str) -> list[str]: return [key for key, _ in entries(block)] def value_map(block: str) -> dict[str, str]: return dict(entries(block)) def arrow_arg_names(value: str) -> list[str] | None: match = re.match(r"\((?P[^)]*)\)\s*=>", value) if not match: match = re.match(r"(?P[a-zA-Z_$][\w$]*)\s*=>", value) if not match: return None return [match.group("arg")] args = match.group("args").strip() if not args: return [] return [arg.strip() for arg in args.split(",") if arg.strip()] def arrow_arg_count(value: str) -> int | None: args = arrow_arg_names(value) if args is None: return None return len(args) def template_arg_refs(value: str, arg_names: list[str] | None) -> list[str]: arg_index = {arg: index for index, arg in enumerate(arg_names or [])} refs = [] for var_name in re.findall(r"\$\{([a-zA-Z_]\w*)\}", value): if var_name in arg_index: refs.append(f"arg:{arg_index[var_name]}") else: refs.append(f"name:{var_name}") return sorted(set(refs)) def test_english_locale_remains_english_source_text(): src = read(REPO / "static" / "i18n.js") en_block = locale_block(src, "\n en: {") cjk_lines = [ line.strip() for line in en_block.splitlines() if re.search(r"[\u4e00-\u9fff]", line) ] assert not cjk_lines, cjk_lines def test_zh_hant_locale_block_exists(): src = read(REPO / "static" / "i18n.js") assert "\n 'zh-Hant': {" in src assert "_label: '繁體中文'" in src assert "_speech: 'zh-TW'" in src def test_zh_hant_locale_covers_english_keys_without_duplicates(): src = read(REPO / "static" / "i18n.js") en_block = locale_block(src, "\n en: {") zh_block = locale_block(src, "\n 'zh-Hant': {") en_keys = set(keys(en_block)) zh_keys = keys(zh_block) duplicates = sorted({key for key in zh_keys if zh_keys.count(key) > 1}) assert not duplicates, f"zh-Hant locale duplicate keys: {duplicates}" missing = sorted((en_keys - set(zh_keys)) - PROFILE_CONCEPT_FALLBACK_KEYS) extra = sorted(set(zh_keys) - en_keys) assert not missing, f"zh-Hant locale missing keys: {missing}" assert not extra, f"zh-Hant locale extra keys: {extra}" def test_zh_hant_locale_preserves_function_and_placeholder_shapes(): src = read(REPO / "static" / "i18n.js") en_values = value_map(locale_block(src, "\n en: {")) zh_values = value_map(locale_block(src, "\n 'zh-Hant': {")) function_mismatches = [] placeholder_mismatches = [] template_var_mismatches = [] for key, en_value in en_values.items(): if key in PROFILE_CONCEPT_FALLBACK_KEYS: continue assert key in zh_values, f"Missing zh-Hant translation: {key}" zh_value = zh_values[key] en_arg_names = arrow_arg_names(en_value) zh_arg_names = arrow_arg_names(zh_value) en_args = None if en_arg_names is None else len(en_arg_names) zh_args = None if zh_arg_names is None else len(zh_arg_names) if en_args != zh_args: function_mismatches.append((key, en_args, zh_args)) placeholder_pattern = r"(?