npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@salesforce/ui-bundle-template-feature-react-language-switcher

v11.71.3

Published

Language switcher for B2X UI Bundles — rewrites the URL language segment and reloads

Readme

feature-react-language-switcher

A drop-in language switcher for B2X (public / Experience) React UI Bundles.

Selecting a language rewrites the current URL so the chosen language becomes the first path segment after basePath, then reloads the page at that URL. A full reload lets the platform serve the correctly localized content — this feature owns only the URL rewrite. It does not load translations, manage i18n state, or persist a preference.

B2X only. Do not add this feature to B2E (internal / CustomApplication) apps. Those serve logged-in users whose language comes from their profile, and their URLs have no language segment.

Usage

Render <LanguageSwitcher /> anywhere in your app chrome (typically the header in appLayout.tsx). It reads everything it needs from the URL and SFDC_ENV, so no props are required:

import { LanguageSwitcher } from ".../features/language-switcher";

<header>
  {/* ... */}
  <LanguageSwitcher />
</header>;

Optional props:

  • label — accessible label for the <select> (default "Language").
  • className — replaces the default Tailwind classes on the <select>.

Single-language sites render nothing. When fewer than two languages are configured in languages.ts, the component returns null — a one-option dropdown can't change anything, so showing it would add an inert focus stop announced as a "1 of 1" menu. The switcher appears only when there is a real choice to make.

How the URL is rewritten

Given basePath from SFDC_ENV.basePath, the leading path segment after it is handled by the chosen language:

  • DEFAULT_LANGUAGE is the implicit, un-prefixed language, so no segment is written. An existing language segment is removed (/shop/fr/page/shop/page); a path with no language segment is left unchanged.
  • Any other language is replaced into an existing language segment (no /en-US/fr/page stacking), or inserted when there is none.

The rest of the path, the query string, and the hash are preserved.

Locale codes use underscores (en_US), but the URL path segment uses hyphens (en-US). The switcher writes the hyphenated form and remaps it back to the underscore code when reading the current language from the URL.

The table below assumes DEFAULT_LANGUAGE = en_US:

| Before (basePath = /shop) | Pick | After | | --------------------------- | ------- | --------------------------- | | /shop/catalog?q=x#top | fr | /shop/fr/catalog?q=x#top | | /shop/de-DE/catalog | fr | /shop/fr/catalog | | /shop | fr | /shop/fr | | /shop/fr/catalog?q=x#top | en_US | /shop/catalog?q=x#top | | /shop/catalog | en_US | /shop/catalog (unchanged) |

Selecting the language that is already active is a no-op (no reload).

The current language

Resolved in precedence order by getCurrentLanguage():

  1. globalThis.SFDC_ENV.language — authoritative when the platform provides it. The platform injects this in hyphenated form (e.g. "en-US"), so it is remapped back to a locale code (en_US) before matching, exactly like the URL segment.
  2. The leading URL path segment after basePath, remapped from hyphens back to a locale code, when it is a supported language — so a direct hit on /shop/en-US/catalog resolves to en_US even before the runtime populates SFDC_ENV.language.
  3. DEFAULT_LANGUAGE — when neither of the above yields a supported code.

Only codes present in LANGUAGES are honored; anything else falls through to the next step.

SFDC_ENV does not declare language in the platform types yet.

Text direction (RTL) — drive it from getCurrentLanguage(), not the session locale

This feature does not touch <html dir> (it owns only the URL rewrite). If your app loads translations via i18next and supports a right-to-left language (Arabic, Hebrew, Farsi, Urdu, …), the i18n init — not this feature — sets direction, and there is one trap worth calling out.

The Platform SDK's fetchI18nContext() returns a dir for the session locale. In B2E that is also the display locale, so ctx.dir is correct. But on a B2X site the session locale is the guest user's fixed profile language, which ignores the language this switcher picks — so ctx.dir will not flip to rtl when the user selects an RTL language, even though <html lang> did. lang and dir end up disagreeing.

Direction is a property of the language itself, so derive it from the resolved display language — the same value this feature exposes via getCurrentLanguage() — using i18next.dir() (which carries a built-in RTL list). In your i18n init:

import i18next from "i18next";
import { getCurrentLanguage } from ".../features/language-switcher";

// Follows the switcher-selected language on a site; in B2E (no switcher / URL
// segment) getCurrentLanguage() resolves to the session locale, so this is a
// no-op there. Falls back to the SDK's session lang only when nothing resolves.
const resolvedLang = getCurrentLanguage() || ctx.lang;
document.documentElement.dir = i18next.dir(resolvedLang); // "rtl" for ar/he/fa/…
document.documentElement.lang = resolvedLang.replace(/_/g, "-"); // BCP-47 form

Setting dir is not the same as an RTL layout. dir="rtl" flips text flow, alignment, and caret behavior, but the layout only mirrors (margins, padding, float/flex sides, directional icons) if your CSS uses logical properties (ms-/me-/ps-/pe-/start-/end- in Tailwind) rather than physical ones (ml-/mr-/left-/right-). Converting an app built with physical properties is a separate effort this feature does not perform. This feature ships an ambient augmentation (src/types/globals.d.ts) adding the optional language field. The runtime populates it in dash form (e.g. "language":"en-US") — the platform converts the underscore locale code before injecting it.

Local development

On a deployed Experience site the platform injects SFDC_ENV.language and folds the active language into SFDC_ENV.basePath (e.g. /shop/fr), recomputing both from the URL on every load. The generic local Vite dev server does neither — SFDC_ENV.language is absent and basePath is always / — so the switcher can't route a non-default language locally (the reload 404s and labels never flip; only the default language works).

This feature integrates with the site entry of @salesforce/vite-plugin-ui-bundle. Switch your vite.config.ts to import @salesforce/vite-plugin-ui-bundle/site and pass the supported LANGUAGES (a shipped __examples__/vite-config-site-example.ts shows this):

import siteUiBundlePlugin from "@salesforce/vite-plugin-ui-bundle/site";
import { LANGUAGES } from "./src/features/language-switcher/languages";

export default defineConfig({
  // The plugin needs the language codes; the first entry is the default.
  plugins: [siteUiBundlePlugin({ languages: LANGUAGES.map((l) => l.code) })],
});

The site plugin then, for local dev only, treats a leading URL segment that matches one of LANGUAGES as the active language — injecting SFDC_ENV.language and a matching basePath, and serving the language-prefixed path (SPA fallback). This mirrors production, so the switcher changes language live via URL + reload — no server restart.

Then npm run dev and use the switcher normally: picking French navigates to /fr, Chinese to /zh-CN, and the default language drops the segment. This has no effect on a deployed build, and non-site bundles keep using the generic @salesforce/vite-plugin-ui-bundle import unchanged.

Keeping the language list in sync

The supported languages are hardcoded in languages.ts (LANGUAGES + DEFAULT_LANGUAGE). There is no runtime API to read sfdc_cms__languageSettings on a published site, so the list must be kept in sync by hand with the site's:

digitalExperiences/site/<siteName>/sfdc_cms__languageSettings/content.json

When the site's supported languages change, update LANGUAGES (and DEFAULT_LANGUAGE) to match.

Unsupported languages are the app's responsibility. The switcher only ever writes codes from LANGUAGES, but nothing stops a URL from carrying a language segment that is not configured in sfdc_cms__languageSettings — a hand-typed or stale link like /shop/it-IT/catalog, for example. The switcher does not honor it (an unrecognized segment is not treated as a language, so getCurrentLanguage() falls through to DEFAULT_LANGUAGE), but it also does not strip or rewrite it — the segment stays in the URL and is served by the platform and your React app like any other path. It is up to the app to handle such a URL gracefully (e.g. redirect to a supported language or render a friendly not-found state). If left unhandled, an unsupported language segment can surface as a 404 / error page.

Public API

| Export | Description | | -------------------- | ------------------------------------------------------------------- | | LanguageSwitcher | Drop-in <select> component. | | getCurrentLanguage | Resolves the active language from SFDC_ENV with default fallback. | | buildLanguageUrl | Pure helper: inserts/replaces the leading language path segment. | | LANGUAGES | Typed list of supported languages ({ code, label }). | | DEFAULT_LANGUAGE | Default language code. | | LANGUAGE_CODES | Set of known codes for membership checks. |