Skip to content

Commit a72c2ee

Browse files
committed
feat: implement Open Library basic framework with multilingual support
1 parent 69dbf8b commit a72c2ee

18 files changed

Lines changed: 2570 additions & 282 deletions

File tree

.husky/pre-push

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
npm run build
1+
pnpm run build

components/open-library/Footer.tsx

Lines changed: 76 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from 'react';
22
import { Col, Nav, Row } from 'react-bootstrap';
33

4+
import { t } from '../../models/Translation';
5+
46
//
57
const ContentContainer: React.FC<{ children: React.ReactNode }> = ({
68
children,
@@ -21,65 +23,95 @@ const ContentContainer: React.FC<{ children: React.ReactNode }> = ({
2123
};
2224

2325
const FooterComponent = () => {
26+
// Use client-side rendering for the copyright text to avoid hydration issues
27+
const [isMounted, setIsMounted] = React.useState(false);
28+
29+
React.useEffect(() => {
30+
setIsMounted(true);
31+
}, []);
32+
2433
return (
2534
<footer className="bg-dark text-light py-4 mt-5">
2635
<ContentContainer>
2736
<Row>
2837
<Col md={4} className="mb-3 mb-md-0">
29-
<h5>Open Library</h5>
30-
<p>
31-
A community-driven library for sharing knowledge and stories.
32-
Built with open source. Powered by generosity.
33-
</p>
34-
</Col>
35-
<Col md={2} className="mb-3 mb-md-0">
36-
<h5>Quick Links</h5>
37-
<Nav className="flex-column">
38-
<Nav.Link href="#home" className="text-light px-0">
39-
Home
40-
</Nav.Link>
41-
<Nav.Link href="#catalog" className="text-light px-0">
42-
Catalog
43-
</Nav.Link>
44-
<Nav.Link href="#about" className="text-light px-0">
45-
About Us
46-
</Nav.Link>
47-
<Nav.Link href="#donate" className="text-light px-0">
48-
Donate
49-
</Nav.Link>
50-
</Nav>
51-
</Col>
52-
<Col md={3} className="mb-3 mb-md-0">
53-
<h5>Contact</h5>
54-
<p className="mb-1">Email: info@openlibrary.community</p>
55-
<p>Address: 123 Library Lane, Knowledge City, World</p>
56-
</Col>
57-
<Col md={3}>
58-
<h5>Follow Us</h5>
59-
<div>
60-
<a href="#facebook" className="text-light me-3">
61-
Facebook
38+
<h5>{t('open_library')}</h5>
39+
<p>{t('footer_description')}</p>
40+
<div className="mt-2">
41+
<a href="#github" className="text-light me-3">
42+
GitHub
6243
</a>
6344
<a href="#twitter" className="text-light me-3">
6445
Twitter
6546
</a>
66-
<a href="#instagram" className="text-light me-3">
67-
Instagram
47+
<a href="#feishu" className="text-light me-3">
48+
Feishu
6849
</a>
69-
<a href="#linkedin" className="text-light">
70-
Linkedin
50+
<a href="#instagram" className="text-light">
51+
Instagram
7152
</a>
7253
</div>
7354
</Col>
74-
</Row>
75-
<Row className="mt-3">
76-
<Col className="text-center text-muted">
77-
<small>
78-
&copy; {new Date().getFullYear()} Open Library Community. All
79-
Rights Reserved.
80-
</small>
55+
<Col md={3} className="mb-3 mb-md-0">
56+
<h5>{t('quick_links_footer')}</h5>
57+
<Nav className="flex-column">
58+
<Nav.Link href="/open-library/books" className="text-light px-0">
59+
{t('catalog_footer')}
60+
</Nav.Link>
61+
<Nav.Link href="/open-library/donate" className="text-light px-0">
62+
{t('donate_footer')}
63+
</Nav.Link>
64+
<Nav.Link
65+
href="/open-library/how-to-borrow"
66+
className="text-light px-0"
67+
>
68+
{t('how_to_borrow')}
69+
</Nav.Link>
70+
<Nav.Link href="/open-library/about" className="text-light px-0">
71+
{t('about_us_footer')}
72+
</Nav.Link>
73+
<Nav.Link href="/open-library/faq" className="text-light px-0">
74+
{t('faq')}
75+
</Nav.Link>
76+
</Nav>
77+
</Col>
78+
<Col md={5}>
79+
<h5>{t('contact')}</h5>
80+
<p className="mb-1">freeCodeCamp Chengdu Community</p>
81+
<p className="mb-1">Chengdu, Sichuan, China</p>
82+
<p className="mb-1">Email: contact@openlibrary.org</p>
83+
<p>WeChat: FCCChengdu</p>
8184
</Col>
8285
</Row>
86+
87+
<hr
88+
className="mt-4 mb-3"
89+
style={{ borderColor: 'rgba(255,255,255,0.2)' }}
90+
/>
91+
92+
{/* Use a more direct approach with inline styles to ensure visibility */}
93+
<div
94+
className="py-2"
95+
style={{
96+
textAlign: 'center',
97+
color: '#6c757d',
98+
fontSize: '0.875rem',
99+
width: '100%',
100+
display: 'block',
101+
}}
102+
>
103+
{isMounted ? (
104+
<>
105+
&copy; {new Date().getFullYear()} {t('open_library')}.{' '}
106+
{t('all_rights_reserved')}
107+
</>
108+
) : (
109+
<>
110+
&copy; {new Date().getFullYear()} Open Library. All rights
111+
reserved.
112+
</>
113+
)}
114+
</div>
83115
</ContentContainer>
84116
</footer>
85117
);

components/open-library/Layout.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import Head from 'next/head';
22
import React from 'react';
33

44
import FooterComponent from './Footer';
5-
import NavbarComponent from './Navbar';
5+
import LibraryNavbar from './Navbar';
6+
import { useOpenLibraryLayout } from './useOpenLibraryLayout';
67

7-
// 内容容器组件,使内容居中但不添加边框
88
const ContentContainer: React.FC<{ children: React.ReactNode }> = ({
99
children,
1010
}) => {
@@ -37,25 +37,24 @@ const Layout: React.FC<LayoutProps> = ({
3737
children,
3838
title = 'Open Library - Open Source Bazaar',
3939
}) => {
40+
// Apply Open Library layout styles
41+
useOpenLibraryLayout();
42+
4043
return (
4144
<>
4245
<Head>
4346
<title>{title}</title>
4447
<meta name="viewport" content="width=device-width, initial-scale=1" />
4548
</Head>
4649

47-
{/* 导航栏 */}
48-
<NavbarComponent />
50+
<LibraryNavbar />
4951

50-
{/* 主要内容 */}
5152
<main>{children}</main>
5253

53-
{/* 页脚 */}
5454
<FooterComponent />
5555
</>
5656
);
5757
};
5858

59-
// 导出布局组件和内容容器组件,以便在页面中使用
6059
export default Layout;
6160
export { ContentContainer, Layout };

components/open-library/Navbar.tsx

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1+
import dynamic from 'next/dynamic';
12
import React from 'react';
23
import { Button, Form, FormControl, Nav, Navbar } from 'react-bootstrap';
34

4-
const NavbarComponent = () => {
5+
import { t } from '../../models/Translation';
6+
7+
// 动态导入 LanguageMenu 组件,禁用 SSR
8+
const LanguageMenu = dynamic(() => import('../Navigator/LanguageMenu'), {
9+
ssr: false,
10+
});
11+
12+
const LibraryNavbar = () => {
13+
// Use a client-side only rendering approach for the brand text to avoid hydration mismatch
14+
const [isMounted, setIsMounted] = React.useState(false);
15+
16+
React.useEffect(() => {
17+
setIsMounted(true);
18+
}, []);
19+
520
return (
621
<Navbar bg="light" expand="lg" sticky="top" className="mb-4 shadow-sm">
722
{/* 使用自定义的居中容器替代 Container fluid,与页面内容保持一致的宽度和居中效果 */}
@@ -22,36 +37,27 @@ const NavbarComponent = () => {
2237
href="/open-library"
2338
className="d-flex align-items-center"
2439
>
25-
Open Library
40+
{/* Use a static placeholder during SSR and replace it with translated content after hydration */}
41+
{isMounted ? t('open_library') : 'Open Library'}
2642
</Navbar.Brand>
2743
<Navbar.Toggle aria-controls="basic-navbar-nav" />
2844
<Navbar.Collapse id="basic-navbar-nav">
2945
<Nav className="me-auto">
30-
<Nav.Link href="/open-library">Home</Nav.Link>
31-
<Nav.Link href="/open-library/books">Catalog</Nav.Link>
32-
<Nav.Link href="/open-library/about">About</Nav.Link>
33-
<Nav.Link href="/open-library/donate">Donate</Nav.Link>
46+
<Nav.Link href="/open-library">{t('home')}</Nav.Link>
47+
<Nav.Link href="/open-library/books">{t('catalog')}</Nav.Link>
48+
<Nav.Link href="/open-library/about">{t('about')}</Nav.Link>
49+
<Nav.Link href="/open-library/donate">{t('donate')}</Nav.Link>
3450
<Nav.Link href="/open-library/how-to-borrow">
35-
How to Borrow
51+
{t('how_to_borrow')}
3652
</Nav.Link>
37-
<Nav.Link href="/open-library/review">Review</Nav.Link>
53+
<Nav.Link href="/open-library/review">{t('review')}</Nav.Link>
3854
</Nav>
39-
<Form className="d-flex">
40-
<FormControl
41-
type="search"
42-
placeholder="Search Books..."
43-
className="me-2"
44-
aria-label="Search"
45-
/>
46-
<Button variant="outline-success">Search</Button>
47-
</Form>
48-
<Button variant="primary" className="ms-lg-2 mt-2 mt-lg-0">
49-
Login/Register
50-
</Button>
55+
{/* 添加语言切换菜单 */}
56+
<LanguageMenu />
5157
</Navbar.Collapse>
5258
</div>
5359
</Navbar>
5460
);
5561
};
5662

57-
export default NavbarComponent;
63+
export default LibraryNavbar;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useEffect } from 'react';
2+
3+
/**
4+
* Hook to apply Open Library layout styles
5+
* This adds the open-library class to the body and html elements
6+
* which triggers the CSS rules in styles/open-library.css
7+
*/
8+
export function useOpenLibraryLayout() {
9+
useEffect(() => {
10+
// Add open-library class to body and html
11+
document.body.classList.add('open-library');
12+
document.documentElement.classList.add('open-library');
13+
14+
// Clean up function to remove classes when component unmounts
15+
return () => {
16+
document.body.classList.remove('open-library');
17+
document.documentElement.classList.remove('open-library');
18+
};
19+
}, []);
20+
}

pages/_app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import '../styles/globals.css';
2+
import '../styles/open-library.css';
23

34
import { HTTPError } from 'koajax';
45
import { configure } from 'mobx';

pages/api/hello.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2-
import type { NextApiRequest, NextApiResponse } from 'next'
2+
import type { NextApiRequest, NextApiResponse } from 'next';
33

44
type Data = {
5-
name: string
6-
}
5+
name: string;
6+
};
77

88
export default function handler(
99
req: NextApiRequest,
10-
res: NextApiResponse<Data>
10+
res: NextApiResponse<Data>,
1111
) {
12-
res.status(200).json({ name: 'John Doe' })
12+
res.status(200).json({ name: 'John Doe' });
1313
}

0 commit comments

Comments
 (0)