(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
98
fork

Configure Feed

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

qol

+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(() => {