- Extract 485 remaining hardcoded English UI strings across
app, cloud, components, contexts, hooks, and modules
- Append them to a new 'untranslated' namespace in en.ts as baseline
- Mirror same English values in zh.ts as placeholders to maintain key alignment
- No Chinese translations added yet - to be translated in future pass
- Localize PeerEditIPModal config and buttons
- Localize PeerSSHToggle dialogs, tooltips, help text and callouts
- Localize AddRouteDropdownButton and RemoteJobDropdownButton
- Localize PeerRoutesTable and RouteMetricCell tooltips
- Localize AccessTokensTable empty state
- Localize SetupKeysTable filters, options and empty states
- Add missing translation keys to en.ts and zh.ts
- Fix t.rich tag usage for peerOfflineRemoteJob
- Fix singularize usage with ICU plural activePoliciesCount
- Normalize formatting with prettier
- Remove empty progress.md
- Refactor both table column defs from const to function with t() param
- Add lastUsed, lastUsedOn, nameAndKey, expires keys to common/setupKeys namespaces
- Refactor UsersTableColumns from const to function with t() param
- Add useTranslations('users') to UsersTable component
- Replace hardcoded Name, Role, Status, Groups, Last Login headers
- Refactor PeersTableColumns from const to function with t() param
- Add useTranslations('peers') to PeersTable component
- Replace hardcoded Name, Address, Groups, OS, Serial number, Version headers
- Add selectAll, selectRow, unknown keys
Gate premium endpoints so the open-source dashboard stops calling
endpoints the open-source management server does not serve:
- MSP/Distributor: only fetch /integrations/msp, /integrations/msp/switcher
and /integrations/msp/reseller on NetBird Cloud
- EDR bypass: only fetch /peers/edr/bypassed when licensed
- Event Streaming: gate /integrations/event-streaming GETs on the license
in addition to the existing permission check
- useIsLicensed: cache the licensed-only probe result (per API origin, 1h
TTL) so it no longer re-fires on every page load
- Team > Users: show the Identity Provider Sync tile in open-source mode so
IdP sync remains discoverable
- Add userRole, userRoleHelp to users namespace
- Add expiresIn, expiresInHelp to users namespace
- Replace hardcoded User Role, Expires in with t() calls
- Add useTranslations to UserInviteModal
Brings the unified dashboard into the open-source repo. Premium features
ship in the open code, gated at runtime via NETBIRD_CLOUD and
NETBIRD_LICENSED, with upgrade prompts for unlicensed self-hosted
deployments. Adds the cloud-only feature areas (billing, integrations,
MSP, traffic events, notifications) and the Playwright e2e suite.
NetworkResourceAccessControl: "Access Control Policies" label + help text
PeerExpirationToggle: session expiration description, tooltip text (setup
key disabled, no permission, global setting disabled, go to settings)
PeerExpirationSettings: notification texts, inactivity expiration text
New keys in peers + networks namespaces (en + zh).
Resolve merge conflict in IdentityProviderModal.tsx by adopting upstream
main's new 'Endpoint URLs' section (Redirect/Callback + Logout URLs from
PR #657), then re-applying i18n: useTranslations import, Cancel button
localization, and new translation keys for the added endpoint/logout text
(endpointUrls, redirectCallback, logoutLabel, notAllProvidersLogout,
learnMore) in en + zh.
Also brings in upstream DNS Zones & Setup modal improvements (#669),
banner IPv6 link (#662), remote jobs docs link (#664).
Replaces all hardcoded English strings in DangerZoneTab with
useTranslations("settings") calls: card title, warning paragraph,
confirm/cancel dialog, notify messages, and button label.
i18n keys added to settings namespace (en + zh).
Replaces all hardcoded "Cancel" button text with {t("cancel")} using the
common namespace in every modal component, plus a few adjacent button
texts. Adds import { useTranslations } + useTranslations("common") to
each component that needed it.
Files covered: access-tokens, jobs, networks, peer, posture-checks,
remote-access/ssh, reverse-proxy/auth, reverse-proxy/clusters,
reverse-proxy/domain, routes, settings, setup-keys, users — all modal
dialogs with Cancel buttons now respect the active locale.
Covers four high-visibility hardcoded headings and surrounding text:
- User detail page: "Access Tokens" tab label + heading + description
- UserPeersSection: "Peers" heading + "View all peers..." description
- OnboardingIntent: "Get started with NetBird" title
- OnboardingAddResource: "Add your first resource" title
i18n keys added: users.accessTokens, users.accessTokensDescription,
peers.userPeersDescription, onboarding.title, onboarding.addResource
(en + zh translations).
Replaces all hardcoded English text in 8 settings tab components and the
SessionLost page with useTranslations() calls, so they render in the user's
active locale (English or Chinese):
- Settings tabs: Authentication, Clients, Groups, Networks, Danger Zone,
Setup Keys, Identity Providers, Permissions — breadcrumbs and h1 headers
now use the existing settings namespace keys.
- SessionLost: "Session Expired" description + "Login" button localized
via the auth namespace.
- i18n keys added: auth.sessionExpiredDescription, dns.nameserversDescription,
dns.zonesDescription (en + zh translations).
Previously the locale was hardcoded to "zh" in AppLayout's
NextIntlClientProvider, so every user saw Chinese with no way to switch.
This adds proper client-side locale resolution suitable for the static
export build (`output: "export"` — no server runtime / middleware):
- New single source of truth `src/i18n/config.ts` (locales, defaultLocale,
message catalog, cookie name, timezones) consumed by request.ts,
routing.ts, navigation.ts and the new provider.
- New pure detection helpers `src/i18n/detection.ts`:
cookie (NEXT_LOCALE) -> browser language (navigator.languages) ->
default. All branches are SSR/build-safe to avoid hydration mismatches.
- New `LocaleProvider` context wraps NextIntlClientProvider, resolves the
locale on mount, syncs <html lang>, and exposes { locale, setLocale }.
- AppLayout now uses LocaleProvider instead of the hardcoded "zh" wiring.
- New `LanguageSwitcher` in the header and a new `Language` tab in
Settings, both persisting the choice via the cookie for reloads.
Default locale is now English ("en"); Chinese remains available via the
browser-language fallback or the explicit switcher. Includes a manual
assertion script (detection.test.ts) covering cookie precedence, browser
fallback, persistence and SSR safety.