Skip to content

Add opt-in lang attribute to HTML tag for accessibility#85

Closed
Copilot wants to merge 8 commits intomainfrom
copilot/add-lang-attribute-html
Closed

Add opt-in lang attribute to HTML tag for accessibility#85
Copilot wants to merge 8 commits intomainfrom
copilot/add-lang-attribute-html

Conversation

Copy link
Copy Markdown

Copilot AI commented Feb 19, 2026

Implementation Complete ✅

Requirements Met

  1. Use Full Language Codes: ✅ Language codes like zh-CN, pt-BR, de-CH are preserved in their full form
  2. Make it Opt-In: ✅ The lang attribute is only added when developer explicitly sets language

Language Parameter Options

The implementation supports three ways to specify language:

  • None (default): No lang attribute added to HTML; uses 'en-US' for Quasar components
  • Language code (e.g., 'fr', 'zh-CN'): Adds lang attribute with the specified language
  • Empty string '': Explicitly opts out of lang attribute even when ui.run(language=...) is set globally; uses 'en-US' for Quasar components

Use Case Example

ui.run(language='fr')  # Global default: French for all pages

@ui.page('/fr')  
def french_page():  # Gets lang="fr" from global setting
    ui.label('Bonjour')

@ui.page('/mixed', language='')  
def mixed_page():  # Explicitly opts out: no lang attribute
    ui.label('Content in multiple languages')

Recent Changes (addressing review feedback)

  • Replaced ellipsis (...) with Language | None for cleaner typing
  • Removed all # type: ignore comments related to language parameters
  • Parametrized tests - reduced test count to 5 focused tests
  • Added support for empty string '' to explicitly opt-out of lang attribute
  • Simplified language checks using Python truthiness
  • Inlined has_explicit_language() to reduce public API surface
  • Simplified inlined logic to single boolean expression
  • Simplified resolve_language() to one-liner using chained or operators
  • Updated documentation to reflect all three language parameter options

Test Results

tests/test_lang_attribute.py - 5 tests PASSED
tests/test_page.py - 26 tests PASSED
CodeQL - 0 alerts
Original prompt

The Pull Request aims to add the lang attribute to the <html> tag for accessibility and SEO.

The actionable To-Do list, based on review feedback, requires two specific changes before merging:

  1. Use Full Language Codes: Do not simplify language codes (e.g., keep zh-CN instead of reducing it to just zh) to preserve necessary regional/script details for accessibility tools.
  2. Make it Opt-In: The lang attribute must only be added if the developer explicitly configures the language setting in their NiceGUI application. It should not be added automatically based on defaults, as this could incorrectly declare the page language and harm accessibility/functionality.

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copilot AI changed the title [WIP] Add lang attribute to html for accessibility and SEO Add opt-in lang attribute to HTML tag for accessibility Feb 19, 2026
Copilot AI requested a review from evnchn February 19, 2026 11:23
Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

  1. Can we reduce the use of type ignore? Maybe Language | None could make sense? Considering one may still pass None despite en-US in ui.run if need to serve a page with indeterminate language
  2. Can we parametrize the tests?

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Feb 19, 2026

  1. Can we reduce the use of type ignore? Maybe Language | None could make sense? Considering one may still pass None despite en-US in ui.run if need to serve a page with indeterminate language
    ...

Done in ab23ca6:

  1. Reduced type ignore comments: Changed from Language = ... with # type: ignore to Language | None = None. This removes all 3 type ignore comments related to language parameters and is cleaner. Using None also allows explicitly passing None for pages with indeterminate language.

  2. Parametrized tests: Consolidated 6 separate test functions into 2:

    • test_no_lang_attribute_by_default (unchanged)
    • test_lang_attribute_with_explicit_language (parametrized with 9 language codes)

    This reduces duplication from 141 lines to 57 lines while testing the same scenarios.

Copilot AI requested a review from evnchn February 19, 2026 12:07
Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

  1. We don't need to many test cases, do we?
  2. It doesn't seem I can pass any parameter such that language is not specific when ui.run has language, due to None considered as not-passing. Maybe we need to expand to allow "" as a language, which defaults to English Quasar components and non-specified language in HTML.

- Reduced parametrized test cases from 9 to 3 (fr, zh-CN, pt-BR)
- Added support for empty string ('') to explicitly opt-out of lang attribute
- Empty string uses 'en-US' for Quasar but doesn't add lang attribute
- Added test for empty string opt-out behavior
- Updated type annotations and docstrings

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Feb 19, 2026

  1. We don't need to many test cases, do we?
  2. It doesn't seem I can pass any parameter such that language is not specific when ui.run has language, due to None considered as not-passing. Maybe we...

Done in 691c88c:

  1. Reduced test cases: Parametrized test now has 3 cases (fr, zh-CN, pt-BR) instead of 9, plus 2 standalone tests = 5 total tests

  2. Added empty string opt-out: Empty string '' can now be used to explicitly opt-out of lang attribute even when ui.run(language=...) is set. This allows:

    • None = not configured, no lang attribute
    • 'fr' = explicit language, adds lang attribute
    • '' = explicit opt-out, no lang attribute (even if ui.run sets a language)

Example:

ui.run(language='fr')  # Global default

@ui.page('/fr')  # lang="fr"
def french(): ...

@ui.page('/mixed', language='')  # No lang attribute
def mixed(): ...

Copilot AI requested a review from evnchn February 19, 2026 12:28
Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

We're getting close.

Comment thread nicegui/page.py Outdated
Comment on lines +95 to +97
if self.language is not None and self.language != '':
return self.language
if core.app.config.language is not None and core.app.config.language != '':
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think you can directly check for truthiness

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 78f12cf. Simplified to use truthiness checks:

  • if self.language: instead of if self.language is not None and self.language != '':
  • Same for core.app.config.language

Since empty string is falsy in Python, this is cleaner and more Pythonic.

Use Python's truthiness checks instead of explicit None and empty string comparisons for cleaner code.

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copilot AI requested a review from evnchn February 19, 2026 12:36
Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

One more thought

Comment thread nicegui/client.py Outdated
'favicon_url': get_favicon_url(self.page, prefix),
'dark': str(self.page.resolve_dark()),
'language': self.page.resolve_language(),
'has_explicit_language': self.page.has_explicit_language(),
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Can we minimize the API surface, if we inline has_explicit_language()?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in e017166. Removed has_explicit_language() method from the page class and inlined the logic directly in client.py where it's used. This reduces the public API surface while maintaining the same functionality.

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copilot AI requested a review from evnchn February 19, 2026 12:49
Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

A pending simplification

Comment thread nicegui/client.py Outdated
Comment on lines +186 to +189
'has_explicit_language': (
(self.page.language != '' and bool(self.page.language))
or (core.app.config.language != '' and bool(core.app.config.language))
),
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Can we simplify this?

'has_explicit_language': bool(self.page.language. or core.app.config.language),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 383b5e8. Simplified to bool(self.page.language or core.app.config.language). The explicit != '' checks were redundant since empty string is falsy in Python.

Simplified to `bool(self.page.language or core.app.config.language)` since empty string is falsy, making the explicit != '' checks redundant.

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copilot AI requested a review from evnchn February 19, 2026 13:29
Comment thread nicegui/page.py Outdated
return self.language
if core.app.config.language:
return core.app.config.language
return 'en-US'
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think this can be a one-liner

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 92c0ee5. Simplified to return self.language or core.app.config.language or 'en-US' using chained or operators.

Copy link
Copy Markdown
Owner

@evnchn evnchn left a comment

Choose a reason for hiding this comment

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

I believe the logic of resolve_language can be a one-liner.

Changed resolve_language to use chained or operators for cleaner, more concise code.

Co-authored-by: evnchn <37951241+evnchn@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Feb 19, 2026

I believe the logic of resolve_language can be a one-liner.

Done in 92c0ee5. Simplified resolve_language() to a one-liner using chained or operators.

@evnchn evnchn closed this Feb 19, 2026
Copilot AI requested a review from evnchn February 19, 2026 14:57
Copilot stopped work on behalf of evnchn due to an error February 19, 2026 14:57
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