diff --git a/README.md b/README.md index 6606dd7..379c7c1 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,15 @@ Language files are named with [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag). Short -tags (e.g. `en` instead of `en-GB`) are preferred. The library will -automatically detect the language from your browser to use, but for the sake of -testing, you can override it by running the following command in the developer -console (Ctrl+Shift+I): +tags (e.g. `en` instead of `en-GB`) are preferred. + +**IMPORTANT:** If you are adding a new language, you **must** add it to +[`src/utils/i18n.ts`](src/utils/i18n.ts) in `supportedLngs`. Be sure to add +yourself to [`translators.json`](translators.json) too! + +The library will automatically detect the language from your browser to use, but +for the sake of testing, you can override it by running the following command in +the developer console (Ctrl+Shift+I): ```js localStorage.i18nextLng = "en"; diff --git a/package.json b/package.json index 320dc11..0bfa697 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "defaultSyncNode": "https://krist.ceriat.net", "supportURL": "https://donate.lemmmy.pw", "supportersURL": "https://donate.lemmmy.pw/supporters.json", + "translateURL": "https://github.com/tmpim/KristWeb2/blob/master/README.md#contributing-translations", "private": true, "dependencies": { "@craco/craco": "^5.6.4", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index bdeccd1..f98fd40 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -85,6 +85,9 @@ "madeBy": "Made by <1>{{authorName}}", "supportersTitle": "Supporters", "supportersDescription": "This project was made possible by the following amazing supporters:", - "supportButton": "Support KristWeb" + "supportButton": "Support KristWeb", + "translatorsTitle": "Translators", + "translatorsDescription": "This project was translated by the following amazing contributors:", + "translateButton": "Translate KristWeb" } } diff --git a/src/layouts/credits/Supporters.tsx b/src/layouts/credits/Supporters.tsx new file mode 100644 index 0000000..9033253 --- /dev/null +++ b/src/layouts/credits/Supporters.tsx @@ -0,0 +1,88 @@ +import React, { Component, ReactNode } from "react"; + +import { withTranslation, WithTranslation } from "react-i18next"; + +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Button from "react-bootstrap/Button"; + +import packageJson from "@/package.json"; + +interface Supporter { + name: string; + url?: string; +}; + +interface State { + isLoaded: boolean; + supporters: Supporter[] | null; +}; + +class SupportersComponent extends Component { + constructor(props: WithTranslation) { + super(props); + + this.state = { + isLoaded: false, + supporters: null + }; + } + + componentDidMount(): void { + const { supportersURL } = packageJson; + if (!supportersURL) return; + + fetch(supportersURL) + .then(res => res.json()) + .then(result => this.setState({ + isLoaded: true, + supporters: result.supporters + })) + .catch(() => this.setState({ isLoaded: true })); + } + + render(): ReactNode { + const supportURL = packageJson.supportURL; + if (!supportURL) return null; + + const { t } = this.props; + const { isLoaded, supporters } = this.state; + + return <> + + +

{t("credits.supportersTitle")}

+

{t("credits.supportersDescription")}

+ +
+ {/* Supporter list */} + + {isLoaded && supporters !== null + ? + : {t("loading")} + } + + + {/* Support button */} + + + + + ; + } +} + +export const Supporters = withTranslation()(SupportersComponent); + +interface SupportersListProps { + supporters: Supporter[]; +} + +const SupportersList: React.FC = ({ supporters }: SupportersListProps) => + (<>{supporters.map(({ url, name }) => ( + url + ? {name} + : {name} + ))}); diff --git a/src/layouts/credits/Translators.tsx b/src/layouts/credits/Translators.tsx new file mode 100644 index 0000000..d8d87e9 --- /dev/null +++ b/src/layouts/credits/Translators.tsx @@ -0,0 +1,65 @@ +import React, { Component, ReactNode } from "react"; + +import { withTranslation, WithTranslation } from "react-i18next"; + +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Button from "react-bootstrap/Button"; + +import packageJson from "@/package.json"; + +// Find translators.json +const req = require.context("@/", false, /\.\/translators.json$/); + +interface Translator { + name: string; + url?: string; +}; + +export class TranslatorsComponent extends Component { + render(): ReactNode { + const { t } = this.props; + + const translateURL = packageJson.translateURL; + if (!translateURL) return null; + + // Get the translator information from translators.json + if (!req.keys().includes("./translators.json")) return null; + const translators: { [key: string]: Translator[] } = req("./translators.json"); + + return <> + + +

{t("credits.translatorsTitle")}

+

{t("credits.translatorsDescription")}

+ +
+ {/* Translator list */} + +
    + {Object.entries(translators).map(([language, people]) => +
  • + {language} + + {people.map(({ url, name }: Translator) => + url + ? {name} + : {name} + )} +
  • + )} +
+ +
+ {/* Translate button */} + + + + + ; + } +} + +export const Translators = withTranslation()(TranslatorsComponent); diff --git a/src/layouts/credits/index.tsx b/src/layouts/credits/index.tsx index 961613b..fafdb68 100644 --- a/src/layouts/credits/index.tsx +++ b/src/layouts/credits/index.tsx @@ -5,52 +5,21 @@ import Container from "react-bootstrap/Container"; import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; -import Button from "react-bootstrap/Button"; + +import { Supporters } from "./Supporters"; +import { Translators } from "./Translators"; import packageJson from "@/package.json"; -interface Supporter { - name: string; - url?: string; -}; - -interface CreditsState { - isLoaded: boolean; - supporters: Supporter[] | null; -}; - -class CreditsPageComponent extends Component { - constructor(props: WithTranslation) { - super(props); - - this.state = { - isLoaded: false, - supporters: null - }; - } - - componentDidMount(): void { - const { supportersURL } = packageJson; - if (!supportersURL) return; - - fetch(supportersURL) - .then(res => res.json()) - .then(result => this.setState({ - isLoaded: true, - supporters: result.supporters - })) - .catch(() => this.setState({ isLoaded: true })); - } - +class CreditsPageComponent extends Component { render(): ReactNode { const { t } = this.props; - const { isLoaded, supporters } = this.state; const authorName = packageJson.author || "Lemmmy"; const authorURL = `https://github.com/${authorName}`; - const supportURL = packageJson.supportURL; return + {/* Main section */}

KristWeb v2

@@ -61,45 +30,18 @@

+
- {supportURL && /* Supporters section */ - <> - - -

{t("credits.supportersTitle")}

-

{t("credits.supportersDescription")}

- -
- {/* Supporter list */} - - {isLoaded && supporters !== null - ? - : {t("loading")} - } - - - {/* Support button */} - - - - - - } + + {/* Supporters section */} + + +
+ + {/* Translators section */} +
; } } export const CreditsPage = withTranslation()(CreditsPageComponent); - -interface SupportersProps { - supporters: Supporter[]; -} - -export const Supporters: React.FC = ({ supporters }: SupportersProps) => - (<>{supporters.map(({ url, name }) => ( - url - ? {name} - : {name} - ))}); diff --git a/translators.json b/translators.json new file mode 100644 index 0000000..6148ace --- /dev/null +++ b/translators.json @@ -0,0 +1,8 @@ +{ + "German (Deutsch)": [ + { + "name": "Lignum", + "url": "https://github.com/Lignum" + } + ] +}