(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

qol

scanash00 5a912e94 2b5a038b

+46 -14
+32 -2
web/astro.config.mjs
··· 5 5 import node from "@astrojs/node"; 6 6 import { fileURLToPath } from "url"; 7 7 import { dirname, resolve, join } from "path"; 8 - import { readdirSync, existsSync } from "fs"; 8 + import { readdirSync, existsSync, readFileSync } from "fs"; 9 9 10 10 const __dirname = dirname(fileURLToPath(import.meta.url)); 11 11 12 12 const API_PORT = process.env.API_PORT || 8081; 13 + 14 + function i18nResourcesPlugin() { 15 + const virtualId = "virtual:i18n-resources"; 16 + const resolvedId = "\0" + virtualId; 17 + return { 18 + name: "i18n-resources", 19 + resolveId(id) { 20 + if (id === virtualId) return resolvedId; 21 + }, 22 + load(id) { 23 + if (id !== resolvedId) return; 24 + const localesDir = join(__dirname, "public/locales"); 25 + const resources = /** @type {Record<string, unknown>} */ ({}); 26 + readdirSync(localesDir, { withFileTypes: true }) 27 + .filter( 28 + (d) => 29 + d.isDirectory() && 30 + existsSync(join(localesDir, d.name, "translation.json")), 31 + ) 32 + .forEach((d) => { 33 + const content = readFileSync( 34 + join(localesDir, d.name, "translation.json"), 35 + "utf-8", 36 + ); 37 + resources[d.name] = { translation: JSON.parse(content) }; 38 + }); 39 + return `export const resources = ${JSON.stringify(resources)};`; 40 + }, 41 + }; 42 + } 13 43 14 44 function i18nLanguagesPlugin() { 15 45 const virtualId = "virtual:i18n-languages"; ··· 55 85 defaultStrategy: "viewport", 56 86 }, 57 87 vite: { 58 - plugins: [i18nLanguagesPlugin()], 88 + plugins: [i18nResourcesPlugin(), i18nLanguagesPlugin()], 59 89 resolve: { 60 90 alias: { 61 91 "@": resolve(__dirname, "src"),
+7
web/src/env.d.ts
··· 1 1 /// <reference types="astro/client" /> 2 2 3 + declare module "virtual:i18n-resources" { 4 + export const resources: Record< 5 + string, 6 + { translation: Record<string, unknown> } 7 + >; 8 + } 9 + 3 10 declare module "virtual:i18n-languages" { 4 11 export const languages: { code: string; name: string; nativeName: string }[]; 5 12 }
+2 -8
web/src/i18n.ts
··· 1 1 import i18n from "i18next"; 2 2 import { initReactI18next } from "react-i18next"; 3 - import HttpBackend from "i18next-http-backend"; 4 3 import LanguageDetector from "i18next-browser-languagedetector"; 4 + import { resources } from "virtual:i18n-resources"; 5 5 6 6 i18n 7 - .use(HttpBackend) 8 7 .use(LanguageDetector) 9 8 .use(initReactI18next) 10 9 .init({ 11 10 fallbackLng: "en", 11 + resources, 12 12 ns: ["translation"], 13 13 defaultNS: "translation", 14 - backend: { 15 - loadPath: "/locales/{{lng}}/{{ns}}.json", 16 - }, 17 14 detection: { 18 15 order: ["localStorage", "navigator", "htmlTag"], 19 16 caches: ["localStorage"], 20 17 }, 21 18 interpolation: { 22 19 escapeValue: false, 23 - }, 24 - react: { 25 - useSuspense: false, 26 20 }, 27 21 }); 28 22
+5 -4
web/src/views/core/Settings.tsx
··· 86 86 const preferences = useStore($preferences); 87 87 const { i18n: i18nInstance } = useTranslation(); 88 88 const [currentLanguage, setCurrentLanguage] = useState( 89 - () => i18nInstance.resolvedLanguage ?? i18nInstance.language ?? "en" 89 + () => i18nInstance.resolvedLanguage ?? i18nInstance.language ?? "en", 90 90 ); 91 91 92 92 useEffect(() => { 93 - const handler = (lng: string) => 94 - setCurrentLanguage(lng); 93 + const handler = (lng: string) => setCurrentLanguage(lng); 95 94 i18nInstance.on("languageChanged", handler); 96 - return () => { i18nInstance.off("languageChanged", handler); }; 95 + return () => { 96 + i18nInstance.off("languageChanged", handler); 97 + }; 97 98 }, [i18nInstance]); 98 99 99 100 useEffect(() => {