Skip to content

GH-3: Write a script to check websites trilingual feature#4

Open
ChanukaUOJ wants to merge 20 commits into
LDFLK:mainfrom
ChanukaUOJ:feat/trilingual-checker
Open

GH-3: Write a script to check websites trilingual feature#4
ChanukaUOJ wants to merge 20 commits into
LDFLK:mainfrom
ChanukaUOJ:feat/trilingual-checker

Conversation

@ChanukaUOJ

@ChanukaUOJ ChanukaUOJ commented Jun 22, 2026

Copy link
Copy Markdown
Member

This PR introduces the trilingual check for WebsiteScorecard, which automatically detects whether websites support Sri Lanka's three official languages (English, Sinhala, and Tamil).

Changes

  • New Check Added: Implemented TrilingualCheck in src/websitescorecard/checks/trilingual.py.
  • Dependency Updates: Added playwright to the core pyproject.toml dependencies. Moved pytest-playwright and pytest-mock to [dev] dependencies.
  • Documentation: Updated README.md to document the new trilingual check, the required block-letter outputs, and instructions for running the required playwright install chromium setup step.
  • Base Checker: Updated base.py to support the new outputs.

Detection Pipeline

The script employs a multi-layered detection strategy to maximise accuracy, falling back to heavier methods only when necessary:

  1. HTML Attributes: Checks the root <html lang="X"> and <link rel="alternate" hreflang="X"> tags.
  2. Google Translate: Detects native and GTranslate widget implementations.
  3. URL Localization: Uses regex to identify language-specific path segments (e.g., /si/, /en/) or query parameters (e.g., ?lang=ta).
  4. Unicode Content Check: Parses the DOM and single-pass traversal of leaf text nodes to accurately count Sinhala, Tamil, and English characters without duplication.
  5. Headless Browser Fallbacks: For JavaScript-heavy or single-page apps (SPAs), the script uses Playwright to:
    • Verify that Google Translate widgets actually contain the required languages.
    • Simulate clicks on visible language switcher buttons and verify the resulting DOM.
    • Inject target language codes directly into localStorage (e.g., i18n, lang) and reload the page to detect locale changes.
  6. Internal Link Crawling: As a last resort, scrapes internal links for language-specific URLs to fetch and verify.

Robustness & Fixes Included

  • Block-letter Outputs: Outputs standardised statuses (TRILINGUAL, NON_TRILINGUAL, TIMEOUT, UNREACHABLE) and detailed metadata (trilingual_details).

Testing

  • Added a comprehensive pytest suite (tests/test_trilingual.py) achieving full coverage of static and headless browser workflows.
  • Successfully ran scans against the sample data/mins_depts_test.csv file without errors.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new trilingual check to scan websites for English, Sinhala, and Tamil content using BeautifulSoup and Playwright, updating the README and project dependencies accordingly. The review feedback highlights several critical improvements: removing an unnecessary global lock to enable true concurrency, avoiding fragile string interpolation in page.evaluate, correcting the fallback logic for injection tests and URL construction, and preventing the masking of internal exceptions. Additionally, it is recommended to use playwright instead of pytest-playwright in production dependencies, move warning disabling to the module level, enhance the URL localization regex, wrap long lines, and document the Playwright browser installation prerequisite in the README.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/websitescorecard/checks/trilingual.py
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread pyproject.toml Outdated
Comment thread src/websitescorecard/checks/trilingual.py
Comment thread src/websitescorecard/checks/trilingual.py
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread README.md

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new TrilingualCheck to evaluate whether websites support English, Sinhala, and Tamil, updating the CLI, documentation, and dependencies accordingly. The review feedback highlights several important areas for improvement: resolving a concurrency bottleneck caused by a global Playwright lock, preventing browser resource leaks, securing JavaScript evaluations against injection, refining the English language detection regex to avoid false positives, preserving URL path and query parameters during checks, and exposing actual exception details instead of a hardcoded error message. Additionally, the production dependencies should be updated to use playwright instead of the testing plugin pytest-playwright.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread pyproject.toml Outdated
@ChanukaUOJ

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new TrilingualCheck to verify if websites support English, Sinhala, and Tamil, along with corresponding CLI options, documentation updates, and comprehensive tests. Feedback on the implementation highlights critical concurrency issues with shared instance state (_had_timeout) across threads, potential resource leaks of Chromium processes under exception paths, text duplication bugs in unicode content detection, and a double www. prefix bug in the fallback request logic.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/websitescorecard/checks/trilingual.py
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
Comment thread src/websitescorecard/checks/trilingual.py Outdated
found_langs.add(soup.html.get('lang').split('-')[0].lower())

missing = [lang for lang in LANGUAGE_KEY if lang not in found_langs]
return (len(missing) == 0, missing)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we pass len(missing) == 0 and also missing? Can't we get all the info just from missing?

Comment on lines +95 to +98
if soup.find(id=HTML_GOOGLE_TRANSLATE_ELEMENT):
return 'google_translate'
if soup.find(class_=HTML_GOOG_TE_COMBO):
return 'google_translate'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if soup.find(id=HTML_GOOGLE_TRANSLATE_ELEMENT):
return 'google_translate'
if soup.find(class_=HTML_GOOG_TE_COMBO):
return 'google_translate'
if soup.find(id=HTML_GOOGLE_TRANSLATE_ELEMENT) or soup.find(class_=HTML_GOOG_TE_COMBO):
return 'google_translate'

found_langs.add(lang)

missing = [lang for lang in LANGUAGE_KEY if lang not in found_langs]
return (len(missing) == 0, missing)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we pass len(missing) == 0 and also missing? Can't we get all the info just from missing?

Comment on lines +66 to +80
for link in soup.find_all(HTML_KEY_LINK, rel=HTML_KEY_ALTERNATE):
hreflang = link.get(HTML_KEY_HREFLANG)
href = link.get('href')
if not hreflang:
continue
if href:
absolute_url = urljoin(url, href)
href_domain = urlparse(absolute_url).netloc
if href_domain.startswith('www.'):
href_domain = href_domain[4:]

# Check if href domain is the same or a subdomain
if base_domain and not href_domain.endswith(base_domain):
continue
hreflangs.append(hreflang)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In situations where there is a hreflang but no href it will still be accepted. For example:

<link rel="alternate" hreflang="en" />

Is there a reason for this?

Comment on lines +561 to +562
if len(found_langs) == 3:
return TrilingualCheckResult(status="TRILINGUAL", error=None, details=", ".join(passed_criteria))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the basic checks pass then we don't do the deeplink checking. This does not catch the case where only the home page is translated and not the other pages. Better to not return here but continue checking the deeplinks for the same basic checks.

Comment on lines +515 to +516
if re.search(r'(?:/si(?:[/?#]|$)|/ta(?:[/?#]|$)|[?&]lang=(?:si|ta)(?:&|$)|/sinhala|/tamil)', href_lower) or \
any(x in text for x in ['sinhala', 'tamil', 'සිංහල', 'தமிழ்']):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not english?

# Filter out the homepage itself
internal_links = [l for l in internal_links if l != response.url and l != response.url.rstrip('/')]

sample_links = internal_links[:5] # The first links are prioritized language links

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we prioritising language links?

if len(found_langs) == 3:
return TrilingualCheckResult(status="TRILINGUAL", error=None, details=", ".join(passed_criteria))

# Try deeplink crawling

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put the deeplink checking in a different function. This run function is quite lengthy

Comment on lines +25 to +31
LANG_STORAGE_FORMATS: list[tuple[list[str], list[str]]] = [
(['en', 'si', 'ta'], ['en', 'si', 'ta']),
(['en-US', 'en-GB', 'si-LK', 'ta-LK', 'ta-IN'], ['en-US', 'si-LK', 'ta-LK']),
(['en-us', 'en-gb', 'si-lk', 'ta-lk', 'ta-in'], ['en-us', 'si-lk', 'ta-lk']),
(['English', 'Sinhala', 'Tamil'], ['English', 'Sinhala', 'Tamil']),
(['english', 'sinhala', 'tamil'], ['english', 'sinhala', 'tamil']),
]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the first column is needed here

missing_injection = []
langs = LANGUAGE_KEY

for i in range(3):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for i in range(3):
for i in range(len(langs)):

@zaeema-n

zaeema-n commented Jul 1, 2026

Copy link
Copy Markdown
Member

This pr closes #3

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants