fix(i18n): localize topic metadata and footer
This commit is contained in:
parent
3f1223afc3
commit
f1f6f25574
3 changed files with 4 additions and 4 deletions
|
|
@ -17,7 +17,7 @@ export function TopicPage() {
|
||||||
<Link className="back-link" to="/"><ArrowLeft size={16}/>{t.library}</Link>
|
<Link className="back-link" to="/"><ArrowLeft size={16}/>{t.library}</Link>
|
||||||
<header className="topic-hero">
|
<header className="topic-hero">
|
||||||
<div><div className="eyebrow"><span></span>{topic.domains.join(' · ')}</div><h1>{topic.title}</h1><p>{topic.summary}</p>{session.data ? <button className={`favorite-button ${topic.isFavorite ? 'active' : ''}`} onClick={() => favorite.mutate()}><Heart/>{topic.isFavorite ? t.unfavorite : t.favorite}</button> : <p className="login-hint">{t.loginHint}</p>}</div>
|
<div><div className="eyebrow"><span></span>{topic.domains.join(' · ')}</div><h1>{topic.title}</h1><p>{topic.summary}</p>{session.data ? <button className={`favorite-button ${topic.isFavorite ? 'active' : ''}`} onClick={() => favorite.mutate()}><Heart/>{topic.isFavorite ? t.unfavorite : t.favorite}</button> : <p className="login-hint">{t.loginHint}</p>}</div>
|
||||||
<dl><div><dt>Type</dt><dd>{topic.type}</dd></div><div><dt>Version</dt><dd>{topic.currentVersion ?? 'Living standard'}</dd></div><div><dt>Revision</dt><dd>#{topic.revisionNumber}</dd></div><div><dt>Verificeret</dt><dd>{new Date(topic.verifiedAt).toLocaleDateString('da-DK')}</dd></div></dl>
|
<dl><div><dt>{t.type}</dt><dd>{topic.type}</dd></div><div><dt>{t.version}</dt><dd>{topic.currentVersion ?? 'Living standard'}</dd></div><div><dt>{t.revision}</dt><dd>#{topic.revisionNumber}</dd></div><div><dt>{t.verified}</dt><dd>{new Date(topic.verifiedAt).toLocaleDateString(language === 'da' ? 'da-DK' : 'en-GB')}</dd></div></dl>
|
||||||
</header>
|
</header>
|
||||||
<Tabs.Root defaultValue="overview" className="topic-tabs">
|
<Tabs.Root defaultValue="overview" className="topic-tabs">
|
||||||
<Tabs.List aria-label="Content level"><Tabs.Trigger value="overview">{t.overview}</Tabs.Trigger><Tabs.Trigger value="deep">{t.depth}</Tabs.Trigger><Tabs.Trigger value="sources">{t.sources} <span>{topic.sources.length}</span></Tabs.Trigger></Tabs.List>
|
<Tabs.List aria-label="Content level"><Tabs.Trigger value="overview">{t.overview}</Tabs.Trigger><Tabs.Trigger value="deep">{t.depth}</Tabs.Trigger><Tabs.Trigger value="sources">{t.sources} <span>{topic.sources.length}</span></Tabs.Trigger></Tabs.List>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ interface Preferences { language: Language; setLanguage(language: Language): voi
|
||||||
export const usePreferences = create<Preferences>()(persist((set) => ({ language: 'da', setLanguage: (language) => set({ language }) }), { name: 'tech-library-preferences' }));
|
export const usePreferences = create<Preferences>()(persist((set) => ({ language: 'da', setLanguage: (language) => set({ language }) }), { name: 'tech-library-preferences' }));
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
da: { library: 'Bibliotek', favorites: 'Favoritter', editorial: 'Redaktion', login: 'Log ind', logout: 'Log ud', register: 'Opret konto', username: 'Brugernavn', password: 'Adgangskode', sourceLibrary: 'Et levende teknisk opslagsværk', headline: 'Forstå teknologien.', headlineAccent: 'Se hele sammenhængen.', intro: 'Kurateret viden om moderne web-, app- og systemudvikling — fra første begreb til arkitekturbeslutning. Hvert udsagn føres tilbage til sin kilde.', search: 'Søg efter React, databaser, sikkerhed…', curated: 'kuraterede emner', official: 'Officielle primærkilder', verified: 'Løbende verificeret', filters: 'Filtrér', reset: 'Nulstil', domain: 'Fagområde', type: 'Type', allTopics: 'Alle emner', results: 'resultater', loading: 'Henter biblioteket…', noResults: 'Ingen emner matcher din søgning.', sources: 'Kilder', overview: 'Overblik', depth: 'Fordybelse', favorite: 'Føj til favoritter', unfavorite: 'Fjern fra favoritter', loginHint: 'Log ind for at gemme favoritter.', accountTitle: 'Din Tech Library-konto', noFavorites: 'Du har endnu ingen favoritter.' },
|
da: { library: 'Bibliotek', favorites: 'Favoritter', editorial: 'Redaktion', login: 'Log ind', logout: 'Log ud', register: 'Opret konto', username: 'Brugernavn', password: 'Adgangskode', sourceLibrary: 'Et levende teknisk opslagsværk', headline: 'Forstå teknologien.', headlineAccent: 'Se hele sammenhængen.', intro: 'Kurateret viden om moderne web-, app- og systemudvikling — fra første begreb til arkitekturbeslutning. Hvert udsagn føres tilbage til sin kilde.', search: 'Søg efter React, databaser, sikkerhed…', curated: 'kuraterede emner', official: 'Officielle primærkilder', verified: 'Verificeret', filters: 'Filtrér', reset: 'Nulstil', domain: 'Fagområde', type: 'Type', version: 'Version', revision: 'Revision', allTopics: 'Alle emner', results: 'resultater', loading: 'Henter biblioteket…', noResults: 'Ingen emner matcher din søgning.', sources: 'Kilder', overview: 'Overblik', depth: 'Fordybelse', favorite: 'Føj til favoritter', unfavorite: 'Fjern fra favoritter', loginHint: 'Log ind for at gemme favoritter.', accountTitle: 'Din Tech Library-konto', noFavorites: 'Du har endnu ingen favoritter.', footer: 'Levende viden. Dokumenterede kilder.' },
|
||||||
en: { library: 'Library', favorites: 'Favorites', editorial: 'Editorial', login: 'Log in', logout: 'Log out', register: 'Create account', username: 'Username', password: 'Password', sourceLibrary: 'A living technical reference', headline: 'Understand the technology.', headlineAccent: 'See the whole picture.', intro: 'Curated knowledge about modern web, app, and systems development — from first concepts to architecture decisions. Every claim traces back to its source.', search: 'Search for React, databases, security…', curated: 'curated topics', official: 'Official primary sources', verified: 'Continuously verified', filters: 'Filter', reset: 'Reset', domain: 'Domain', type: 'Type', allTopics: 'All topics', results: 'results', loading: 'Loading the library…', noResults: 'No topics match your search.', sources: 'Sources', overview: 'Overview', depth: 'In depth', favorite: 'Add to favorites', unfavorite: 'Remove from favorites', loginHint: 'Log in to save favorites.', accountTitle: 'Your Tech Library account', noFavorites: 'You do not have any favorites yet.' }
|
en: { library: 'Library', favorites: 'Favorites', editorial: 'Editorial', login: 'Log in', logout: 'Log out', register: 'Create account', username: 'Username', password: 'Password', sourceLibrary: 'A living technical reference', headline: 'Understand the technology.', headlineAccent: 'See the whole picture.', intro: 'Curated knowledge about modern web, app, and systems development — from first concepts to architecture decisions. Every claim traces back to its source.', search: 'Search for React, databases, security…', curated: 'curated topics', official: 'Official primary sources', verified: 'Verified', filters: 'Filter', reset: 'Reset', domain: 'Domain', type: 'Type', version: 'Version', revision: 'Revision', allTopics: 'All topics', results: 'results', loading: 'Loading the library…', noResults: 'No topics match your search.', sources: 'Sources', overview: 'Overview', depth: 'In depth', favorite: 'Add to favorites', unfavorite: 'Remove from favorites', loginHint: 'Log in to save favorites.', accountTitle: 'Your Tech Library account', noFavorites: 'You do not have any favorites yet.', footer: 'Living knowledge. Documented sources.' }
|
||||||
} as const;
|
} as const;
|
||||||
export const useText = () => { const language = usePreferences((state) => state.language); return { language, t: messages[language] }; };
|
export const useText = () => { const language = usePreferences((state) => state.language); return { language, t: messages[language] }; };
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export function AppLayout() {
|
||||||
const { language, t } = useText(); const setLanguage = usePreferences((state) => state.setLanguage); const client = useQueryClient();
|
const { language, t } = useText(); const setLanguage = usePreferences((state) => state.setLanguage); const client = useQueryClient();
|
||||||
const session = useQuery({ queryKey: ['user-session'], queryFn: userApi.me, retry: false });
|
const session = useQuery({ queryKey: ['user-session'], queryFn: userApi.me, retry: false });
|
||||||
const logout = useMutation({ mutationFn: () => userApi.logout(session.data!.csrfToken), onSuccess: () => { client.setQueryData(['user-session'], null); client.invalidateQueries({ queryKey: ['topics'] }); } });
|
const logout = useMutation({ mutationFn: () => userApi.logout(session.data!.csrfToken), onSuccess: () => { client.setQueryData(['user-session'], null); client.invalidateQueries({ queryKey: ['topics'] }); } });
|
||||||
return <div className="app-shell"><header className="topbar"><Link className="brand" to="/"><span className="brand-mark">TL</span><span>Tech Library</span></Link><nav aria-label="Primary navigation"><NavLink to="/" end><BookOpen size={17}/>{t.library}</NavLink>{session.data && <NavLink to="/favoritter"><Heart size={17}/>{t.favorites}</NavLink>}<NavLink to="/admin"><ShieldCheck size={17}/>{t.editorial}</NavLink><div className="language-switch"><button className={language === 'da' ? 'active' : ''} onClick={() => setLanguage('da')}>DA</button><button className={language === 'en' ? 'active' : ''} onClick={() => setLanguage('en')}>EN</button></div>{session.data ? <button className="account-button" onClick={() => logout.mutate()} title={t.logout}><LogOut size={17}/><span>{session.data.username}</span></button> : <AccountDialog/>}</nav></header><main><Outlet /></main><footer><span>Living knowledge. Documented sources.</span><a href="https://git.p20.dev/christian/TechLibrary" target="_blank" rel="noreferrer"><Github size={16}/>Source</a></footer></div>;
|
return <div className="app-shell"><header className="topbar"><Link className="brand" to="/"><span className="brand-mark">TL</span><span>Tech Library</span></Link><nav aria-label="Primary navigation"><NavLink to="/" end><BookOpen size={17}/>{t.library}</NavLink>{session.data && <NavLink to="/favoritter"><Heart size={17}/>{t.favorites}</NavLink>}<NavLink to="/admin"><ShieldCheck size={17}/>{t.editorial}</NavLink><div className="language-switch"><button className={language === 'da' ? 'active' : ''} onClick={() => setLanguage('da')}>DA</button><button className={language === 'en' ? 'active' : ''} onClick={() => setLanguage('en')}>EN</button></div>{session.data ? <button className="account-button" onClick={() => logout.mutate()} title={t.logout}><LogOut size={17}/><span>{session.data.username}</span></button> : <AccountDialog/>}</nav></header><main><Outlet /></main><footer><span>{t.footer}</span><a href="https://git.p20.dev/christian/TechLibrary" target="_blank" rel="noreferrer"><Github size={16}/>Source</a></footer></div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AccountDialog() {
|
function AccountDialog() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue