mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-20 07:21:27 +02:00
update language switcher, remove flags
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 512 512">
|
||||
<path fill="#ffce00" d="M0 341.3h512V512H0z"/>
|
||||
<path fill="#000001" d="M0 0h512v170.7H0z"/>
|
||||
<path fill="red" d="M0 170.7h512v170.6H0z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 232 B |
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 512 512">
|
||||
<path fill="#012169" d="M0 0h512v512H0z"/>
|
||||
<path fill="#FFF" d="M512 0v64L322 256l190 187v69h-67L254 324 68 512H0v-68l186-187L0 74V0h62l192 188L440 0z"/>
|
||||
<path fill="#C8102E" d="m184 324 11 34L42 512H0v-3zm124-12 54 8 150 147v45zM512 0 320 196l-4-44L466 0zM0 1l193 189-59-8L0 49z"/>
|
||||
<path fill="#FFF" d="M176 0v512h160V0zM0 176v160h512V176z"/>
|
||||
<path fill="#C8102E" d="M0 208v96h512v-96zM208 0v512h96V0z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 505 B |
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-hu" viewBox="0 0 512 512">
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="#fff" d="M512 512H0V0h512z"/>
|
||||
<path fill="#388d00" d="M512 512H0V341.3h512z"/>
|
||||
<path fill="#d43516" d="M512 170.8H0V.1h512z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 273 B |
@@ -4,7 +4,7 @@ import * as Popover from "@radix-ui/react-popover";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import { Command } from "cmdk";
|
||||
import { errorDialog } from "@/lib/dialogs.ts";
|
||||
import { CheckIcon, ChevronDown, Search } from "lucide-react";
|
||||
import { CheckIcon, ChevronDown, LanguagesIcon, Search } from "lucide-react";
|
||||
import { Preferences } from "@bindings/services";
|
||||
import { LanguageCode, type Language } from "@bindings/i18n/models.js";
|
||||
import { HelpText } from "@/components/typography/HelpText";
|
||||
@@ -13,43 +13,17 @@ import { loadLanguages } from "@/lib/i18n";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { formatErrorMessage } from "@/lib/errors";
|
||||
|
||||
// Flags live alongside the rest of the SVG flag library under
|
||||
// assets/flags/1x1 and are filename-matched to the language code
|
||||
// (de → de.svg, en → en.svg, hu → hu.svg). Vite eager-globs them at
|
||||
// build time; the JS bundle only holds URL refs, not the SVG bytes.
|
||||
const FLAG_URLS = import.meta.glob<string>("@/assets/flags/1x1/*.svg", {
|
||||
eager: true,
|
||||
import: "default",
|
||||
query: "?url",
|
||||
});
|
||||
// Intentionally no flag icons here: flags represent countries, not
|
||||
// languages (German is spoken across DE/AT/CH; English across US/UK/AU/
|
||||
// etc.). Each label shows the endonym followed by the englishName in
|
||||
// parentheses when the two differ (e.g. "Deutsch (German)"), in both
|
||||
// the trigger and the dropdown rows.
|
||||
// See: https://www.flagsarenotlanguages.com/blog/
|
||||
|
||||
const flagByCode: Record<string, string> = {};
|
||||
for (const path in FLAG_URLS) {
|
||||
const match = path.match(/1x1\/([^/]+)\.svg$/);
|
||||
if (match) flagByCode[match[1]] = FLAG_URLS[path];
|
||||
}
|
||||
|
||||
const flagFor = (code: string): string | undefined => flagByCode[code.toLowerCase().split("-")[0]];
|
||||
|
||||
function Flag({ code, label }: { code: string; label: string }) {
|
||||
const src = flagFor(code);
|
||||
if (!src) {
|
||||
return (
|
||||
<span
|
||||
className={"h-3.5 w-3.5 rounded-full bg-nb-gray-800 shrink-0 inline-block"}
|
||||
aria-hidden
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
alt={label}
|
||||
className={"h-3.5 w-3.5 rounded-full object-cover shrink-0 select-none"}
|
||||
draggable={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const labelFor = (lang: Language): string =>
|
||||
lang.englishName && lang.englishName !== lang.displayName
|
||||
? `${lang.displayName} (${lang.englishName})`
|
||||
: lang.displayName;
|
||||
|
||||
export function LanguagePicker() {
|
||||
const { t, i18n } = useTranslation();
|
||||
@@ -121,9 +95,9 @@ export function LanguagePicker() {
|
||||
"disabled:opacity-50",
|
||||
)}
|
||||
>
|
||||
{current && <Flag code={current.code} label={current.displayName} />}
|
||||
<LanguagesIcon size={16} className={"text-nb-gray-200 shrink-0"} />
|
||||
<span className={"truncate flex-1 text-left"}>
|
||||
{current?.displayName ?? "—"}
|
||||
{current ? labelFor(current) : "—"}
|
||||
</span>
|
||||
<ChevronDown size={12} className={"text-nb-gray-400 shrink-0"} />
|
||||
</button>
|
||||
@@ -193,12 +167,8 @@ export function LanguagePicker() {
|
||||
"data-[selected=true]:bg-nb-gray-850 data-[selected=true]:text-nb-gray-50",
|
||||
)}
|
||||
>
|
||||
<Flag
|
||||
code={lang.code}
|
||||
label={lang.displayName}
|
||||
/>
|
||||
<span className={"flex-1 truncate"}>
|
||||
{lang.displayName}
|
||||
<span className={"flex-1 min-w-0 truncate"}>
|
||||
{labelFor(lang)}
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
|
||||
@@ -29,17 +29,21 @@ for (const path in bundleModules) {
|
||||
}
|
||||
|
||||
// detectBrowserLanguage walks navigator.language + navigator.languages
|
||||
// and returns the first base code ("de" from "de-DE") that has a shipped
|
||||
// bundle. Returns null when none match, so the caller can fall back to
|
||||
// English. We only ever match against the lowercased base — region tags
|
||||
// don't have separate bundles today.
|
||||
// and returns the first shipped bundle that matches. We try an exact
|
||||
// case-insensitive match first (so "en-GB" picks the en-GB bundle when
|
||||
// shipped), then fall back to the base code ("de" from "de-DE"). Returns
|
||||
// null when nothing matches, so the caller can fall back to English.
|
||||
function detectBrowserLanguage(available: string[]): string | null {
|
||||
const tags = [navigator.language, ...(navigator.languages ?? [])].filter(
|
||||
(tag): tag is string => typeof tag === "string" && tag.length > 0,
|
||||
);
|
||||
const byLower = new Map(available.map((code) => [code.toLowerCase(), code]));
|
||||
for (const tag of tags) {
|
||||
const base = tag.toLowerCase().split("-")[0];
|
||||
if (available.includes(base)) return base;
|
||||
const lower = tag.toLowerCase();
|
||||
const exact = byLower.get(lower);
|
||||
if (exact) return exact;
|
||||
const base = byLower.get(lower.split("-")[0]);
|
||||
if (base) return base;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"languages": [
|
||||
{"code": "en", "displayName": "English", "englishName": "English"},
|
||||
{"code": "en", "displayName": "English (US)", "englishName": "English (US)"},
|
||||
{"code": "de", "displayName": "Deutsch", "englishName": "German"},
|
||||
{"code": "hu", "displayName": "Magyar", "englishName": "Hungarian"}
|
||||
]
|
||||
|
||||
@@ -245,7 +245,7 @@
|
||||
"settings.troubleshooting.duration.suffix": "Minute(s)",
|
||||
"settings.troubleshooting.create": "Create Bundle",
|
||||
"settings.troubleshooting.progress.description": "Collecting logs, system details, and connection state. This usually takes a moment — keep this window open until it completes.",
|
||||
"settings.troubleshooting.cancelling": "Cancelling…",
|
||||
"settings.troubleshooting.cancelling": "Canceling…",
|
||||
"settings.troubleshooting.done.uploadedTitle": "Debug bundle successfully uploaded!",
|
||||
"settings.troubleshooting.done.savedTitle": "Bundle saved",
|
||||
"settings.troubleshooting.done.uploadedDescription": "Share the upload key below with NetBird support. A local copy was also saved on your device.",
|
||||
@@ -261,7 +261,7 @@
|
||||
"settings.troubleshooting.stage.restoring": "Restoring previous log level…",
|
||||
"settings.troubleshooting.stage.bundling": "Generating debug bundle…",
|
||||
"settings.troubleshooting.stage.uploading": "Uploading to NetBird…",
|
||||
"settings.troubleshooting.stage.cancelling": "Cancelling…",
|
||||
"settings.troubleshooting.stage.cancelling": "Canceling…",
|
||||
|
||||
"settings.about.client": "NetBird Client v{version}",
|
||||
"settings.about.clientName": "NetBird Client",
|
||||
|
||||
Reference in New Issue
Block a user