diff --git a/packages/ui/src/dialog/__snapshots__/index.test.tsx.snap b/packages/ui/src/contributor-card/dialog/__snapshots__/index.test.tsx.snap
similarity index 100%
rename from packages/ui/src/dialog/__snapshots__/index.test.tsx.snap
rename to packages/ui/src/contributor-card/dialog/__snapshots__/index.test.tsx.snap
diff --git a/packages/ui/src/dialog/index.test.tsx b/packages/ui/src/contributor-card/dialog/index.test.tsx
similarity index 71%
rename from packages/ui/src/dialog/index.test.tsx
rename to packages/ui/src/contributor-card/dialog/index.test.tsx
index 908898aa2..79f52c15b 100644
--- a/packages/ui/src/dialog/index.test.tsx
+++ b/packages/ui/src/contributor-card/dialog/index.test.tsx
@@ -1,16 +1,16 @@
import { render, screen } from "@testing-library/react";
-import { createProjects } from "../__mocks__/create-projects";
-import { createRepositories } from "../__mocks__/create-repositories";
-import { ContributionsDialog } from ".";
+import { createProjects } from "../../__mocks__/create-projects";
+import { createRepositories } from "../../__mocks__/create-repositories";
+import { ContributionsDialog, ContributionsDialogProps } from ".";
it("should render a closed dialog on first mount", async () => {
const onClose = jest.fn();
- const props = {
+ const props: ContributionsDialogProps = {
onClose: onClose,
open: false,
- projects: createProjects(2),
repositories: createRepositories(2),
+ repositoriesText: "Repositories",
};
const { container } = render( );
@@ -19,11 +19,11 @@ it("should render a closed dialog on first mount", async () => {
it("should handle users with no projects", async () => {
const onClose = jest.fn();
- const props = {
+ const props: ContributionsDialogProps = {
onClose: onClose,
open: true,
- projects: [],
repositories: createRepositories(2),
+ repositoriesText: "Repositories",
};
render( );
@@ -33,11 +33,11 @@ it("should handle users with no projects", async () => {
it("should handle users with no repositories", async () => {
const onClose = jest.fn();
- const props = {
+ const props: ContributionsDialogProps = {
onClose: onClose,
open: true,
- projects: createProjects(2),
repositories: [],
+ repositoriesText: "Repositories",
};
render( );
@@ -47,11 +47,11 @@ it("should handle users with no repositories", async () => {
it("should render a dialog", async () => {
const onClose = jest.fn();
- const props = {
+ const props: ContributionsDialogProps = {
onClose: onClose,
open: true,
- projects: createProjects(2),
repositories: createRepositories(2),
+ repositoriesText: "Repositories",
};
render( );
diff --git a/packages/ui/src/dialog/index.tsx b/packages/ui/src/contributor-card/dialog/index.tsx
similarity index 67%
rename from packages/ui/src/dialog/index.tsx
rename to packages/ui/src/contributor-card/dialog/index.tsx
index 135e7e211..54b683021 100644
--- a/packages/ui/src/dialog/index.tsx
+++ b/packages/ui/src/contributor-card/dialog/index.tsx
@@ -8,6 +8,7 @@ import ListItem from "@mui/material/ListItem";
import ListItemAvatar from "@mui/material/ListItemAvatar";
import ListItemText from "@mui/material/ListItemText";
import Typography from "@mui/material/Typography";
+import { FC } from "react";
interface ContributionItemProps {
description: string;
@@ -36,7 +37,7 @@ interface ContributionListProps {
const ContributionList = ({ title, items }: ContributionListProps) => {
return (
<>
-
+
{title}
@@ -48,66 +49,39 @@ const ContributionList = ({ title, items }: ContributionListProps) => {
};
export interface ContributionsDialogProps {
+ repositoriesText: string;
open: boolean;
- projects?: Array<{
- slug: string;
- image?: string;
- title: string;
- description?: string;
- content?: string;
- authors?: string[];
- contributors?: string[];
- views?: number;
- githubURI?: string;
- }>;
repositories?: { provider: string; owner: string; repository: string }[];
onClose: () => void;
}
-export function ContributionsDialog(props: ContributionsDialogProps) {
- const { onClose, open, projects, repositories } = props;
-
- const handleClose = () => {
- onClose();
- };
-
+export const ContributionsDialog: FC = ({
+ onClose,
+ open,
+ repositories,
+ repositoriesText,
+}) => {
return (
-
-
- Contributions
-
-
{repositories && repositories.length > 0 && (
({
label: repository.repository,
}))}
/>
)}
-
- {projects && projects.length > 0 && (
- ({ label: project.title }))}
- />
- )}
-
);
-}
+};
diff --git a/packages/ui/src/contributor-card/index.test.tsx b/packages/ui/src/contributor-card/index.test.tsx
index 188e242d7..094fc16c2 100644
--- a/packages/ui/src/contributor-card/index.test.tsx
+++ b/packages/ui/src/contributor-card/index.test.tsx
@@ -13,7 +13,13 @@ it("should render a contributor skeleton ", async () => {
it("should render a contributor card", () => {
const contributor = createContributors(1)[0];
- render( );
+ render(
+ ,
+ );
const username = screen.getByText(contributor.username);
expect(username).toBeInTheDocument();
@@ -21,7 +27,13 @@ it("should render a contributor card", () => {
it("should show contributions dialog after Contributions button is clicked", async () => {
const contributor = createContributors(1)[0];
- render( );
+ render(
+ ,
+ );
const button = await screen.findByRole("button", { name: "Contributions" });
userEvent.click(button);
diff --git a/packages/ui/src/contributor-card/index.tsx b/packages/ui/src/contributor-card/index.tsx
index 05df6b2a5..ad14dc617 100644
--- a/packages/ui/src/contributor-card/index.tsx
+++ b/packages/ui/src/contributor-card/index.tsx
@@ -9,7 +9,7 @@ import Skeleton from "@mui/material/Skeleton";
import Typography from "@mui/material/Typography";
import { FC, useState } from "react";
-import { ContributionsDialog } from "../dialog";
+import { ContributionsDialog } from "./dialog";
const styles = {
root: {
@@ -41,11 +41,17 @@ export const ContributorSkeleton = () => {
);
};
-interface ContributorCardProps {
+export interface ContributorCardProps {
+ ctaText: string;
+ repositoriesText: string;
contributor: ContributorEntity;
}
-export const ContributorCard: FC = ({ contributor }) => {
+export const ContributorCard: FC = ({
+ contributor,
+ ctaText,
+ repositoriesText,
+}) => {
const [open, setOpen] = useState(false);
const { avatarUrl, username, repositories } = contributor;
return (
@@ -64,10 +70,15 @@ export const ContributorCard: FC = ({ contributor }) => {
fullWidth
variant="contained"
>
- Contributions
+ {ctaText}
- setOpen(false)} />
+ setOpen(false)}
+ />
);
};
diff --git a/packages/ui/src/faq-card/index.tsx b/packages/ui/src/faq-card/index.tsx
index aae9f48c5..d322bfe3b 100644
--- a/packages/ui/src/faq-card/index.tsx
+++ b/packages/ui/src/faq-card/index.tsx
@@ -24,7 +24,7 @@ export const FaqCard = ({ title, questions }: FaqCardProps) => {
{questions.map(({ question, answer }, index) => (
-
+
}>
{question}
diff --git a/packages/ui/src/translation-factory/index.tsx b/packages/ui/src/translation-factory/index.tsx
new file mode 100644
index 000000000..92705a49b
--- /dev/null
+++ b/packages/ui/src/translation-factory/index.tsx
@@ -0,0 +1,47 @@
+import type { VFC } from "react";
+
+type BaseDictionary = Record>;
+
+export const translationFactory =
+ (
+ dictionary: T,
+ getLanguageCode: () => keyof T[keyof T],
+ fallbackText = "MISSING_TRANSLATION",
+ ): VFC<
+ Partial> & {
+ k?: keyof T;
+ r?: Record;
+ }
+ > =>
+ // eslint-disable-next-line react/display-name
+ ({ k, r = {}, ...props }) => {
+ const languageCode = getLanguageCode();
+ const key = (k as keyof T) || (Object.keys(props)[0] as keyof T);
+ return <>{replace(dictionary, languageCode, fallbackText, key, r)}>;
+ };
+
+export const translationFunctionFactory =
+ >>(
+ dictionary: T,
+ getLanguageCode: () => keyof T[keyof T],
+ fallbackText?: string,
+ ): ((k: keyof T, r?: Record) => string) =>
+ (k, r = {}) => {
+ const languageCode = getLanguageCode();
+ return replace(dictionary, languageCode, fallbackText, k, r);
+ };
+
+const replace = (
+ dictionary: T,
+ languageCode: keyof T[keyof T],
+ fallbackText = "MISSING_TRANSLATION",
+ k: keyof T,
+ r: Record = {},
+) => {
+ const key = k;
+ let value = dictionary[key]?.[languageCode] || fallbackText;
+ Object.keys(r).forEach((rKey: keyof typeof r) => {
+ value = value.replace(RegExp(`${rKey}`), r[rKey]);
+ });
+ return value;
+};
diff --git a/web/package.json b/web/package.json
index 35007ebd6..58e165295 100644
--- a/web/package.json
+++ b/web/package.json
@@ -26,8 +26,6 @@
"markdown-to-jsx": "^7.1.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
- "react-intl": "^5.20.6",
- "react-intl-translations-manager": "^5.0.3",
"react-redux": "^7.2.6",
"react-router-dom": "^5.2.0",
"react-spring": "^8.0.27",
diff --git a/web/src/apps/main/components/card/index.tsx b/web/src/apps/main/components/card/index.tsx
index 4682f1019..0917e69cc 100644
--- a/web/src/apps/main/components/card/index.tsx
+++ b/web/src/apps/main/components/card/index.tsx
@@ -47,7 +47,7 @@ export const Card: FC = ({ info }) => {
{info ? (
<>
-
+
{info.title}
diff --git a/web/src/apps/main/components/footer/__snapshots__/footer.spec.tsx.snap b/web/src/apps/main/components/footer/__snapshots__/footer.spec.tsx.snap
index e2cba5cc8..7103d6b64 100644
--- a/web/src/apps/main/components/footer/__snapshots__/footer.spec.tsx.snap
+++ b/web/src/apps/main/components/footer/__snapshots__/footer.spec.tsx.snap
@@ -183,13 +183,14 @@ exports[`components/footer/footer.spec.tsx should render properly 1`] = `
- Contact Information
+ FAQ
+213 06-76-26-11-57
@@ -206,10 +207,12 @@ exports[`components/footer/footer.spec.tsx should render properly 1`] = `
- Copyright ©
- 2020
+ Copyright ©
+
+ 2022
@dzCode_io
diff --git a/web/src/apps/main/components/footer/footer.spec.tsx b/web/src/apps/main/components/footer/footer.spec.tsx
index 219c84b3a..09019fedc 100644
--- a/web/src/apps/main/components/footer/footer.spec.tsx
+++ b/web/src/apps/main/components/footer/footer.spec.tsx
@@ -1,5 +1,4 @@
import { render } from "@testing-library/react";
-import { IntlProvider } from "react-intl";
import { Provider } from "react-redux";
import { BrowserRouter as Router } from "react-router-dom";
import { createMainStore } from "src/apps/main/redux";
@@ -13,9 +12,7 @@ describe("components/footer/footer.spec.tsx", () => {
const { container } = render(
-
-
-
+
,
);
diff --git a/web/src/apps/main/components/footer/index.tsx b/web/src/apps/main/components/footer/index.tsx
index 95efcd4e1..e6123689b 100644
--- a/web/src/apps/main/components/footer/index.tsx
+++ b/web/src/apps/main/components/footer/index.tsx
@@ -1,11 +1,10 @@
import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid";
-import makeStyles from "@material-ui/core/styles/makeStyles";
+import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import { FC } from "react";
-import { FormattedMessage } from "react-intl";
-import { useIntl } from "react-intl";
import { useSelector } from "react-redux";
+import { T } from "src/apps/main/components/t";
import { StateInterface } from "src/apps/main/redux";
import { FooterComponentState } from "src/apps/main/redux/reducers/footer-component";
import { LinkV2 } from "src/components/link-v2";
@@ -34,7 +33,7 @@ const useStyles = makeStyles((theme) => ({
export const Footer: FC = () => {
const classes = useStyles();
- const intl = useIntl();
+
const { sections } = useSelector(
(state: StateInterface) => state.footerComponent,
);
@@ -48,31 +47,13 @@ export const Footer: FC = () => {
sections.map((category, i) => (
- {intl.formatMessage({
- id: `footer.${category.title.toLowerCase().replace(" ", ".")}`,
- defaultMessage: category.title,
- })}
+
{category.links.map((link, i) => {
return (
-
+
- {link.id
- ? intl.formatMessage({
- id: link.id.replace("-", ".") || link.text.replace(" ", "."),
- defaultMessage: link.text,
- })
- : link.text}
+
);
@@ -82,17 +63,27 @@ export const Footer: FC = () => {
-
+
- +213 06-76-26-11-57
+
+ {"+213 06-76-26-11-57"}
+
- contact@dzcode.io
+ {"contact@dzcode.io"}
- Copyright © {new Date(Date.now()).getFullYear() + " "}
- @dzCode_io
+ {new Date().getFullYear() + " "}
+
+ {"@dzCode_io"}
+
diff --git a/web/src/apps/main/components/markdown/index.tsx b/web/src/apps/main/components/markdown/index.tsx
index acc175929..98067e0a4 100644
--- a/web/src/apps/main/components/markdown/index.tsx
+++ b/web/src/apps/main/components/markdown/index.tsx
@@ -9,7 +9,7 @@ import tomorrow from "react-syntax-highlighter/dist/cjs/styles/prism/tomorrow";
import { StateInterface } from "src/apps/main/redux";
import { LinkV2 } from "src/components/link-v2";
-export const Markdown: FC = (markdownProps) => {
+export const Markdown: FC = ({ children, t, ...markdownProps }) => {
const theme = useTheme();
const darkMode = useSelector((state) => state.settings.darkMode);
@@ -69,12 +69,13 @@ export const Markdown: FC = (markdownProps) => {
},
},
}}
- />
+ children={t || (children as string)} // eslint-disable-line react/no-children-prop
+ >
);
};
interface ReactMarkdown {
[key: string]: unknown;
- children: string;
+ t?: string;
options?: MarkdownToJSX.Options;
}
diff --git a/web/src/apps/main/components/navbar/index.tsx b/web/src/apps/main/components/navbar/index.tsx
index e99b67ed7..fa79fc6b2 100644
--- a/web/src/apps/main/components/navbar/index.tsx
+++ b/web/src/apps/main/components/navbar/index.tsx
@@ -8,17 +8,16 @@ import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import { useScrollPosition } from "@n8tb1t/use-scroll-position";
import { FC, Fragment, useState } from "react";
-import { FormattedMessage } from "react-intl";
-import { useIntl } from "react-intl";
import { useDispatch, useSelector } from "react-redux";
import { animated, useSpring } from "react-spring";
+import { T } from "src/apps/main/components/t";
import { Dispatch, StateInterface } from "src/apps/main/redux";
import { SettingsState } from "src/apps/main/redux/reducers/settings";
import logo from "src/assets/svg/logo-wide.svg";
import { LinkV2 } from "src/components/link-v2";
import { IOSSwitch } from "./ios-switch";
-// import { LanguageSwitch } from "./lang-switch";
+import { LanguageSwitch } from "./lang-switch";
const useStyles = makeStyles((theme) =>
createStyles({
@@ -67,7 +66,7 @@ const useStyles = makeStyles((theme) =>
borderBottom: `1px solid ${theme.palette.background.paper}`,
},
toolbarTitle: {
- marginRight: "auto",
+ flex: 1,
},
toolbarVersion: {
margin: "auto 1rem",
@@ -104,7 +103,7 @@ const useStyles = makeStyles((theme) =>
logo: {
display: "flex",
alignItems: "center",
- justifyContent: "center",
+ justifyContent: "start",
height: "100%",
},
logoImg: {
@@ -118,13 +117,13 @@ const useStyles = makeStyles((theme) =>
);
export const Navbar: FC = () => {
- const { settings, navbarComponent } = useSelector(
- (state) => state,
- );
+ const {
+ settings,
+ navbarComponent: { sections },
+ } = useSelector((state) => state);
const dispatch = useDispatch>();
const classes = useStyles();
- const intl = useIntl();
const [visible, setVisible] = useState(true);
useScrollPosition(({ prevPos, currPos }) => {
const isVisible = currPos.y <= -120 ? currPos.y > prevPos.y : true;
@@ -160,7 +159,7 @@ export const Navbar: FC = () => {
- {/*
*/}
+
{
- {navbarComponent.sections
- ? navbarComponent.sections.map((section, index) => (
+ {sections
+ ? sections.map((section, index) => (
{index > 0 && (
)}
-
-
+
+
))
diff --git a/web/src/apps/main/components/t/dictionary.ts b/web/src/apps/main/components/t/dictionary.ts
new file mode 100644
index 000000000..ed290c2af
--- /dev/null
+++ b/web/src/apps/main/components/t/dictionary.ts
@@ -0,0 +1,283 @@
+export type DictionaryKeys = keyof typeof dictionary &
+ (`${G}-${string}` | `${G}`);
+
+export const dictionary = {
+ "navbar-section-contribute": { en: "Contribute", ar: "اساهم" },
+ "navbar-section-connect": { en: "Connect", ar: "اتواصل" },
+ "navbar-section-learn": { en: "Learn", ar: "اتعلم" },
+ "navbar-section-projects": { en: "Projects", ar: "مشاريع" },
+ "navbar-section-articles": { en: "Articles", ar: "مقالات" },
+ "navbar-section-faq": { en: "FAQ", ar: "اسئلة / اجوبة" },
+
+ "footer-category-title-helpful-links": {
+ en: "Helpful Links",
+ ar: "روابط مفيدة",
+ },
+ "footer-category-link-text-home": { en: "Home", ar: "الصفحة الرئيسية" },
+ "footer-category-link-text-learn": { en: "Learn", ar: "اتعلم" },
+ "footer-category-link-text-projects": { en: "Projects", ar: "مشاريع" },
+ "footer-category-link-text-articles": { en: "Articles", ar: "مقالات" },
+ "footer-category-link-text-faq": { en: "FAQ", ar: "اسئلة / اجوبة" },
+ "footer-category-title-mobile": { en: "Mobile", ar: "تطبيق جوال" },
+ "footer-category-link-text-android": { en: "Android", ar: "أندرويد" },
+ "footer-category-link-text-ios": { en: "iOS", ar: "ايفون" },
+ "footer-category-link-text-expo": { en: "Expo", ar: "اكسبو" },
+ "footer-category-title-social-media": {
+ en: "Social Media",
+ ar: "وسائل التواصل الاجتماعي",
+ },
+ "footer-category-link-text-github": { en: "Github", ar: "جيتهاب" },
+ "footer-category-link-text-slack": { en: "Slack", ar: "سلاك" },
+ "footer-category-link-text-facebook": { en: "Facebook", ar: "فيسبوك" },
+ "footer-category-link-text-instagram": { en: "Instagram", ar: "انستغرام" },
+ "footer-category-link-text-youTube": { en: "YouTube", ar: "يوتيوب" },
+ "footer-category-link-text-twitter": { en: "Twitter", ar: "تويتر" },
+ "footer-category-link-text-linkedIn": { en: "LinkedIn", ar: "لينكد إن" },
+ "footer-contact-information": { en: "FAQ", ar: "اسئلة / اجوبة" },
+ "footer-category-link-text-copyright": {
+ en: "Copyright ©",
+ ar: "حقوق النشر ©",
+ },
+
+ "faq-title": { en: "Frequently Asked Questions", ar: "اللاسئلة الاكثر طرحا" },
+ "faq-need-help": {
+ en: "Still need help? send us an email at ",
+ ar: "هل ما زلت بحاجة إلى المساعدة؟ أرسل إلينا بريدًا إلكترونيًا على ",
+ },
+ "faq-topic-1": {
+ en: "General",
+ ar: "اسئلة عامة",
+ },
+ "faq-topic-1-question-1": {
+ en: "What exactly is DzCode i/o ?",
+ ar: "ما هو DzCode i/o بالضبط؟",
+ },
+ "faq-topic-1-answer-1": {
+ en: `An open-source online community of Algerian developers, collaborating on solving Algerian technical problems.`,
+ ar: `مجتمع مفتوح المصدر على الإنترنت للمطورين الجزائريين ، يتعاونون في حل المشاكل التقنية الجزائرية.`,
+ },
+ "faq-topic-1-question-2": {
+ en: "What are the goals of DzCode i/o ?",
+ ar: "ما هي أهداف DzCode i/o؟",
+ },
+ "faq-topic-1-answer-2": {
+ en: `
+- Share our experiences (in form of [articles](/Articles)), there are many Algerian developers that accumulated lots of experiences, here comes dzcode.io to give them the chance to share these experiences with the right people.
+- Guide new developers to build their careers (with some educational resources and [articles](/Learn)), and to understand the software market, instead of wasting their time on other resources they won't be needing.
+- Fix some common Algerian software problems, in form of ready-to-use open-source [softwares](/Projects) (packages, libraries ...etc).
+`,
+ ar: `
+- لمشاركة خبراتنا (في شكل [مقالات](/Articles)) ، هناك العديد من المطورين الجزائريين الذين جمعوا الكثير من الخبرات ، وهنا يأتي dzcode.io لمنحهم الفرصة لمشاركة هذه الخبرات مع الأشخاص المناسبين.
+- توجيه المطورين الجدد لبناء حياتهم المهنية (ببعض الموارد و[المقالات التعليمية](/Learn)) ، وفهم سوق البرمجيات ، بدلاً من إضاعة وقتهم في موارد أخرى لن يحتاجوا إليها.
+- إصلاح بعض مشاكل البرمجيات الجزائرية الشائعة ، على شكل [برامج](/Projects) مفتوحة المصدر جاهزة للاستخدام (حزم ، مكتبات ... إلخ).
+`,
+ },
+ "faq-topic-1-question-3": {
+ en: "What would I benefit from DzCode i/o?",
+ ar: "ما الذي سأستفيد منه من DzCode i/o؟",
+ },
+ "faq-topic-1-answer-3": {
+ en: `
+- You will meet other Algerian developers, experienced and noobs, and there you can expand your professional network!
+- You'll get to experience a professional environment, which is completely different than the one you used to in school, uni, or on YouTube 😄.
+- Your contribution will be shown on your Github profile, and that is a big plus in your CV!
+`,
+ ar: `
+- ستلتقي بمطورين جزائريين آخرين ، من ذوي الخبرة والمبتدئين ، وهناك يمكنك توسيع شبكتك المهنية!
+- ستتمتع بتجربة بيئة احترافية مختلفة تمامًا عن تلك التي اعتدت عليها في المدرسة أو الجامعة أو على YouTube 😄.
+- ستظهر مساهمتك في ملفك الشخصي على Github ، وهذه إضافة كبيرة في سيرتك الذاتية!
+`,
+ },
+ "faq-topic-2": {
+ en: "Participation",
+ ar: "المشاركة",
+ },
+ "faq-topic-2-question-1": {
+ en: "How do I join DzCode i/o ?",
+ ar: "كيف أنضم إلى DzCode i / o؟",
+ },
+ "faq-topic-2-answer-1": {
+ en: `We're most active in slack, we recommend you [join us there](https://join.slack.com/t/dzcode/shared_invite/zt-ek9kscb7-m8z_~cBjX79l~uchuABPFQ).`,
+ ar: `نحن أكثر نشاطًا في Slack ، نوصيك [بالانضمام إلينا هناك](https://join.slack.com/t/dzcode/shared_invite/zt-ek9kscb7-m8z_~cBjX79l~uchuABPFQ)`,
+ },
+ "faq-topic-2-question-2": {
+ en: "I want to code, where should I start?",
+ ar: "أرغب في البرمجة ، من أين أبدأ؟",
+ },
+ "faq-topic-2-answer-2": {
+ en: `Go to [/Contribute](/Contribute) page, you will see a list of projects with available tasks for you to pick from whatever you like and start programming 🔥.`,
+ ar: `انتقل إلى صفحة [المساهمة](/Contribute) ، سترى قائمة بالمشاريع بالمهام المتاحة لتختار منها ما تريد وتبدأ البرمجة 🔥.`,
+ },
+ "faq-topic-2-question-3": {
+ en: "I like the idea, what are the different ways I can contribute to DzCode i/o ?",
+ ar: "تعجبني الفكرة ، ما هي الطرق المختلفة التي يمكنني من خلالها المساهمة في DzCode i/o؟",
+ },
+ "faq-topic-2-answer-3": {
+ en: `
+Besides the open tasks on [/Contribute](/Contribute) page, you can also contribute to DzCode i/o by:
+
+- Adding your open-source projects to the dzcode.io website, you can do that [here](/Learn/About_dzcode_io/Add_Your_Project_To_dzcode_io).
+- Writing about your programming experience in form of articles, to do that, simply follow [these simple steps](/Learn/About_dzcode_io/Add_Your_Article_To_dzcode_io).
+`,
+ ar: `
+إلى جانب المهام المفتوحة في صفحة المساهمة ، يمكنك أيضًا المساهمة في DzCode i / o عن طريق:
+
+- إضافة مشاريعك مفتوحة المصدر إلى موقع dzcode.io ، يمكنك القيام بذلك [هنا](/Learn/About_dzcode_io/Add_Your_Project_To_dzcode_io).
+- الكتابة عن التجربة البرمجة الخاصة بك في شكل مقالات ، للقيام بذلك ، ما عليك سوى اتباع [هذه الخطوات البسيطة](/Learn/About_dzcode_io/Add_Your_Article_To_dzcode_io).
+`,
+ },
+ "faq-topic-3": {
+ en: "Articles",
+ ar: "مقالات",
+ },
+ "faq-topic-3-question-1": {
+ en: "How to write an article in dzcode.io",
+ ar: "كيفية كتابة مقال في dzcode.io",
+ },
+ "faq-topic-3-answer-1": {
+ en: `Follow [these steps](/Learn/About_dzcode_io/Add_Your_Article_To_dzcode_io).`,
+ ar: `اتبع هذه [الخطوات](/Learn/About_dzcode_io/Add_Your_Article_To_dzcode_io).`,
+ },
+ "faq-topic-3-question-2": {
+ en: "The articles should they be in English?",
+ ar: "هل يجب أن تكون المقالات باللغة الإنجليزية؟",
+ },
+ "faq-topic-3-answer-2": {
+ en: `You can write in both English and Arabic`,
+ ar: `يمكنك الكتابة باللغتين الإنجليزية والعربية`,
+ },
+ "faq-topic-3-question-3": {
+ en: "Can I write about anything?",
+ ar: "هل يمكنني الكتابة عن أي شيء؟",
+ },
+ "faq-topic-3-answer-3": {
+ en: `As long as it's IT-related yes you can write about anything.`,
+ ar: `طالما أن الأمر يتعلق بتكنولوجيا المعلومات نعم يمكنك الكتابة عن أي شيء.`,
+ },
+ "faq-topic-4": {
+ en: "Projects",
+ ar: "المشاريع",
+ },
+ "faq-topic-4-question-1": {
+ en: "How to add my open-source project to dzcode.io",
+ ar: "كيفية إضافة مشروعي مفتوح المصدر إلى dzcode.io",
+ },
+ "faq-topic-4-answer-1": {
+ en: `Follow [these steps](/Learn/About_dzcode_io/Add_Your_Project_To_dzcode_io).`,
+ ar: `اتبع هذه [الخطوات](/Learn/About_dzcode_io/Add_Your_Project_To_dzcode_io).`,
+ },
+ "faq-topic-4-question-2": {
+ en: "What makes my project eligible to be added to dzcode.io",
+ ar: "ما الذي يجعل مشروعي مؤهلاً للإضافة إلى dzcode.io",
+ },
+ "faq-topic-4-answer-2": {
+ en: `It has to solve an Algerian problem, or, be written by an Algerian Developer.`,
+ ar: `يجب أن تحل مشكلة جزائرية ، أو أن يكتبها مطور جزائري.`,
+ },
+ "faq-topic-4-question-3": {
+ en: "Why should I add my project to dzcode.io?",
+ ar: "لماذا يجب علي إضافة مشروعي إلى dzcode.io؟",
+ },
+ "faq-topic-4-answer-3": {
+ en: `
+- Your project will potentially get noticed by more Algerian developers.
+- You will get potential contributions from other developers, which will eventually make your piece of software better.
+`,
+ ar: `
+- من المحتمل أن يحظى مشروعك باهتمام المزيد من المطورين الجزائريين.
+- ستحصل على مساهمات محتملة من مطورين آخرين ، مما سيجعل برنامجك في النهاية أفضل.
+`,
+ },
+ "landing-heading-title": {
+ en: `Open-Source Algerian Comunity`,
+ ar: `مجموعة جزائرية للبرامج مفتوحة المصدر `,
+ },
+ "landing-heading-subtitle": {
+ en: `DzCode i/o helps you find, contribute and add to the list of open-source projects that solve Algerian problems.`,
+ ar: `يساعدك DzCode i / o في العثور على والمساهمة والإضافة إلى قائمة المشاريع مفتوحة المصدر التي تحل مشاكل الجزائر.`,
+ },
+ "landing-cta-button": {
+ en: "Make a Contribution",
+ ar: "قدم مساهمة",
+ },
+ "landing-help-button": {
+ en: "Do you have a question?",
+ ar: "هل لديك سؤال؟",
+ },
+ "landing-mobile-title": {
+ en: "Try the mobile app from AppStore or PlayStore",
+ ar: "جرب تطبيق الهاتف المحمول من AppStore أو PlayStore",
+ },
+ "landing-mobile-subtitle": {
+ 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-error": {
+ en: "Oops, an error occurred while loading the articles list, please try again...",
+ ar: "عفوًا ، حدث خطأ أثناء تحميل قائمة المقالات ، يرجى المحاولة مرة أخرى ...",
+ },
+ "team-try-again": {
+ en: "Try Again",
+ ar: "حاول مرة أخري",
+ },
+ "team-title": {
+ en: "Get to know our team 💻",
+ ar: "تعرف على فريقنا 💻",
+ },
+ "team-card-cta-button": {
+ en: "Contributions",
+ ar: "المساهمات",
+ },
+ "team-card-repositories": {
+ en: "Repositories",
+ ar: "مستودعات",
+ },
+ "projects-error": {
+ en: "Oops, an error occurred while loading the projects list, please try again...",
+ ar: "عفوًا ، حدث خطأ أثناء تحميل قائمة المشاريع ، يرجى المحاولة مرة أخرى ...",
+ },
+ "projects-try-again": {
+ en: "Try Again",
+ ar: "حاول مرة أخري",
+ },
+ "projects-title": {
+ en: "Open Source Projects",
+ ar: "مشاريع مفتوحة المصدر",
+ },
+ "projects-card-cta-button": {
+ en: "Go to code",
+ ar: "إلى الكود",
+ },
+ "notfound-subtitle": {
+ en: `Finally someone saw the 404 page Nour built 😄`,
+ ar: `أخيرًا شاهد شخص ما صفحة 404 التي أنشأتها نور 😄`,
+ },
+ "notfound-back-home": {
+ en: "Go Back Home",
+ ar: "ارجع إلى الصفحة الرئيسية",
+ },
+ "contribute-filter-projects": {
+ en: "Project",
+ ar: "المشروع",
+ },
+ "contribute-filter-languages": {
+ en: "Programming Language",
+ ar: "لغة البرمجة",
+ },
+ "contribute-filter-labels": {
+ en: "Label",
+ ar: "الوسم",
+ },
+ "contribute-read-issue": {
+ en: "Learn more",
+ ar: "اقرأ المزيد",
+ },
+ "contribute-review-changes": {
+ en: "Review changes",
+ ar: "مراجعة التغييرات",
+ },
+ "elapsed-time-suffixes": {
+ en: "y|mo|d|h|min|Just now",
+ ar: " عام| شهر| يوم| ساعة| دقيقة| الآن",
+ },
+};
diff --git a/web/src/apps/main/components/t/index.ts b/web/src/apps/main/components/t/index.ts
new file mode 100644
index 000000000..b76766e85
--- /dev/null
+++ b/web/src/apps/main/components/t/index.ts
@@ -0,0 +1,14 @@
+import {
+ translationFactory,
+ translationFunctionFactory,
+} from "@dzcode.io/ui/dist/translation-factory";
+
+import { mainStore } from "../../redux";
+import { dictionary } from "./dictionary";
+
+export const T = translationFactory(dictionary, () => mainStore.getState().settings.language.code);
+
+export const t = translationFunctionFactory(
+ dictionary,
+ () => mainStore.getState().settings.language.code,
+);
diff --git a/web/src/apps/main/entry/app.tsx b/web/src/apps/main/entry/app.tsx
index 7a145f68e..96844fd6b 100644
--- a/web/src/apps/main/entry/app.tsx
+++ b/web/src/apps/main/entry/app.tsx
@@ -3,52 +3,16 @@ import "./style.scss";
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 { defineMessages, useIntl } from "react-intl";
-import { Route, Switch, useLocation } from "react-router-dom";
+import { Route, RouteProps, Switch, useLocation } from "react-router-dom";
import { Footer } from "src/apps/main/components/footer";
import { Navbar } from "src/apps/main/components/navbar";
import { Theme } from "src/apps/main/components/theme";
import { getEnv } from "src/common/utils";
import { Loading } from "src/components/loading";
-import Localization from "src/localization";
-type RouteInterface = {
+interface RouteInterface extends RouteProps {
import: Promise<{ default: ComponentType }>;
- exact?: boolean;
-} & ({ translate?: false; path?: string } | { translate: true; path: Record });
-
-const messages = defineMessages({
- learn: {
- id: "learn.path",
- defaultMessage: "Learn",
- description: "Learn page route",
- },
- projects: {
- id: "projects.path",
- defaultMessage: "Projects",
- description: "Projects page route",
- },
- articles: {
- id: "articles.path",
- defaultMessage: "Articles",
- description: "Articles page route",
- },
- faq: {
- id: "faq.path",
- defaultMessage: "FAQ",
- description: "FAQ page route",
- },
- contribute: {
- id: "contribute.path",
- defaultMessage: "Contribute",
- description: "Contribute page route",
- },
- team: {
- id: "team.path",
- defaultMessage: "Team",
- description: "Team page route",
- },
-});
+}
const routes: RouteInterface[] = [
{
@@ -58,59 +22,32 @@ const routes: RouteInterface[] = [
},
{
import: import("src/apps/main/pages/learn"),
- path: messages.learn,
- translate: true,
+ path: "/Learn",
},
{
import: import("src/apps/main/pages/projects"),
- path: messages.projects,
- translate: true,
+ path: "/Projects",
},
{
import: import("src/apps/main/pages/articles"),
- path: messages.articles,
- translate: true,
+ path: "/Articles",
},
{
import: import("src/apps/main/pages/faq"),
- path: messages.faq,
- translate: true,
+ path: "/FAQ",
},
{
import: import("src/apps/main/pages/contribute"),
- path: messages.contribute,
- translate: true,
+ path: "/Contribute",
},
{
import: import("src/apps/main/pages/team"),
- path: messages.team,
- translate: true,
+ path: "/Team",
},
{
import: import("src/apps/main/pages/not-found"),
},
];
-const Routes: FC = () => {
- const intl = useIntl();
- return (
-
- {routes.map((route, index) => (
- route.import)}
- />
- ))}{" "}
-
- );
-};
export const App: FC = () => {
if (getEnv() !== "development") {
@@ -128,17 +65,25 @@ export const App: FC = () => {
return (
-
-
-
-
- }>
-
-
-
-
-
-
+
+
+
+ }>
+
+ {routes.map((route, index) => (
+ route.import)} />
+ ))}
+
+
+
+
+
);
diff --git a/web/src/apps/main/pages/articles/index.tsx b/web/src/apps/main/pages/articles/index.tsx
index 85d3bcae8..f153f6b77 100644
--- a/web/src/apps/main/pages/articles/index.tsx
+++ b/web/src/apps/main/pages/articles/index.tsx
@@ -29,7 +29,7 @@ export const ArticlesPage: FC = () => {
return (
-
+
{/* Sidebar */}
{sidebarTree === "ERROR" ? (
diff --git a/web/src/apps/main/pages/articles/landing/index.tsx b/web/src/apps/main/pages/articles/landing/index.tsx
index 2e3d614eb..d1450155a 100644
--- a/web/src/apps/main/pages/articles/landing/index.tsx
+++ b/web/src/apps/main/pages/articles/landing/index.tsx
@@ -5,7 +5,6 @@ import Typography from "@material-ui/core/Typography";
import useMediaQuery from "@material-ui/core/useMediaQuery";
import ListIcon from "@material-ui/icons/List";
import { FC } from "react";
-import { FormattedMessage } from "react-intl";
import articlesLanding from "src/apps/main/assets/svg/articles-landing.svg";
const useStyles = makeStyles((theme) => ({
@@ -40,18 +39,10 @@ export const Landing: FC = ({ onShowSidebar }) => {
-
+ {"Welcome to the articles section of Dzcode i/o"}
{md ? (
-
-
-
+
{"👈 Please select from the left sidebar"}
) : (
({
flex: "1",
display: "flex",
flexDirection: "column",
+ direction: "ltr",
},
chip: {
flex: "1",
@@ -43,9 +46,6 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(1),
marginRight: theme.spacing(1),
},
- contribute: {
- marginRight: "auto",
- },
}));
export const Contributions: FC = () => {
@@ -53,6 +53,9 @@ export const Contributions: FC = () => {
const { contributions } = useSelector(
(state) => state.contributePage,
);
+ const {
+ language: { code: languageCode },
+ } = useSelector((state) => state.settings);
const dispatch = useDispatch>();
return (
@@ -104,17 +107,26 @@ export const Contributions: FC = () => {
color: type === "issue" ? "#56d364" : "#a371f7",
}}
>
-
+
- {type === "issue" ? "Read Issue" : "Review Changes"}
+ {type === "issue"
+ ? t("contribute-read-issue")
+ : t("contribute-review-changes")}
- {elapsedTime(updatedAt)}
+
+ {elapsedTime(updatedAt, t("elapsed-time-suffixes"))}
+
{commentsCount > 0 && (
diff --git a/web/src/apps/main/pages/contribute/filters/index.tsx b/web/src/apps/main/pages/contribute/filters/index.tsx
index 1853ba188..4706af215 100644
--- a/web/src/apps/main/pages/contribute/filters/index.tsx
+++ b/web/src/apps/main/pages/contribute/filters/index.tsx
@@ -14,6 +14,8 @@ import SpeedDialIcon from "@material-ui/lab/SpeedDialIcon";
import { FC, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { SpeedDial } from "src/apps/main/components/speed-dial";
+import { t } from "src/apps/main/components/t";
+import type { DictionaryKeys } from "src/apps/main/components/t/dictionary";
import { Dispatch, StateInterface } from "src/apps/main/redux";
import { updateFilterValue } from "src/apps/main/redux/actions/contribute-page";
import { ContributePageState } from "src/apps/main/redux/reducers/contribute-page";
@@ -41,9 +43,11 @@ export const Filters: FC = () => {
const theme = useTheme();
const md = useMediaQuery(theme.breakpoints.up("md"));
const renderFilters = () =>
- filters.map(({ name: filterName, label: filterLabel, options }) => (
+ filters.map(({ name: filterName, options }) => (
- }>{filterLabel}
+ }>
+ {t(`contribute-filter-${filterName}` as DictionaryKeys<"contribute-filter">)}
+
{options.map(({ label: optionLabel, name: optionName, checked }) => (
- An open-source online community of algerian developers, no organization or any political party is involved, only us dz developers.
+ An open-source online community of Algerian developers, collaborating on solving Algerian technical problems.
@@ -100,6 +101,7 @@ exports[`src/pages/landing/index.tsx Render FAQ page 1`] = `
- What are the Goals of DzCode i/o ?
+ What are the goals of DzCode i/o ?
- Articles
+ articles
- ), there are many algerian developers that accumulated lots experiences, dzcode.io gives them the chance to share it with the right audience.
+ ), there are many Algerian developers that accumulated lots of experiences, here comes dzcode.io to give them the chance to share these experiences with the right people.
- Guide new developers to build their career (with detailed
+ Guide new developers to build their careers (with some educational resources and
- documentation
+ articles
- ), and understand the software market, instead of wasting their time on other useless resources.
+ ), and to understand the software market, instead of wasting their time on other resources they won't be needing.
- Fix some common algerian software problems, in form of ready to use open-source
+ Fix some common Algerian software problems, in form of ready-to-use open-source
@@ -186,6 +188,7 @@ exports[`src/pages/landing/index.tsx Render FAQ page 1`] = `
- What benefit will i get from it ?
+ What would I benefit from DzCode i/o?
-
-
- There are couple of benefits, to name few:
-
-
-
- You will meet other algerian developers, experienced and juniors, where you can grow your network!
-
-
- You'll get to experience a professional environment, which is completely different than the one you used to in school.
-
-
- Your contribution will be shown on your Github profile, and that is a big plus in your professional career!
-
-
-
-
-
-
-
-
-
-