Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/react-multilingual-intranet-switcher/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.14.0
18 changes: 18 additions & 0 deletions samples/react-multilingual-intranet-switcher/.yo-rc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.23.2",
"libraryName": "multilingual-intranet-switcher",
"libraryId": "c9de8f07-70ae-4d19-b2a5-7ab36a0c6b54",
"environment": "spo",
"packageManager": "npm",
"solutionName": "react-multilingual-intranet-switcher",
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"componentType": "webpart",
"framework": "react",
"webpartName": "MultilingualIntranetSwitcher",
"webpartDescription": "Read-only multilingual intranet navigation and content switcher",
"webpartHelpUrl": "",
"supportedHosts": ["SharePointWebPart"]
}
}
66 changes: 66 additions & 0 deletions samples/react-multilingual-intranet-switcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Multilingual Intranet Switcher

An SPFx 1.23.2 React 17 web part using Fluent UI v9 to show read-only intranet navigation/content links in a user-selected language.

This sample does not translate content, infer translations, call a translation API, read SharePoint data, write data, or mutate tenant state. Locale detection reads the browser locale only once as the initial default. The accessible language selector is always available for an explicit choice.

## Run

```bash
nvm use
npm install
npm test
npm run build
```

Use `npm run serve` only after adding the standard SPFx serve script if local workbench serving is needed; the included `config/serve.json` targets the hosted SharePoint workbench.

## Configuration schema

Set the web part's `configurationJson` property to one JSON object:

```json
{
"defaultLocale": "en-US",
"locales": [
{
"code": "en-US",
"displayName": "English",
"items": [
{
"id": "home",
"label": "Home",
"url": "/sites/intranet/SitePages/home.aspx",
"description": "Company home"
}
]
}
]
}
```

Required fields are `defaultLocale`, `locales[]`, `code`, `displayName`, `items[]`, `id`, `label`, and `url`. `description` is optional. Locale codes use a strict BCP 47-style shape such as `en`, `en-US`, or `zh-Hans-CN`; locale codes and item IDs must be unique. `defaultLocale` must be one of the configured locales.

The parser enforces these bounds before rendering: 100,000 JSON characters, 12 locales, 24 items per locale, 20 locale-code characters, 80 display-name characters, 80 ID characters, 160 label characters, 2,048 URL characters, and 400 description characters. Invalid JSON or schema data shows an error with a retry action. A valid configuration with no usable links shows an empty state.

## URL and tenant validation

Links may be relative or absolute, but the component accepts only `http`/`https` URLs whose origin exactly matches the current SharePoint web's origin. `javascript:`, `data:`, protocol-relative external URLs, credentials, malformed URLs, and external hosts are discarded. This is a same-origin guard for the current tenant; administrators should still review every configured path and publish the web part only in the intended tenant.

The sample has no API permissions and no `webApiPermissionRequests`. It uses only the SPFx page context to obtain the current web origin. It performs no POST, PATCH, PUT, DELETE, list update, navigation mutation, or configuration persistence.

## Accessibility and behavior

The selector is a native keyboard-operable Fluent UI select with an accessible label. Navigation is a labelled `nav` list of links, status messages use a polite live region, and malformed configuration uses an alert. Layout collapses to a full-width selector on narrow screens; links remain keyboard-focusable with a visible focus indicator.

Browser locale matching is deterministic: exact supported tag, then matching language, then configured default. Unsupported or malformed browser locale values are ignored. Selecting a language never changes the URL or configuration.

## References

- [Microsoft Graph localeInfo resource](https://learn.microsoft.com/graph/api/resources/localeinfo?view=graph-rest-1.0)
- [SharePoint Framework overview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
- [Build a SharePoint Framework client-side web part](https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part)

PnP metadata is in [`assets/sample.json`](assets/sample.json), with the sample credited to `vystartasv` and dated `2026-08-30`.

<img src="https://m365-visitor-stats.azurewebsites.net/sp-dev-fx-webparts/samples/react-multilingual-intranet-switcher" />
47 changes: 47 additions & 0 deletions samples/react-multilingual-intranet-switcher/assets/sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-multilingual-intranet-switcher",
"source": "pnp",
"title": "Multilingual Intranet Switcher",
"shortDescription": "A read-only, explicitly configured multilingual SharePoint intranet navigation and content switcher.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-multilingual-intranet-switcher",
"longDescription": [
"This sample uses bounded local JSON configuration for locale labels, optional descriptions, and same-tenant SharePoint URLs. Browser locale detection only chooses the initial view; it does not translate content or call a translation API."
],
"creationDateTime": "2026-08-30",
"updateDateTime": "2026-08-30",
"products": ["SharePoint"],
"metadata": [
{ "key": "SAMPLE-TYPE", "value": "SPFx-WebPart" },
{ "key": "CLIENT-SIDE-DEV", "value": "React" },
{ "key": "SPFX-VERSION", "value": "1.23.2" },
{ "key": "UI-FRAMEWORK", "value": "Fluent UI v9" }
],
"thumbnails": [],
"authors": [
{
"gitHubAccount": "vystartasv",
"pictureUrl": "https://github.com/vystartasv.png",
"name": "vystartasv"
}
],
"references": [
{
"name": "Microsoft Graph localeInfo resource type",
"description": "Official Microsoft Graph reference for locale information and language/region concepts.",
"url": "https://learn.microsoft.com/graph/api/resources/localeinfo?view=graph-rest-1.0"
},
{
"name": "SharePoint Framework overview",
"description": "Official Microsoft documentation for building SharePoint Framework solutions.",
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview"
},
{
"name": "Build your first SharePoint client-side web part",
"description": "Official Microsoft tutorial for a SharePoint Framework client-side web part.",
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
],
"downloadUrl": "https://pnp.github.io/download-partial/?url=https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-multilingual-intranet-switcher"
}
]
18 changes: 18 additions & 0 deletions samples/react-multilingual-intranet-switcher/config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"multilingual-intranet-switcher-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/multilingualIntranetSwitcher/MultilingualIntranetSwitcherWebPart.js",
"manifest": "./src/webparts/multilingualIntranetSwitcher/MultilingualIntranetSwitcherWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"MultilingualIntranetSwitcherWebPartStrings": "lib/webparts/multilingualIntranetSwitcher/loc/{locale}.js"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-multilingual-intranet-switcher",
"id": "c9de8f07-70ae-4d19-b2a5-7ab36a0c6b54",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"developer": {
"name": "vystartasv",
"websiteUrl": "https://github.com/vystartasv",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": "Undefined-1.0.0"
},
"metadata": {
"shortDescription": { "default": "Read-only multilingual intranet navigation" },
"longDescription": { "default": "A bounded, explicitly configured multilingual SharePoint intranet navigation and content switcher." },
"screenshotPaths": [],
"categories": ["Collaboration"]
},
"features": [
{
"title": "Multilingual intranet switcher",
"description": "Deploys the multilingual intranet switcher web part.",
"id": "5d26f3f7-46e1-46dd-a9bb-8f27ea5350c0",
"version": "1.0.0.0"
}
]
},
"paths": {
"zippedPackage": "solution/multilingual-intranet-switcher.sppkg"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx"
}
3 changes: 3 additions & 0 deletions samples/react-multilingual-intranet-switcher/gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const build = require('@microsoft/sp-build-web');

build.initialize(require('gulp'));
Loading
Loading