diff --git a/data/onefile_join.md b/data/onefile.md similarity index 100% rename from data/onefile_join.md rename to data/onefile.md diff --git a/data/onefile_en.md b/data/onefile_en.md new file mode 100644 index 00000000..bc343810 --- /dev/null +++ b/data/onefile_en.md @@ -0,0 +1,44 @@ +## I. Introduction + +Are you already overwhelmed by the monotonous CRUD business code, and tired of the endless process of installing dependencies and deploying environments just to create a webpage, followed by the never-ending updates of dependency versions? + +The OneFile programming challenge with a single file is here to awaken your interest and pleasure in coding! + +Write the code you want, with the simplest code in **just one file**, and find back the original joy of programming. + +## II. Requirements + +When you step away from modern frameworks and libraries, the opportunity to showcase your talent comes! If you have an idea, you should write it down quickly. It may not need to be very complex; perhaps one file is enough. + +The **one-file** programming challenge has the following requirements: + +- A single file, not compressed, with a size smaller than 1 MB +- Clear code structure, including comments, and fewer than 5000 lines of code +- Any programming language is allowed, but cannot only contain Markdown files +- No external files are introduced (images, videos, CSS, etc.) +- The project function is complete and has learning or usage value +- (Optional) Up to one open-source library or framework may be depended on + +The collected works will be promoted through HelloGitHub's accounts across the entire network (more than 200,000 fans), allowing your project to help more people and be loved by more people. + +In addition to submitting your own works, you can also share projects collected from the internet, but the author and source of the code must be indicated. + +## III. Submission Methods + +**First Method**: Submit through the form: [Click Here](https://hellogithub.yuque.com/forms/share/4f0bf06b-2991-4f7e-a860-5b76337b7b5b) + +**Second Method**: Submit through GitHub Pull Request steps: + +1. Fork this project +2. Categorize the work according to the language and put it in the `src/appropriate directory` +3. Submit PR +4. Fill in the relevant information in the PR description area +5. You will receive a reply on whether to include it within three days + +Tips: [Click Here](https://docs.github.com/cn/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) to view the PR method + +## IV. Final Thoughts + +Every project starts with a single file and becomes what you see today through continuous iteration. The one file you submit today is a seed. + +> The best time to plant a tree is twenty years ago. The second-best time is now. diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 07451f41..200bcb16 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -27,12 +27,12 @@ "login": "Sign In" }, "advert": { - "desc": "Server expires by", + "desc": "expire in", "desc2": "Buy me a coffee", "next": "Need to gold", "next2": "Next target by", "day": "D", - "year": "" + "year": "Y" }, "site_stats": { "user": "#Users", diff --git a/public/locales/en/onefile.json b/public/locales/en/onefile.json new file mode 100644 index 00000000..aa64daad --- /dev/null +++ b/public/locales/en/onefile.json @@ -0,0 +1,26 @@ +{ + "title": "Single-File Open Source Projects", + "description": "A collection of single-file, copy-to-run open source projects", + "click": "Click", + "p_text": "OneFile gathers open source projects that are single-file, easy to run, and straightforward to understand. Including games, compilers, servers, tools, utility libraries, and other interesting open source projects that you can run just by copying the code. Click to view the source code and try it online.", + "p_text2": "OneFile is an open source project where you can find interesting and easy-to-run programs. It's also a programming challenge where you can submit a single file to take on the challenge.", + "p_text3": "Join the OneFile programming challenge and write some interesting code with just one file!", + "code": { + "copy_success": "Source code copied, paste it into a file to run", + "copy_fail": "Copy failed", + "title": "OneFile: {{name}} Source Code", + "nav": "Code", + "author": "Author: {{author}}", + "language": "Language: {{language}}", + "package": "Deps", + "nopackage": "No Deps", + "read": "{{num}} views", + "play": "Play", + "vite": "Visit", + "copy": "Copy" + }, + "join": { + "title": "Join the OneFile Programming Challenge", + "nav": "Join" + } +} diff --git a/public/locales/zh/onefile.json b/public/locales/zh/onefile.json new file mode 100644 index 00000000..3cd219a3 --- /dev/null +++ b/public/locales/zh/onefile.json @@ -0,0 +1,26 @@ +{ + "title": "仅一个文件的开源项目", + "description": "仅一个文件、复制即可运行的开源项目集合", + "click": "点击", + "p_text": "OneFile 是汇集了仅一个文件、运行简单、一看就懂的开源项目。包括:游戏、编译器、服务器、工具、实用库等有趣的开源项目,而且复制代码就能跑,点击即可在线查看源码和试玩。", + "p_text2": "「OneFile」是一个开源项目,在这里你可以找到有趣运行简单的程序。同时它也是一个编程挑战,你也可以提交一个文件接受挑战。", + "p_text3": "加入 OneFile 编程挑战,一个文件而已就写点有趣的代码吧!", + "code": { + "copy_success": "源码已复制,粘贴到文件中即可运行", + "copy_fail": "复制失败", + "title": "OneFile: {{name}} 源码", + "nav": "源码", + "author": "作者 {{author}}", + "language": "主语言 {{language}}", + "package": "有依赖", + "nopackage": "无依赖", + "read": "{{num}} 次查看", + "play": "试玩", + "vite": "访问", + "copy": "复制" + }, + "join": { + "title": "加入 OneFile 编程挑战", + "nav": "加入" + } +} diff --git a/src/components/buttons/LanguageSwitcher.tsx b/src/components/buttons/LanguageSwitcher.tsx index 8214bcff..01cfeeab 100644 --- a/src/components/buttons/LanguageSwitcher.tsx +++ b/src/components/buttons/LanguageSwitcher.tsx @@ -37,7 +37,7 @@ const LanguageSwitcher = (props: LanguageSwitchProps) => { }; if (props.type === 'text') { - const isChinese = locale == 'en' ? false : true; + const isChinese = locale == 'zh' ? true : false; const buttonText = isChinese ? 'English' : '简体中文'; return ( changeLanguage(isChinese ? 'en' : 'zh')}> @@ -74,7 +74,7 @@ const LanguageSwitcher = (props: LanguageSwitchProps) => { aria-haspopup='true' aria-expanded={isHovered ? 'true' : 'false'} > - {selectedLocale === 'en' ? 'EN' : '中文'} + {selectedLocale === 'zh' ? '中文' : 'EN'} diff --git a/src/components/side/Ad.tsx b/src/components/side/Ad.tsx index 19d95a95..389413c6 100644 --- a/src/components/side/Ad.tsx +++ b/src/components/side/Ad.tsx @@ -1,5 +1,6 @@ import Link from 'next/link'; import { MouseEventHandler, useState } from 'react'; +import { GoServer } from 'react-icons/go'; import { VscClose } from 'react-icons/vsc'; import { redirectRecord } from '@/services/home'; @@ -19,6 +20,7 @@ interface Props { interface AdContentProps { t: (key: string) => string; data: AdvertItem; + i18n_lang?: string; } interface ImageAdContentProps { @@ -71,11 +73,12 @@ export default function Ad({ data, className, t, i18n_lang }: Props) { ); - const AdServerInfo = ({ data, t }: AdContentProps) => ( + const AdServerInfo = ({ data, t, i18n_lang }: AdContentProps) => (
- + + {i18n_lang != 'zh' && } {t('advert.desc')} - {data.day} + {data.day} {t('advert.day')}
@@ -92,7 +95,7 @@ export default function Ad({ data, className, t, i18n_lang }: Props) {
ad
@@ -101,7 +104,7 @@ export default function Ad({ data, className, t, i18n_lang }: Props) {
- +
diff --git a/src/pages/onefile/code/[oid].tsx b/src/pages/onefile/code/[oid].tsx index 8432c2df..9bf6eab6 100644 --- a/src/pages/onefile/code/[oid].tsx +++ b/src/pages/onefile/code/[oid].tsx @@ -1,6 +1,8 @@ import copy from 'copy-to-clipboard'; import { GetServerSideProps, NextPage } from 'next'; import Link from 'next/link'; +import { useTranslation } from 'next-i18next'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { GoClippy, GoLink, GoPlay } from 'react-icons/go'; import { CodeRender } from '@/components/mdRender/MDRender'; @@ -10,23 +12,24 @@ import Seo from '@/components/Seo'; import ToTop from '@/components/toTop/ToTop'; import { getOnefileContent } from '@/services/article'; -import { numFormat } from '@/utils/util'; +import { getClientIP, numFormat } from '@/utils/util'; import { OneFileProps, OneItem } from '@/types/article'; const OneFileDetailPage: NextPage = ({ onefile }) => { + const { t } = useTranslation('onefile'); const handleCopy = (onefile: OneItem) => { const text = onefile.source_code; if (copy(text)) { - message.success('源码已复制,粘贴到文件中即可运行!'); - } else message.error('复制失败'); + message.success(t('code.copy_success')); + } else message.error(t('code.copy_fail')); }; return ( <> - +
- +
@@ -39,19 +42,21 @@ const OneFileDetailPage: NextPage = ({ onefile }) => {
- 作者 {onefile.author} + {t('code.author', { author: onefile.author })} · - 主语言 {onefile.language} + + {t('code.language', { language: onefile.language })} + · - {onefile.package ? ( - 有依赖 - ) : ( - 无依赖 - )} + + {onefile.package ? t('code.package') : t('code.package')} + · - {numFormat(onefile.uv_count, 1, 1000)} 次查看 -
- {onefile.demo_url ? ( + {t('code.read', { + num: numFormat(onefile.uv_count, 1, 1000), + })} +
- {onefile.demo_url ? ( + {onefile.demo_url && ( = ({ onefile }) => { rel='noreferrer' > - 试玩 + {t('code.play')} - ) : ( - <> )} = ({ onefile }) => { rel='noreferrer' > - 访问 + {t('code.vite')} = ({ onefile }) => { onClick={() => handleCopy(onefile)} > - 复制 + {t('code.copy')}
@@ -136,17 +137,9 @@ export default OneFileDetailPage; export const getServerSideProps: GetServerSideProps = async ({ query, req, + locale, }) => { - let ip; - if (req.headers['x-forwarded-for']) { - ip = req.headers['x-forwarded-for'] as string; - ip = ip.split(',')[0] as string; - } else if (req.headers['x-real-ip']) { - ip = req.headers['x-real-ip'] as string; - } else { - ip = req.socket.remoteAddress as string; - } - + const ip = getClientIP(req); const oid = query['oid'] as string; const data = await getOnefileContent(ip, oid); if (!data.success) { @@ -157,6 +150,10 @@ export const getServerSideProps: GetServerSideProps = async ({ return { props: { onefile: data.data, + ...(await serverSideTranslations(locale as string, [ + 'common', + 'onefile', + ])), }, }; } diff --git a/src/pages/onefile/index.tsx b/src/pages/onefile/index.tsx index d9af6835..fc8ada24 100644 --- a/src/pages/onefile/index.tsx +++ b/src/pages/onefile/index.tsx @@ -1,17 +1,21 @@ import { GetServerSideProps, NextPage } from 'next'; import Link from 'next/link'; import { useRouter } from 'next/router'; +import { Trans, useTranslation } from 'next-i18next'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import Navbar from '@/components/navbar/Navbar'; import Seo from '@/components/Seo'; import { getOnefile } from '@/services/article'; +import { getClientIP } from '@/utils/util'; import { OneItemsResp, TableOneItem } from '@/types/article'; type column = { key: string; title: string; + title_en: string; width: number | string; render: (row: any, index: number) => any; }; @@ -21,16 +25,18 @@ const columns: any[] = [ { key: 'oid', title: '名称', + title_en: 'Name', width: 130, render: (row: TableOneItem) => { return {row.name}; }, }, - { key: 'language', title: '语言', width: 100 }, - { key: 'suggestions', title: '描述' }, + { key: 'language', title: '语言', title_en: 'Language', width: 100 }, + { key: 'suggestions', title: '描述', title_en: 'Description' }, ]; const OneFilePage: NextPage = ({ data }) => { + const { t, i18n } = useTranslation('onefile'); const router = useRouter(); const handleCode = (oid: string) => { @@ -39,13 +45,10 @@ const OneFilePage: NextPage = ({ data }) => { return ( <> - +
- +
= ({ data }) => { src='https://img.hellogithub.com/article/tK30nYW8bMiPOdB_1647991896.png' >

- OneFile 汇集了一个文件、运行简单、一看就懂的开源项目。 - 包括:游戏、编译器、服务器、工具、实用库等有趣的开源项目,而且 - 复制代码就能跑,点击即可在线查看源码和试玩。 +

@@ -63,16 +64,18 @@ const OneFilePage: NextPage = ({ data }) => { - {columns?.map(({ key, title, width = 'auto' }: column) => ( - - ))} + {columns?.map( + ({ key, title, title_en, width = 'auto' }: column) => ( + + ) + )} @@ -105,20 +108,20 @@ const OneFilePage: NextPage = ({ data }) => {

- 「OneFile」 - 是一个开源项目,在这里你可以找到有趣运行简单的程序。同时它也是一个编程挑战,你也可以提交一个文件接受挑战。 + + - 点击加入 + {t('click')} - {' '} - OneFile 编程挑战,一个文件而已就写点有趣的代码吧! + + {t('p_text3')}

-
+
@@ -127,17 +130,11 @@ const OneFilePage: NextPage = ({ data }) => { export default OneFilePage; -export const getServerSideProps: GetServerSideProps = async ({ req }) => { - let ip; - if (req.headers['x-forwarded-for']) { - ip = req.headers['x-forwarded-for'] as string; - ip = ip.split(',')[0] as string; - } else if (req.headers['x-real-ip']) { - ip = req.headers['x-real-ip'] as string; - } else { - ip = req.socket.remoteAddress as string; - } - +export const getServerSideProps: GetServerSideProps = async ({ + req, + locale, +}) => { + const ip = getClientIP(req); const data = await getOnefile(ip); if (!data.success) { return { @@ -147,6 +144,10 @@ export const getServerSideProps: GetServerSideProps = async ({ req }) => { return { props: { data: data.data, + ...(await serverSideTranslations(locale as string, [ + 'common', + 'onefile', + ])), }, }; } diff --git a/src/pages/onefile/join.tsx b/src/pages/onefile/join.tsx index dd8bfd3a..9d4a21a2 100644 --- a/src/pages/onefile/join.tsx +++ b/src/pages/onefile/join.tsx @@ -1,4 +1,6 @@ -import { NextPage } from 'next'; +import { GetStaticProps, NextPage } from 'next'; +import { useTranslation } from 'next-i18next'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import ItemBottom from '@/components/home/ItemBottom'; import { MDRender } from '@/components/mdRender/MDRender'; @@ -6,16 +8,16 @@ import Navbar from '@/components/navbar/Navbar'; import Seo from '@/components/Seo'; import ToTop from '@/components/toTop/ToTop'; -import content from '../../../data/onefile_join.md'; - import { HelpPageProps } from '@/types/help'; const OneFileJoinPage: NextPage = ({ content }) => { + const { t } = useTranslation('onefile'); + return ( <> - +
- +
@@ -23,20 +25,33 @@ const OneFileJoinPage: NextPage = ({ content }) => { {content}
- +
-
+
); }; -export default OneFileJoinPage; - -export async function getStaticProps() { +export const getStaticProps: GetStaticProps = async ({ locale }) => { + let content; + if (locale === 'en') { + content = await import('../../../data/onefile_en.md'); + } else { + content = await import('../../../data/onefile.md'); + } + content = content.default; return { - props: { content: content }, + props: { + content, + ...(await serverSideTranslations(locale as string, [ + 'common', + 'onefile', + ])), + }, }; -} +}; + +export default OneFileJoinPage;
- {title} - + {i18n.language == 'en' ? title_en : title} +