Function
This was a native <select> until the mobile-viewport-fit change. A
<select> renders the text of its selected <option>, so its width is set
by the longest language name in the active locale — 106px, which does not fit
the header's width budget at 320px and cannot be swapped for a short code
with CSS. A button's width follows the text it actually renders, so the
sm:hidden / hidden sm:inline pair below is enough.
The short codes come from routing.locales rather than from the message
files: EN/ES/PT are ISO 639-1 codes, identical in every locale, and
routing them through next-intl would invite three translations of a
constant to drift apart.
The accessible name is built from the full localized language name, so a screen reader announces "Language: English" at every width rather than spelling out "E N" from the visible short code.
Uses @/i18n/navigation (not next/navigation) so the locale prefix
is preserved automatically. The active locale comes from
useLocale() — never read it from the URL directly.
Language selector that updates the active locale via
next-intl's locale-aware navigation.Renders a compact trigger showing the active language — its ISO code on a phone, its full localized name from
smup — which opens a menu of every locale inrouting.locales. Picking one navigates to the same path under the new locale (/en/about→/es/about) usingrouter.replaceso the back button still works. The transition is wrapped inuseTransitionso React can keep the previous UI responsive while the navigation is in flight.