Skip to content

[web] Semantics lang attribute uses Locale.toString(), producing invalid tags like es_US #194688

Description

@T-Davis

Steps to reproduce

  1. Run flutter create --platforms web bug and replace lib/main.dart with the code sample below.
  2. Run flutter build web, then serve it with python3 -m http.server -d build/web.
  3. Open http://localhost:8000 and run this in the DevTools console:
    document.querySelector('flt-semantics[lang]').getAttribute('lang')

Expected results

"es-US". The HTML lang attribute must be a valid BCP 47 language tag, and BCP 47 separates subtags with hyphens.

Actual results

"es_US". In Chromium and WebKit, element.matches(':lang(es)') is then false for that node. Firefox accepts the underscore and returns true.

Measured on 3.47.7 stable with Playwright's Chromium 156, Firefox 157 and WebKit 27.2 builds. With Locale('es') the attribute is "es", which avoids the bug at the cost of the region. Localizations.override with Locale('es', 'US') produces the same "es_US", since it sets localeForSubtree too.

The web engine's _updateLocale builds the attribute from semanticsObject.locale?.toString(), which joins subtags with an underscore. This is still the code on master. #171196 used toString() for both web and native, and #173364 later switched the native path in dart:ui to toLanguageTag(). Making the same change in _updateLocale should fix web, since Locale('es', 'US').toLanguageTag() returns "es-US" there.

The web tests did not catch this because they construct Locale('es-MX'), which puts the whole tag in languageCode and leaves countryCode null.

Code sample

Code sample
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(
    Directionality(
      textDirection: TextDirection.ltr,
      child: Semantics(
        localeForSubtree: const Locale('es', 'US'),
        child: const Text('Hola'),
      ),
    ),
  );
  SemanticsBinding.instance.ensureSemantics();
}

Screenshots or Video

No response

Logs

No response

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.47.7, on macOS 26.5.1 25F80 darwin-arm64, locale en-US) [1,606ms]
    • Flutter version 3.47.7 on channel stable at ~/fvm/versions/3.47.7
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision abaf9c5237 (10 days ago), 2026-10-08 09:51:21 -0700
    • Engine revision deb287481e
    • Dart version 3.13.5
    • DevTools version 2.60.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [2.8s]
    • Android SDK at ~/Library/Android/sdk
    • Emulator version 37.1.11.0 (build_id 15917651) (CL:N/A)
    • Platform android-36.1, build-tools 36.1.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 25.0.3+-15898627-b508.16)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 26.3) [3.3s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17C529
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [19ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Connected device (4 available) [15.6s]
    • Pixel 7 Pro (mobile)         • <redacted>                • android-arm64  • Android 17 (API 37)
    • iPhone (mobile)              • <redacted>                • ios            • iOS 26.7.1 23H30
    • macOS (desktop)              • macos                     • darwin-arm64   • macOS 26.5.1 25F80 darwin-arm64
    • Chrome (web)                 • chrome                    • web-javascript • Google Chrome 155.0.8059.80

[✓] Network resources [782ms]
    • All expected network resources are available.

• No issues found!

Activity

  1. added
    in triagePresently being triaged by the triage team
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    platform-webWeb applications specifically
    fyi-webFor the attention of Web platform team
    and removed
    in triagePresently being triaged by the triage team
    on Oct 20, 2026
  2. suryaKommana2662 commented on Oct 20, 2026

    @suryaKommana2662
    Contributor

    @T-Davis Thanks for teh report. I was able to reproduce the issue. In Chrome DevTools Console, the following command returns es_US instead of the expected es-US. Routing this to the accessibility team for further investigation.

    document.querySelector('flt-semantics[lang]').getAttribute('lang')
    // Output: 'es_US'

    Flutter Doctor Output

    [✓] Flutter (Channel stable, 3.47.7, on macOS 27.0 26A428 darwin-arm64, locale en-IN) [282ms]
        • Flutter version 3.47.7 on channel stable at /Users/suryakommana/develop/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision abaf9c5237 (12 days ago), 2026-10-08 09:51:21 -0700
        • Engine revision deb287481e
        • Dart version 3.13.5
        • DevTools version 2.60.0
        • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets,
          enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [1,520ms]
        • Android SDK at /Users/suryakommana/Library/Android/sdk
        • Emulator version 37.1.11.0 (build_id 15917651) (CL:N/A)
        • Platform android-37.0, build-tools 36.0.0
        • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
          This is the JDK bundled with the latest Android Studio installation on this machine.
          To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
        • Java version OpenJDK Runtime Environment (build 25.0.2+-15348964-b329.117)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 27.1) [758ms]
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 27A9275
        • CocoaPods version 1.17.0
    
    [✓] Chrome - develop for the web [3ms]
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Connected device (3 available) [5.9s]
        • sdk gphone16k arm64 (mobile) • emulator-5554 • android-arm64  • Android 17 (API 37) (emulator)
        • macOS (desktop)              • macos         • darwin-arm64   • macOS 27.0 26A428 darwin-arm64
        • Chrome (web)                 • chrome        • web-javascript • Google Chrome 155.0.8059.84
    
    [✓] Network resources [833ms]
        • All expected network resources are available.
    
    • No issues found!
  3. added
    triaged-webTriaged by Web platform team
    P2Important issues not at the top of the work list
    on Oct 21, 2026
  4. flutter-zl commented on Oct 21, 2026

    @flutter-zl
    Contributor
  5. added
    team-webOwned by Web platform team
    triaged-webTriaged by Web platform team
    and removed
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions