Browse Source

fix: fix language default in picker. Misc i18n fixes

pull/664/head
Dan Ditomaso 1 year ago
parent
commit
1c96a04523
  1. 12
      .githooks/_/pre-commit
  2. 14
      src/components/LanguageSwitcher.tsx
  3. 2
      src/components/UI/ErrorPage.tsx
  4. 71
      src/core/hooks/useLang.ts
  5. 31
      src/i18n/config.ts

12
.githooks/_/pre-commit

@ -0,0 +1,12 @@
#!/bin/sh
if [ "$SKIP_SIMPLE_GIT_HOOKS" = "1" ]; then
echo "[INFO] SKIP_SIMPLE_GIT_HOOKS is set to 1, skipping hook."
exit 0
fi
if [ -f "$SIMPLE_GIT_HOOKS_RC" ]; then
. "$SIMPLE_GIT_HOOKS_RC"
fi
deno task lint:fix && deno task format

14
src/components/LanguageSwitcher.tsx

@ -16,15 +16,11 @@ interface LanguageSwitcherProps {
disableHover?: boolean; disableHover?: boolean;
} }
export default function LanguageSwitcher( export default function LanguageSwitcher({
{ disableHover = false }: LanguageSwitcherProps, disableHover = false,
) { }: LanguageSwitcherProps) {
const { i18n } = useTranslation("ui"); const { i18n } = useTranslation("ui");
const { set: setLanguage } = useLang(); const { set: setLanguage, currentLanguage } = useLang();
const currentLanguage =
supportedLanguages.find((lang) => lang.code === i18n.language) ||
supportedLanguages[0];
const handleLanguageChange = async (languageCode: LangCode) => { const handleLanguageChange = async (languageCode: LangCode) => {
await setLanguage(languageCode, true); await setLanguage(languageCode, true);
@ -65,7 +61,7 @@ export default function LanguageSwitcher(
"group-hover:text-gray-900 dark:group-hover:text-white", "group-hover:text-gray-900 dark:group-hover:text-white",
)} )}
> >
{currentLanguage.name} {currentLanguage?.name}
</Subtle> </Subtle>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>

2
src/components/UI/ErrorPage.tsx

@ -60,7 +60,7 @@ export function ErrorPage({ error }: { error: Error }) {
<div className="hidden md:block md:max-w-64 lg:max-w-80 w-full aspect-suqare"> <div className="hidden md:block md:max-w-64 lg:max-w-80 w-full aspect-suqare">
<img <img
src="chirpy.svg" src="/chirpy.svg"
alt="Chirpy the Meshtastic error" alt="Chirpy the Meshtastic error"
className="max-w-full h-auto" className="max-w-full h-auto"
/> />

71
src/core/hooks/useLang.ts

@ -1,86 +1,55 @@
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LangCode } from "@app/i18n/config.ts"; import {
FALLBACK_LANGUAGE_CODE,
Lang,
LangCode,
supportedLanguages,
} from "../../i18n/config.ts";
import useLocalStorage from "./useLocalStorage.ts"; import useLocalStorage from "./useLocalStorage.ts";
/**
* Hook to set the i18n language
*
* @returns The `set` function
*/
const STORAGE_KEY = "language"; const STORAGE_KEY = "language";
type LanguageState = { type LanguageState = {
language: string; language: LangCode;
}; };
function useLang() { function useLang() {
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const [_, setLanguage] = useLocalStorage<LanguageState | null>( const [_, setLanguageInStorage] = useLocalStorage<LanguageState | null>(
STORAGE_KEY, STORAGE_KEY,
null, null,
); );
const regionNames = useMemo(() => { const currentLanguage = useMemo((): Lang | undefined => {
return new Intl.DisplayNames(i18n.language, { const lang = supportedLanguages.find((l) => l.code === i18n.language);
type: "region", if (lang) {
fallback: "none", return lang;
style: "long", }
}); return supportedLanguages.find((l) => l.code === FALLBACK_LANGUAGE_CODE);
}, [i18n.language]); }, [i18n.language]);
const collator = useMemo(() => { const collator = useMemo(() => {
return new Intl.Collator(i18n.language, {}); return new Intl.Collator(i18n.language, { sensitivity: "base" });
}, [i18n.language]); }, [i18n.language]);
/**
* Sets the i18n language.
*
* @param lng - The language tag to set
* @param persist - Whether to persist the language setting in local storage
*/
const set = useCallback( const set = useCallback(
async (lng: LangCode, persist = true) => { async (lng: LangCode, persist = true) => {
if (i18n.language === lng) { if (i18n.language === lng) {
return; return;
} }
try { try {
console.info("setting language:", lng);
if (persist) { if (persist) {
setLanguage({ language: lng }); setLanguageInStorage({ language: lng });
} }
await i18n.changeLanguage(lng); await i18n.changeLanguage(lng);
} catch (e) { } catch (e) {
console.warn(e); console.warn("Failed to change language:", e);
}
},
[i18n],
);
/**
* Get the localized country name
*
* @param code - Two-letter country code
*/
const getCountryName = useCallback(
(code: LangCode) => {
let name = null;
try {
name = regionNames.of(code);
} catch (e) {
console.warn(e);
} }
return name;
}, },
[regionNames], [i18n, setLanguageInStorage],
); );
/**
* Compare two strings according to the sort order of the current language
*
* @param a - The first string to compare
* @param b - The second string to compare
*/
const compare = useCallback( const compare = useCallback(
(a: string, b: string) => { (a: string, b: string) => {
return collator.compare(a, b); return collator.compare(a, b);
@ -88,7 +57,7 @@ function useLang() {
[collator], [collator],
); );
return { compare, set, getCountryName }; return { compare, set, currentLanguage };
} }
export default useLang; export default useLang;

31
src/i18n/config.ts

@ -7,6 +7,7 @@ export type Lang = {
code: Intl.Locale["language"]; code: Intl.Locale["language"];
name: string; name: string;
flag: string; flag: string;
region: string;
}; };
export type LangCode = Lang["code"]; export type LangCode = Lang["code"];
@ -21,13 +22,27 @@ function getFlagEmoji(regionCode: string): string {
return String.fromCodePoint(...codePoints); return String.fromCodePoint(...codePoints);
} }
export const supportedLanguages: Lang[] = [ const languageDefinitions = [
{ code: "de", name: "German", flag: getFlagEmoji("de") }, { code: "de", name: "Deutschland" },
{ code: "en", name: "English", flag: getFlagEmoji("us") }, { code: "en", name: "English", region: "us" },
{ code: "fi", name: "Finnish", flag: getFlagEmoji("fi") }, { code: "fi", name: "Suomi" },
{ code: "sv", name: "Swedish", flag: getFlagEmoji("se") }, { code: "sv", name: "Svenska" },
]; ];
export const supportedLanguages: Lang[] = languageDefinitions.map(
({ code, name, region }) => {
const regionCode = region ?? code;
return {
code,
name,
region: regionCode.toUpperCase(),
flag: getFlagEmoji(regionCode),
};
},
);
export const FALLBACK_LANGUAGE_CODE: LangCode = "en";
i18next i18next
.use(Backend) .use(Backend)
.use(initReactI18next) .use(initReactI18next)
@ -45,7 +60,11 @@ i18next
caches: ["localStorage"], caches: ["localStorage"],
}, },
fallbackLng: { fallbackLng: {
"default": ["en"], default: [FALLBACK_LANGUAGE_CODE],
"en-GB": [FALLBACK_LANGUAGE_CODE],
"fi": ["fi-FI"],
"sv": ["sv-SE"],
"de": ["de-DE"],
}, },
fallbackNS: ["common", "ui", "dialog"], fallbackNS: ["common", "ui", "dialog"],
debug: import.meta.env.MODE === "development", debug: import.meta.env.MODE === "development",

Loading…
Cancel
Save