diff --git a/packages/ui/src/translation-factory/index.tsx b/packages/ui/src/translation-factory/index.tsx index 92705a49b..0135190fa 100644 --- a/packages/ui/src/translation-factory/index.tsx +++ b/packages/ui/src/translation-factory/index.tsx @@ -25,9 +25,9 @@ export const translationFunctionFactory = dictionary: T, getLanguageCode: () => keyof T[keyof T], fallbackText?: string, - ): ((k: keyof T, r?: Record) => string) => - (k, r = {}) => { - const languageCode = getLanguageCode(); + ): ((k: keyof T, r?: Record, overrideLanguage?: keyof T[keyof T]) => string) => + (k, r = {}, overrideLanguage) => { + const languageCode = overrideLanguage || getLanguageCode(); return replace(dictionary, languageCode, fallbackText, k, r); }; diff --git a/web/package.json b/web/package.json index 58e165295..d96d32612 100644 --- a/web/package.json +++ b/web/package.json @@ -26,6 +26,7 @@ "markdown-to-jsx": "^7.1.0", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-helmet": "^6.1.0", "react-redux": "^7.2.6", "react-router-dom": "^5.2.0", "react-spring": "^8.0.27", @@ -52,6 +53,7 @@ "@types/mini-css-extract-plugin": "^2.0.1", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", + "@types/react-helmet": "^6.1.5", "@types/react-redux": "^7.1.20", "@types/react-router-dom": "^5.1.6", "@types/react-syntax-highlighter": "^13.5.0", diff --git a/web/src/apps/main/components/t/dictionary.ts b/web/src/apps/main/components/t/dictionary.ts index ed290c2af..23f5a3c6e 100644 --- a/web/src/apps/main/components/t/dictionary.ts +++ b/web/src/apps/main/components/t/dictionary.ts @@ -39,7 +39,15 @@ export const dictionary = { ar: "حقوق النشر ©", }, - "faq-title": { en: "Frequently Asked Questions", ar: "اللاسئلة الاكثر طرحا" }, + "faq-title": { + en: "Frequently Asked Questions | DzCode i/o", + ar: "اللاسئلة الاكثر طرحا | DzCode i / o", + }, + "faq-description": { + en: "Frequently asked questions about DzCode i/o", + ar: "الأسئلة المتداولة حول DzCode i / o", + }, + "faq-header-title": { en: "Frequently Asked Questions", ar: "اللاسئلة الاكثر طرحا" }, "faq-need-help": { en: "Still need help? send us an email at ", ar: "هل ما زلت بحاجة إلى المساعدة؟ أرسل إلينا بريدًا إلكترونيًا على ", @@ -188,6 +196,14 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu - ستحصل على مساهمات محتملة من مطورين آخرين ، مما سيجعل برنامجك في النهاية أفضل. `, }, + "landing-title": { + en: "Algeria Codes | DzCode i/o", + ar: "الجزائر تبرمج | DzCode i / o", + }, + "landing-description": { + en: "DzCode i/o is community of developers that tries to solve technical problems in Algeria via open-source software, this website helps you find, contribute and add to the list of open-source projects that solve some technical problems in Algerian.", + ar: "DzCode i / o هو مجتمع من المطورين الذين يحاولون حل المشاكل التقنية في الجزائر عبر برمجيات مفتوحة المصدر ، وهذا الموقع يساعدك في العثور على ، والمساهمة والإضافة إلى قائمة المشاريع مفتوحة المصدر التي تحل بعض المشاكل التقنية في الجزائر.", + }, "landing-heading-title": { en: `Open-Source Algerian Comunity`, ar: `مجموعة جزائرية للبرامج مفتوحة المصدر`, @@ -212,6 +228,14 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Meet the DzCode i/o mobile app and stay up-to-date with the state of Algerian open-source software on iOS and Android.", ar: "تعرف على تطبيق DzCode i / o للجوال وابق على اطلاع دائم بأحدث البرامج مفتوحة المصدر الجزائرية على iOS و Android.", }, + "team-title": { + en: "Meet the team! | DzCode i/o", + ar: "تعرّف على الفريق! | DzCode i / o", + }, + "team-description": { + en: "Meet and connect with all the contributors of all the listed projects on dzcode.io website", + ar: "تعرف على جميع المساهمين في جميع المشاريع المدرجة وتواصل معهم على موقع dzcode.io الإلكتروني", + }, "team-error": { en: "Oops, an error occurred while loading the articles list, please try again...", ar: "عفوًا ، حدث خطأ أثناء تحميل قائمة المقالات ، يرجى المحاولة مرة أخرى ...", @@ -220,7 +244,7 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Try Again", ar: "حاول مرة أخري", }, - "team-title": { + "team-header-title": { en: "Get to know our team 💻", ar: "تعرف على فريقنا 💻", }, @@ -232,6 +256,14 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Repositories", ar: "مستودعات", }, + "projects-title": { + en: "Browse a growing list of Algerian open-source projects | DzCode i/o", + ar: "تصفح قائمة المشاريع الجزائرية مفتوحة المصدر | DzCode i / o", + }, + "projects-description": { + en: "Browse a growing list of Algerian open-source projects and be up-to-date with the state of open-source software in Algeria, you can also add your project to the list!", + ar: "تصفح قائمة متزايدة من المشاريع الجزائرية مفتوحة المصدر وكن على اطلاع دائم بأحدث البرامج مفتوحة المصدر في الجزائر ، كما يمكنك إضافة مشروعك إلى القائمة!", + }, "projects-error": { en: "Oops, an error occurred while loading the projects list, please try again...", ar: "عفوًا ، حدث خطأ أثناء تحميل قائمة المشاريع ، يرجى المحاولة مرة أخرى ...", @@ -240,7 +272,7 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Try Again", ar: "حاول مرة أخري", }, - "projects-title": { + "projects-header-title": { en: "Open Source Projects", ar: "مشاريع مفتوحة المصدر", }, @@ -248,6 +280,14 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Go to code", ar: "إلى الكود", }, + "notfound-title": { + en: "A broken link? | DzCode i/o", + ar: "عنوان url معطل؟ | DzCode i / o", + }, + "notfound-description": { + en: "A broken link?", + ar: "عنوان url معطل؟", + }, "notfound-subtitle": { en: `Finally someone saw the 404 page Nour built 😄`, ar: `أخيرًا شاهد شخص ما صفحة 404 التي أنشأتها نور 😄`, @@ -256,6 +296,14 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "Go Back Home", ar: "ارجع إلى الصفحة الرئيسية", }, + "contribute-title": { + en: "Contribute to algerian open-source projects | DzCode i/o", + ar: "ساهم في المشاريع الجزائرية مفتوحة المصدر | DzCode i / o", + }, + "contribute-description": { + en: "Browse and contribute to Algerian open-source projects", + ar: "تصفح وساهم في مشاريع جزائرية مفتوحة المصدر", + }, "contribute-filter-projects": { en: "Project", ar: "المشروع", @@ -280,4 +328,20 @@ Besides the open tasks on [/Contribute](/Contribute) page, you can also contribu en: "y|mo|d|h|min|Just now", ar: " عام| شهر| يوم| ساعة| دقيقة| الآن", }, + "articles-title": { + en: "Read and discuss articles written by algerian developers | DzCode i/o", + ar: "اقرأ وناقش المقالات التي كتبها المطورون الجزائريون | DzCode i / o", + }, + "articles-description": { + en: "Browse, read or modify a growing list of articles written by Algerian developers, or Add your own article to the list!", + ar: "تصفح أو اقرأ أو عدل قائمة متزايدة من المقالات التي كتبها مطورون جزائريون ، أو أضف مقالك الخاص إلى القائمة!", + }, + "learn-title": { + en: "Learn about software development through open-source | DzCode i/o", + ar: "تعرف على البرمجة من خلال البرامج مفتوحة المصدر | DzCode i / o", + }, + "learn-description": { + en: "Learn and share your knowledge with other Algerian developers!", + ar: "تعلم وشارك معرفتك مع مطورين جزائريين آخرين!", + }, }; diff --git a/web/src/apps/main/entry/app.tsx b/web/src/apps/main/entry/app.tsx index b388ad986..6fc953f68 100644 --- a/web/src/apps/main/entry/app.tsx +++ b/web/src/apps/main/entry/app.tsx @@ -4,10 +4,12 @@ import { allLanguages, LanguageEntity } from "@dzcode.io/models/dist/language"; import { ErrorBoundary } from "@dzcode.io/ui/dist/error-boundary"; import Container from "@material-ui/core/Container"; import { ComponentType, FC, lazy, Suspense, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { Route, RouteProps, Switch, useLocation, useRouteMatch } from "react-router-dom"; import { Footer } from "src/apps/main/components/footer"; import { Navbar } from "src/apps/main/components/navbar"; +import { t } from "src/apps/main/components/t"; import { Theme } from "src/apps/main/components/theme"; import { getEnv } from "src/common/utils"; import { urlLanguageRegEx } from "src/common/utils/language"; @@ -83,6 +85,9 @@ export const App: FC = () => { return ( + + {t("landing-title")} +
"en"); + // SSR --------------------------------| -// Root URL ---- -const pages = [ - { - uri: "/", - title: "DZ Open-Source | DzCode i/o", - description: "Algerian Open-Source Community", - ogImage: - "https://images.unsplash.com/photo-1527285341945-715b98b98ea2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "", - }, -]; -// Other URLs -if (process.env.NODE_ENV !== "development") { - // Static URLs ---- - pages.push( - ...[ - { - uri: "/Contribute", - title: "Contribute to algerian open-source projects | DzCode i/o", - description: "Contribute to algerian open-source projects", - ogImage: - "https://images.unsplash.com/photo-1532618500676-2e0cbf7ba8b8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "contribute, open-source, algeria, dzcode", - }, - { - uri: "/Learn", - title: "Learn about software development through open-source | DzCode i/o", - description: "Learn, edit and share the knowledge between all Algerian developers!", - ogImage: - "https://images.unsplash.com/photo-1519670107408-15dc1b3ecb1c?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "learn, open-source, algeria, dzcode", - }, - { - uri: "/Projects", - title: "Browse a growing list of algerian open-source projects | DzCode i/o", - description: - "Browse a growing list of algerian open-source projects and be up-to-date with the state of dz open-source, or Add your own project to the list!", - ogImage: - "https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "projects, open-source, algeria, dzcode", - }, - { - uri: "/Articles", - title: "Read and discuss articles written by algerian developers | DzCode i/o", - description: - "Browse, read, modify a growing list of articles written by algerian developers, or Add your own article to the list!", - ogImage: - "https://images.unsplash.com/photo-1585241936939-be4099591252?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "articles, open-source, algeria, dzcode", - }, - { - uri: "/FAQ", - title: - "Understand what exactly is DzCode i/o, get answers to the frequently asked questions | DzCode i/o", - description: "Frequently asked questions about DzCode i/o", - ogImage: - "https://images.unsplash.com/photo-1516246843873-9d12356b6fab?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8https://images.unsplash.com/photo-1516246843873-9d12356b6fab?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=750&q=80&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "faq, open-source, algeria, dzcode", - }, - { - uri: "/Team", - title: "Meet the team! | DzCode i/o", - description: - "Meet and connect with all the open-source contributors of all the listed projects in dzcode.io website", - ogImage: - "https://images.unsplash.com/photo-1526663089957-f2aa2776f572?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=750&q=80&auto=format&fit=crop&w=1200&h=627&q=80", - themeColor: "#000", - keywords: "faq, open-source, algeria, dzcode", - }, - ], - ); - // Dynamic URLs ---- +// Static URLs ---- +const pages = ( [ - { file: "articles", slug: "Articles" }, - { file: "documentation", slug: "Learn" }, - { file: "projects", slug: "Projects" }, - ].forEach((collectionInfo) => { - const collection = getCollection>( - join(__dirname, "../../../../../data"), - collectionInfo.file, - "ssr.json", - ); - if (!Array.isArray(collection)) { - throw new Error(`Collection is not an array: ${collection}`); - } - collection.forEach((entry) => { - pages.push({ - uri: `/${collectionInfo.slug}/${entry.slug}`, - title: `${entry.title} | DzCode i/o`, - description: entry.description, - ogImage: entry.image, - themeColor: "#000", - keywords: entry.keywords, - }); + { + uri: "/", + title: "landing-title", + description: "landing-description", + ogImage: + "https://images.unsplash.com/photo-1527285341945-715b98b98ea2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "", + }, + { + uri: "/Contribute", + title: "contribute-title", + description: "contribute-description", + ogImage: + "https://images.unsplash.com/photo-1532618500676-2e0cbf7ba8b8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "contribute, open-source, algeria, dzcode", + }, + { + uri: "/Learn", + title: "learn-title", + description: "learn-description", + ogImage: + "https://images.unsplash.com/photo-1519670107408-15dc1b3ecb1c?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "learn, open-source, algeria, dzcode", + }, + { + uri: "/Projects", + title: "projects-title", + description: "projects-description", + ogImage: + "https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "projects, open-source, algeria, dzcode", + }, + { + uri: "/Articles", + title: "articles-title", + description: "articles-description", + ogImage: + "https://images.unsplash.com/photo-1585241936939-be4099591252?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "articles, open-source, algeria, dzcode", + }, + { + uri: "/FAQ", + title: "faq-title", + description: "faq-description", + ogImage: + "https://images.unsplash.com/photo-1516246843873-9d12356b6fab?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8https://images.unsplash.com/photo-1516246843873-9d12356b6fab?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=750&q=80&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "faq, open-source, algeria, dzcode", + }, + { + uri: "/Team", + title: "team-title", + description: "team-description", + ogImage: + "https://images.unsplash.com/photo-1526663089957-f2aa2776f572?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=750&q=80&auto=format&fit=crop&w=1200&h=627&q=80", + themeColor: "#000", + keywords: "faq, open-source, algeria, dzcode", + }, + ] as const +).reduce( + (pV, { title, description, uri, ...page }) => [ + ...pV, + ...allLanguages.map(({ code }) => ({ + ...page, + title: t(title, undefined, code), + description: t(description, undefined, code), + uri: code === "en" ? uri : `/${code}${uri}`, + })), + ], + [] as any[], +); + +// Dynamic URLs ---- +// @TODO-ZM: to localize this +[ + { file: "articles", slug: "Articles" }, + { file: "documentation", slug: "Learn" }, + { file: "projects", slug: "Projects" }, +].forEach((collectionInfo) => { + const collection = getCollection>( + join(__dirname, "../../../../../data"), + collectionInfo.file, + "ssr.json", + ); + if (!Array.isArray(collection)) { + throw new Error(`Collection is not an array: ${collection}`); + } + collection.forEach((entry) => { + pages.push({ + uri: `/${collectionInfo.slug}/${entry.slug}`, + title: `${entry.title} | DzCode i/o`, + description: entry.description, + ogImage: entry.image, + themeColor: "#000", + keywords: entry.keywords, }); }); -} +}); + // Convert pages into html webpack plugins -pages.forEach((page) => { +(process.env.NODE_ENV === "development" ? [pages[0]] : pages).forEach((page) => { plugins.push( new HtmlWebpackPlugin({ filename: (page.uri !== "/" ? `${page.uri}/index.html` : "/index.html").substring(1), diff --git a/web/src/apps/main/pages/articles/content/index.tsx b/web/src/apps/main/pages/articles/content/index.tsx index a87d71734..607c457fe 100644 --- a/web/src/apps/main/pages/articles/content/index.tsx +++ b/web/src/apps/main/pages/articles/content/index.tsx @@ -14,6 +14,7 @@ import LinkedInIcon from "@material-ui/icons/LinkedIn"; import TwitterIcon from "@material-ui/icons/Twitter"; import Skeleton from "@material-ui/lab/Skeleton"; import { FC, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { Authors } from "src/apps/main/components/authors"; import { Contributors } from "src/apps/main/components/contributors"; @@ -104,6 +105,10 @@ export const Content: FC = () => { /> ) : currentArticle ? ( <> + + {`${currentArticle.title} | DzCode i/o`} + + {/* Image */} {currentArticle.image && ( { return ( + + {t("articles-title")} + + {/* Sidebar */} diff --git a/web/src/apps/main/pages/contribute/index.tsx b/web/src/apps/main/pages/contribute/index.tsx index 9d4935535..a30efb07f 100644 --- a/web/src/apps/main/pages/contribute/index.tsx +++ b/web/src/apps/main/pages/contribute/index.tsx @@ -1,7 +1,9 @@ import { ErrorBoundary } from "@dzcode.io/ui/dist/error-boundary"; import Grid from "@material-ui/core/Grid"; import { FC, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch } from "react-redux"; +import { t } from "src/apps/main/components/t"; import { ContributePageState } from "src/apps/main/redux/reducers/contribute-page"; import { Dispatch } from "../../redux"; @@ -17,6 +19,10 @@ export const ContributePage: FC = () => { return ( + + {t("contribute-title")} + + {/* Filters */} diff --git a/web/src/apps/main/pages/faq/index.tsx b/web/src/apps/main/pages/faq/index.tsx index fccfa02bc..b1c1285ec 100644 --- a/web/src/apps/main/pages/faq/index.tsx +++ b/web/src/apps/main/pages/faq/index.tsx @@ -4,6 +4,7 @@ import { Grid } from "@dzcode.io/ui/dist/grid"; import { ThemeProvider } from "@dzcode.io/ui/dist/theme/theme-provider"; import { Typography } from "@dzcode.io/ui/dist/typography"; import type { FC } from "react"; +import { Helmet } from "react-helmet"; import { useSelector } from "react-redux"; import { Markdown } from "src/apps/main/components/markdown"; import { T, t } from "src/apps/main/components/t"; @@ -15,6 +16,10 @@ const FaqCards = () => { return ( + + {t("faq-title")} + + {faqData.map(({ title, questions }, index) => ( { const PageTitle = () => { return ( - + ); }; diff --git a/web/src/apps/main/pages/landing/index.tsx b/web/src/apps/main/pages/landing/index.tsx index d6ccd7ae0..3e984983e 100644 --- a/web/src/apps/main/pages/landing/index.tsx +++ b/web/src/apps/main/pages/landing/index.tsx @@ -1,5 +1,7 @@ import { ErrorBoundary } from "@dzcode.io/ui/dist/error-boundary"; import { FC } from "react"; +import { Helmet } from "react-helmet"; +import { t } from "src/apps/main/components/t"; import { Header } from "./header"; import { Mobile } from "./mobile"; @@ -7,6 +9,10 @@ import { Mobile } from "./mobile"; export const LandingPage: FC = () => { return ( + + {t("landing-title")} + +
diff --git a/web/src/apps/main/pages/learn/content/index.tsx b/web/src/apps/main/pages/learn/content/index.tsx index 596c6294f..4ba3a2ab5 100644 --- a/web/src/apps/main/pages/learn/content/index.tsx +++ b/web/src/apps/main/pages/learn/content/index.tsx @@ -14,6 +14,7 @@ import LinkedInIcon from "@material-ui/icons/LinkedIn"; import TwitterIcon from "@material-ui/icons/Twitter"; import Skeleton from "@material-ui/lab/Skeleton"; import { FC, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { Authors } from "src/apps/main/components/authors"; import { Contributors } from "src/apps/main/components/contributors"; @@ -103,6 +104,10 @@ export const Content: FC = () => { /> ) : currentDocument ? ( <> + + {`${currentDocument.title} | DzCode i/o`} + + {/* Image */} {currentDocument.image && ( { return ( + + {t("learn-title")} + + {/* Sidebar */} diff --git a/web/src/apps/main/pages/not-found/index.tsx b/web/src/apps/main/pages/not-found/index.tsx index 730092070..d260ac206 100644 --- a/web/src/apps/main/pages/not-found/index.tsx +++ b/web/src/apps/main/pages/not-found/index.tsx @@ -5,6 +5,7 @@ import Typography from "@material-ui/core/Typography"; import ArrowBackIcon from "@material-ui/icons/ArrowBack"; import ArrowForwardIcon from "@material-ui/icons/ArrowForward"; import { FC } from "react"; +import { Helmet } from "react-helmet"; import { useSelector } from "react-redux"; import { LinkV2 } from "src/apps/main/components/link-v2"; import { T, t } from "src/apps/main/components/t"; @@ -35,6 +36,10 @@ const NotFound: FC = () => { return ( + + {t("notfound-title")} + +
DzCode i/o: 404 page not found diff --git a/web/src/apps/main/pages/projects/catalog/index.tsx b/web/src/apps/main/pages/projects/catalog/index.tsx index 530db06ee..66d1e68b1 100644 --- a/web/src/apps/main/pages/projects/catalog/index.tsx +++ b/web/src/apps/main/pages/projects/catalog/index.tsx @@ -28,7 +28,7 @@ export const Catalog: FC = ({ projectsList }) => { return ( <> - + {projectsList diff --git a/web/src/apps/main/pages/projects/index.tsx b/web/src/apps/main/pages/projects/index.tsx index 20144cde3..2421f602f 100644 --- a/web/src/apps/main/pages/projects/index.tsx +++ b/web/src/apps/main/pages/projects/index.tsx @@ -1,6 +1,7 @@ import { ErrorBoundary } from "@dzcode.io/ui/dist/error-boundary"; import { TryAgain } from "@dzcode.io/ui/dist/try-again"; import { FC, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { t } from "src/apps/main/components/t"; import { Dispatch, StateInterface } from "src/apps/main/redux"; @@ -21,6 +22,10 @@ export const ProjectsPage: FC = () => { return ( + + {t("projects-title")} + +
{projectsList == "ERROR" ? ( = ({ teamList }) => { - + {teamList diff --git a/web/src/apps/main/pages/team/index.tsx b/web/src/apps/main/pages/team/index.tsx index 6af49de3b..12656830c 100644 --- a/web/src/apps/main/pages/team/index.tsx +++ b/web/src/apps/main/pages/team/index.tsx @@ -2,6 +2,7 @@ import { ErrorBoundary } from "@dzcode.io/ui/dist/error-boundary"; import { ThemeProvider } from "@dzcode.io/ui/dist/theme/theme-provider"; import { TryAgain } from "@dzcode.io/ui/dist/try-again"; import { FC, useEffect } from "react"; +import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { t } from "src/apps/main/components/t"; import { Dispatch, StateInterface } from "src/apps/main/redux"; @@ -23,6 +24,10 @@ export const TeamPage: FC = () => { return ( + + {t("team-title")} + + { switch (action.type) { case "UPDATE_SETTINGS": - if (action.payload.darkMode) { + if ("darkMode" in action.payload) { localStorage.setItem("darkMode", action.payload.darkMode ? "on" : "off"); + console.log(action.payload.darkMode ? "on" : "off"); } - if (action.payload.language) { + if ("language" in action.payload) { localStorage.setItem("languageCode", action.payload.language.code); document.body.setAttribute("dir", action.payload.language.code === "ar" ? "rtl" : "ltr"); const match = matchPath<{ lang?: LanguageEntity["code"] }>(history.location.pathname, { @@ -42,12 +43,11 @@ export const settings = ( action.payload.language.code === "en" ? "" : `/${action.payload.language.code}`; if (match?.params.lang || langPrefix) { - history.push({ - ...history.location, - pathname: match?.params.lang + const pathname = + (match?.params.lang ? history.location.pathname.replace(`/${match.params.lang}`, langPrefix) - : `${langPrefix}${history.location.pathname}`, - }); + : `${langPrefix}${history.location.pathname}`) || "/"; + history.push({ ...history.location, pathname }); } } return { ...state, ...action.payload }; diff --git a/web/src/build/sitemap.ts b/web/src/build/sitemap.ts index e33edeaac..93101f788 100644 --- a/web/src/build/sitemap.ts +++ b/web/src/build/sitemap.ts @@ -1,20 +1,26 @@ +import { getCollection } from "@dzcode.io/data/dist/get/collection"; +import { allLanguages } from "@dzcode.io/models/dist/language"; import { createWriteStream } from "fs"; +import { join } from "path"; import { SitemapStream } from "sitemap"; + const distFolder = "./bundle"; -import { getCollection } from "@dzcode.io/data/dist/get/collection"; -import { join } from "path"; // Static URLs console.log("Getting Static URLs ..."); -const urls = ["/", "/Contribute", "/Learn", "/Projects", "/Articles", "/FAQ"]; +const urls = ["/", "/Contribute", "/Learn", "/Projects", "/Articles", "/FAQ", "/Team"].reduce( + (pV, uri) => [...pV, ...allLanguages.map(({ code }) => (code === "en" ? uri : `/${code}${uri}`))], + [] as string[], +); console.log("✅", urls.length, "URLs Found"); // Dynamic URLs +// @TODO-ZM: to localize this console.log("Getting Dynamic URLs ..."); [ { file: "articles", slug: "Articles" }, { file: "documentation", slug: "Learn" }, - { file: "projects", slug: "Projects" }, + // { file: "projects", slug: "Projects" }, // @TODO-ZM: to put back when we have proper project details page ].forEach((collectionInfo) => { const collection = getCollection>( join(__dirname, "../../../data"), @@ -41,9 +47,17 @@ writeStream.on("error", (err) => { writeStream.on("ready", () => { sitemap.pipe(writeStream); urls.forEach((url) => { - sitemap.write(url, undefined, (err) => { - if (err) console.log("Sitemap generation error", err); - }); + const lang = allLanguages.find(({ code }) => `/${code}/` === url.substring(0, 4))?.code || "en"; + sitemap.write( + { + url, + links: [{ lang, url }], + }, + undefined, + (err) => { + if (err) console.log("Sitemap generation error", err); + }, + ); }); sitemap.end(); diff --git a/yarn.lock b/yarn.lock index 751a0057e..23ce7ec04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4318,6 +4318,13 @@ dependencies: "@types/react" "*" +"@types/react-helmet@^6.1.5": + version "6.1.5" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083" + integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA== + dependencies: + "@types/react" "*" + "@types/react-is@^16.7.1 || ^17.0.0": version "17.0.3" resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" @@ -4381,9 +4388,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^17.0.0", "@types/react@^17.0.1", "@types/react@~17.0.21": - version "17.0.47" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.47.tgz#4ee71aaf4c5a9e290e03aa4d0d313c5d666b3b78" - integrity sha512-mk0BL8zBinf2ozNr3qPnlu1oyVTYq+4V7WA76RgxUAtf0Em/Wbid38KN6n4abEkvO4xMTBWmnP1FtQzgkEiJoA== + version "17.0.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.48.tgz#a4532a8b91d7b27b8768b6fc0c3bccb760d15a6c" + integrity sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -6621,9 +6628,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001272, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001359: - version "1.0.30001359" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001359.tgz#a1c1cbe1c2da9e689638813618b4219acbd4925e" - integrity sha512-Xln/BAsPzEuiVLgJ2/45IaqD9jShtk3Y33anKb4+yLwQzws3+v6odKfpgES/cDEaZMLzSChpIGdbOYtH9MyuHw== + version "1.0.30001374" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" + integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== capture-exit@^2.0.0: version "2.0.0" @@ -18372,6 +18379,21 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== +react-fast-compare@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + "react-is@^16.12.0 || ^17.0.0", "react-is@^16.8.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -18586,6 +18608,11 @@ react-shallow-renderer@^16.13.1: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0" +react-side-effect@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== + react-spring@^8.0.27: version "8.0.27" resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-8.0.27.tgz#97d4dee677f41e0b2adcb696f3839680a3aa356a"