mirror of
https://github.com/LemmyNet/joinlemmy-site.git
synced 2024-12-22 19:01:28 +00:00
Adding i18n support. Fixes #6
This commit is contained in:
parent
5c9bf3ae15
commit
5156537d1a
19 changed files with 300 additions and 149 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ node_modules
|
|||
*.log
|
||||
*.swp
|
||||
*.orig
|
||||
src/shared/translations
|
||||
|
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -2,3 +2,7 @@
|
|||
path = lemmy-docs
|
||||
url = https://github.com/lemmynet/lemmy-docs
|
||||
branch = main
|
||||
[submodule "joinlemmy-translations"]
|
||||
path = joinlemmy-translations
|
||||
url = https://github.com/lemmynet/joinlemmy-translations
|
||||
branch = main
|
||||
|
|
68
generate_translations.js
Normal file
68
generate_translations.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
const fs = require("fs");
|
||||
|
||||
const translationDir = "joinlemmy-translations/translations/";
|
||||
const outDir = "src/shared/translations/";
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
fs.readdir(translationDir, (_err, files) => {
|
||||
files.forEach(filename => {
|
||||
const lang = filename.split(".")[0];
|
||||
try {
|
||||
const json = JSON.parse(
|
||||
fs.readFileSync(translationDir + filename, "utf8")
|
||||
);
|
||||
let data = `export const ${lang} = {\n translation: {`;
|
||||
for (const key in json) {
|
||||
if (key in json) {
|
||||
const value = json[key].replace(/"/g, '\\"');
|
||||
data += `\n ${key}: "${value}",`;
|
||||
}
|
||||
}
|
||||
data += "\n },\n};";
|
||||
const target = outDir + lang + ".ts";
|
||||
fs.writeFileSync(target, data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// generate types for i18n keys
|
||||
const baseLanguage = "en";
|
||||
|
||||
fs.readFile(`${translationDir}${baseLanguage}.json`, "utf8", (_, fileStr) => {
|
||||
const keys = Object.keys(JSON.parse(fileStr));
|
||||
|
||||
const data = `import { i18n } from "i18next";
|
||||
|
||||
declare module "i18next" {
|
||||
export type I18nKeys =
|
||||
${keys.map(key => ` | "${key}"`).join("\n")};
|
||||
|
||||
export interface TFunctionTyped {
|
||||
// basic usage
|
||||
<
|
||||
TResult extends TFunctionResult = string,
|
||||
TInterpolationMap extends Record<string, unknown> = StringMap
|
||||
>(
|
||||
key: I18nKeys | I18nKeys[],
|
||||
options?: TOptions<TInterpolationMap> | string
|
||||
): TResult;
|
||||
// overloaded usage
|
||||
<
|
||||
TResult extends TFunctionResult = string,
|
||||
TInterpolationMap extends Record<string, unknown> = StringMap
|
||||
>(
|
||||
key: I18nKeys | I18nKeys[],
|
||||
defaultValue?: string,
|
||||
options?: TOptions<TInterpolationMap> | string
|
||||
): TResult;
|
||||
}
|
||||
|
||||
export interface i18nTyped extends i18n {
|
||||
t: TFunctionTyped;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
fs.writeFileSync(`${outDir}i18next.d.ts`, data);
|
||||
});
|
1
joinlemmy-translations
Submodule
1
joinlemmy-translations
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c6811ee56e38bf38eaa0fb4e9d5b4665f51d92c0
|
|
@ -1 +1 @@
|
|||
Subproject commit 25e104c1cd6f2a2fc5de095314747814d3d0c71d
|
||||
Subproject commit 322a2a730ccd3835bbb1391d47daeab2a1492f32
|
|
@ -7,10 +7,10 @@
|
|||
"build:dev": "webpack --mode=development",
|
||||
"build:prod": "webpack --mode=production",
|
||||
"clean": "yarn run rimraf dist",
|
||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src",
|
||||
"lint": "node generate_translations.js && tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src",
|
||||
"postinstall": "husky install",
|
||||
"prebuild:dev": "yarn clean",
|
||||
"prebuild:prod": "yarn clean",
|
||||
"prebuild:dev": "yarn clean && node generate_translations.js",
|
||||
"prebuild:prod": "yarn clean && node generate_translations.js",
|
||||
"start": "yarn build:dev --watch"
|
||||
},
|
||||
"repository": "https://github.com/LemmyNet/joinlemmy-site",
|
||||
|
@ -23,6 +23,7 @@
|
|||
"inferno-create-element": "^7.4.8",
|
||||
"inferno-helmet": "^5.2.1",
|
||||
"inferno-hydrate": "^7.4.8",
|
||||
"inferno-i18next": "github:nimbusec-oss/inferno-i18next#semver:^7.4.2",
|
||||
"inferno-router": "^7.4.8",
|
||||
"inferno-server": "^7.4.8"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { hydrate } from "inferno-hydrate";
|
||||
import { BrowserRouter } from "inferno-router";
|
||||
import { App } from "../shared/components/app";
|
||||
import { i18n } from "../shared/i18next";
|
||||
|
||||
// Setting the language for js browsers
|
||||
i18n.changeLanguage(navigator.language);
|
||||
|
||||
const wrapper = (
|
||||
<BrowserRouter>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { App } from "../shared/components/app";
|
|||
// import { routes } from "../shared/routes";
|
||||
import process from "process";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { i18n } from "../shared/i18next";
|
||||
|
||||
const server = express();
|
||||
const port = 1234;
|
||||
|
@ -21,6 +22,12 @@ server.get("/*", async (req, res) => {
|
|||
// const activeRoute = routes.find(route => matchPath(req.path, route)) || {};
|
||||
const context = {} as any;
|
||||
|
||||
// Setting the language for non-js browsers
|
||||
let lang = req.headers["accept-language"]
|
||||
? req.headers["accept-language"].split(",")[0]
|
||||
: "en";
|
||||
i18n.changeLanguage(lang);
|
||||
|
||||
const wrapper = (
|
||||
<StaticRouter location={req.url} context={context}>
|
||||
<App />
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Component } from "inferno";
|
||||
import { Route, Switch } from "inferno-router";
|
||||
import { Provider } from "inferno-i18next";
|
||||
import { i18n } from "../i18next";
|
||||
import { routes } from "../routes";
|
||||
import { NoMatch } from "./no-match";
|
||||
import { Symbols } from "./symbols";
|
||||
|
@ -15,20 +17,22 @@ export class App extends Component<any, any> {
|
|||
return (
|
||||
<>
|
||||
<div>
|
||||
<Navbar />
|
||||
<Switch>
|
||||
{routes.map(({ path, exact, component: C, ...rest }) => (
|
||||
<Route
|
||||
key={path}
|
||||
path={path}
|
||||
exact={exact}
|
||||
render={props => <C {...props} {...rest} />}
|
||||
/>
|
||||
))}
|
||||
<Route render={props => <NoMatch {...props} />} />
|
||||
</Switch>
|
||||
<Footer />
|
||||
<Symbols />
|
||||
<Provider i18next={i18n}>
|
||||
<Navbar />
|
||||
<Switch>
|
||||
{routes.map(({ path, exact, component: C, ...rest }) => (
|
||||
<Route
|
||||
key={path}
|
||||
path={path}
|
||||
exact={exact}
|
||||
render={props => <C {...props} {...rest} />}
|
||||
/>
|
||||
))}
|
||||
<Route render={props => <NoMatch {...props} />} />
|
||||
</Switch>
|
||||
<Footer />
|
||||
<Symbols />
|
||||
</Provider>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Component } from "inferno";
|
||||
import { AppDetails } from "./app-details";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { i18n } from "../i18next";
|
||||
|
||||
const title = "Lemmy - Apps and Libraries";
|
||||
const title = i18n.t("apps_title");
|
||||
|
||||
export class Apps extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -15,8 +16,8 @@ export class Apps extends Component<any, any> {
|
|||
<meta property={"title"} content={title} />
|
||||
</Helmet>
|
||||
<div class="container">
|
||||
<h1>Lemmy Apps</h1>
|
||||
<p>Choose from any of the apps below.</p>
|
||||
<h1>{i18n.t("lemmy_apps")}</h1>
|
||||
<p>{i18n.t("choose_from_apps")}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="card col-6">
|
||||
|
@ -66,7 +67,7 @@ export class Apps extends Component<any, any> {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Web Apps</h1>
|
||||
<h1>{i18n.t("web_apps")}</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="card col-6">
|
||||
|
@ -100,7 +101,7 @@ export class Apps extends Component<any, any> {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Lemmy API Libraries</h1>
|
||||
<h1>{i18n.t("api_libraries")}</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/LemmyNet/lemmy-js-client">
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Component } from "inferno";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { i18n } from "../i18next";
|
||||
|
||||
const title = "Lemmy - Contact";
|
||||
const title = i18n.t("contact_title");
|
||||
|
||||
export class Contact extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -14,8 +15,7 @@ export class Contact extends Component<any, any> {
|
|||
<meta property={"title"} content={title} />
|
||||
</Helmet>
|
||||
<div class="container">
|
||||
<h1>Contact</h1>
|
||||
|
||||
<h1>{i18n.t("contact")}</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://mastodon.social/@LemmyDev">Mastodon</a>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { Component } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import { i18n } from "../i18next";
|
||||
import { T } from "inferno-i18next";
|
||||
|
||||
export class DonateLines extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -8,20 +11,19 @@ export class DonateLines extends Component<any, any> {
|
|||
return (
|
||||
<>
|
||||
<p>
|
||||
Lemmy is free, open-source software, meaning no advertising,
|
||||
monetizing, or venture capital, ever.{" "}
|
||||
<a href="/sponsors">Your donations</a> directly support full-time
|
||||
development of the project.
|
||||
<T i18nKey="donate_desc">
|
||||
#<Link to="/sponsors">#</Link>#
|
||||
</T>
|
||||
</p>
|
||||
<div class="row is-horizontal-align">
|
||||
<div class="col-3">
|
||||
<a class="button primary" href="https://liberapay.com/Lemmy">
|
||||
Support on Liberapay
|
||||
{i18n.t("support_on_liberapay")}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<a class="button primary" href="https://www.patreon.com/dessalines">
|
||||
Support on Patreon
|
||||
{i18n.t("support_on_patreon")}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
|
@ -29,7 +31,7 @@ export class DonateLines extends Component<any, any> {
|
|||
class="col button primary"
|
||||
href="https://opencollective.com/lemmy"
|
||||
>
|
||||
Support on OpenCollective
|
||||
{i18n.t("support_on_opencollective")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component } from "inferno";
|
||||
import { LinkLine } from "./link-line";
|
||||
import { T } from "inferno-i18next";
|
||||
|
||||
export class Footer extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -13,17 +14,18 @@ export class Footer extends Component<any, any> {
|
|||
<nav class="nav">
|
||||
<div class="nav-left">
|
||||
<p style="padding-left: 2rem">
|
||||
Made with
|
||||
<a style="display: inline-block" href="https://infernojs.org">
|
||||
Inferno
|
||||
</a>
|
||||
and
|
||||
<a
|
||||
style="display: inline-block"
|
||||
href="https://jenil.github.io/chota"
|
||||
>
|
||||
Chota
|
||||
</a>
|
||||
<T i18nKey="footer_desc">
|
||||
#
|
||||
<a style="display: inline-block" href="https://infernojs.org">
|
||||
#
|
||||
</a>
|
||||
<a
|
||||
style="display: inline-block"
|
||||
href="https://jenil.github.io/chota"
|
||||
>
|
||||
#
|
||||
</a>
|
||||
</T>
|
||||
</p>
|
||||
</div>
|
||||
<div class="nav-right hide-sm hide-md hide-lg">
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Component } from "inferno";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { i18n } from "../i18next";
|
||||
|
||||
const title = "Lemmy - Join a Server";
|
||||
const title = i18n.t("join_title");
|
||||
|
||||
// TODO wait until new lemmy-instances is written to refactor this
|
||||
|
||||
|
@ -16,8 +17,8 @@ export class Join extends Component<any, any> {
|
|||
<meta property={"title"} content={title} />
|
||||
</Helmet>
|
||||
<div class="container">
|
||||
<h1>Lemmy servers</h1>
|
||||
<p>Choose and join a server from the approved servers below.</p>
|
||||
<h1>{i18n.t("lemmy_servers")}</h1>
|
||||
<p>{i18n.t("choose_and_join")}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="card col-6">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import { i18n } from "../i18next";
|
||||
|
||||
export class LinkLine extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -8,14 +9,17 @@ export class LinkLine extends Component<any, any> {
|
|||
render() {
|
||||
return (
|
||||
<>
|
||||
<Link to="/join">Join</Link>
|
||||
<Link to="/apps">Apps</Link>
|
||||
<Link to="/sponsors">Sponsors</Link>
|
||||
<a href="/docs/en/index.html">Docs</a>
|
||||
<a href="/docs/en/code_of_conduct.html" title="Code of Conduct">
|
||||
CoC
|
||||
<Link to="/join">{i18n.t("join")}</Link>
|
||||
<Link to="/apps">{i18n.t("apps")}</Link>
|
||||
<Link to="/sponsors">{i18n.t("sponsors")}</Link>
|
||||
<a href="/docs/en/index.html">{i18n.t("docs")}</a>
|
||||
<a
|
||||
href="/docs/en/code_of_conduct.html"
|
||||
title={i18n.t("code_of_conduct")}
|
||||
>
|
||||
{i18n.t("coc")}
|
||||
</a>
|
||||
<Link to="/contact">Contact</Link>
|
||||
<Link to="/contact">{i18n.t("contact")}</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ import { Component } from "inferno";
|
|||
import { Link } from "inferno-router";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { DonateLines } from "./donate-lines";
|
||||
import { i18n } from "../i18next";
|
||||
import { T } from "inferno-i18next";
|
||||
|
||||
const title = "Lemmy - A link aggregator for the fediverse";
|
||||
const title = i18n.t("lemmy_title");
|
||||
|
||||
export class Main extends Component<any, any> {
|
||||
constructor(props: any, context: any) {
|
||||
|
@ -13,7 +15,7 @@ export class Main extends Component<any, any> {
|
|||
joinServer() {
|
||||
return (
|
||||
<Link className="button primary" to="/join">
|
||||
Join a Server
|
||||
{i18n.t("join_a_server")}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
@ -24,7 +26,7 @@ export class Main extends Component<any, any> {
|
|||
class="button primary"
|
||||
href="/docs/en/administration/administration.html"
|
||||
>
|
||||
Run a Server
|
||||
{i18n.t("run_a_server")}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -38,8 +40,8 @@ export class Main extends Component<any, any> {
|
|||
<div class="bg-image"></div>
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h1 class="stylized">Lemmy</h1>
|
||||
<h4>A link aggregator for the fediverse.</h4>
|
||||
<h1 class="stylized">{i18n.t("lemmy")}</h1>
|
||||
<h4>{i18n.t("lemmy_desc")}</h4>
|
||||
<div class="row is-horizontal-align">
|
||||
<div class="col-2-lg">{this.joinServer()}</div>
|
||||
<div class="col-2-lg">{this.runServer()}</div>
|
||||
|
@ -51,18 +53,15 @@ export class Main extends Component<any, any> {
|
|||
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h2>Follow communities anywhere in the world</h2>
|
||||
<h2>{i18n.t("follow_communities")}</h2>
|
||||
<p>
|
||||
<a href="https://github.com/LemmyNet">Lemmy</a> is similar to
|
||||
sites like <a href="https://reddit.com">Reddit</a>,{" "}
|
||||
<a href="https://lobste.rs">Lobste.rs</a>, or{" "}
|
||||
<a href="https://news.ycombinator.com/">Hacker News</a>: you
|
||||
subscribe to communities you're interested in, post links and
|
||||
discussions, then vote and comment on them. Lemmy isn't just a
|
||||
reddit alternative; its a network of interconnected communities
|
||||
ran by different people and organizations, all combining to create{" "}
|
||||
<b>a single, personalized front page</b> of your favorite news,
|
||||
articles, and memes.{" "}
|
||||
<T i18nKey="lemmy_long_desc">
|
||||
#<a href="https://github.com/LemmyNet">#</a>
|
||||
<a href="https://reddit.com">#</a>
|
||||
<a href="https://lobste.rs">#</a>
|
||||
<a href="https://news.ycombinator.com/">#</a>
|
||||
<b>#</b>
|
||||
</T>
|
||||
</p>
|
||||
<p>{this.joinServer()}</p>
|
||||
</div>
|
||||
|
@ -83,18 +82,15 @@ export class Main extends Component<any, any> {
|
|||
/>
|
||||
</header>
|
||||
<br />
|
||||
<h4 class="text-center">Open Source</h4>
|
||||
<h4 class="text-center">{i18n.t("open_source")}</h4>
|
||||
<p>
|
||||
Lemmy is and will always remain free,{" "}
|
||||
<a href="https://github.com/LemmyNet">open source</a>{" "}
|
||||
software, using the strong{" "}
|
||||
<a href="https://en.wikipedia.org/wiki/Copyleft">
|
||||
copyleft
|
||||
</a>{" "}
|
||||
<a href="https://github.com/LemmyNet/lemmy/blob/master/LICENSE">
|
||||
AGPL License
|
||||
</a>
|
||||
.
|
||||
<T i18nKey="open_source_desc">
|
||||
#<a href="https://github.com/LemmyNet">#</a>
|
||||
<a href="https://en.wikipedia.org/wiki/Copyleft">#</a>
|
||||
<a href="https://github.com/LemmyNet/lemmy/blob/master/LICENSE">
|
||||
#
|
||||
</a>
|
||||
</T>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -107,14 +103,15 @@ export class Main extends Component<any, any> {
|
|||
/>
|
||||
</header>
|
||||
<br />
|
||||
<h4 class="text-center">Blazing Fast</h4>
|
||||
<h4 class="text-center">{i18n.t("blazing_fast")}</h4>
|
||||
<p>
|
||||
Made using some of the fastest frameworks and tools,
|
||||
including <a href="https://www.rust-lang.org">Rust</a>,{" "}
|
||||
<a href="https://actix.rs/">Actix</a>,{" "}
|
||||
<a href="http://diesel.rs/">Diesel</a>,{" "}
|
||||
<a href="https://infernojs.org">Inferno</a>, and{" "}
|
||||
<a href="https://www.typescriptlang.org/">Typescript</a>.
|
||||
<T i18nKey="blazing_fast_desc">
|
||||
#<a href="https://www.rust-lang.org">#</a>
|
||||
<a href="https://actix.rs/">#</a>
|
||||
<a href="http://diesel.rs/">#</a>
|
||||
<a href="https://infernojs.org">#</a>
|
||||
<a href="https://www.typescriptlang.org/">#</a>
|
||||
</T>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,12 +124,8 @@ export class Main extends Component<any, any> {
|
|||
/>
|
||||
</header>
|
||||
<br />
|
||||
<h4 class="text-center">Powerful Mod Tools</h4>
|
||||
<p>
|
||||
Each server can set its own moderation policy, to help
|
||||
foster a healthy environment where all can feel comfortable
|
||||
contributing.
|
||||
</p>
|
||||
<h4 class="text-center">{i18n.t("mod_tools")}</h4>
|
||||
<p>{i18n.t("mod_tools_desc")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -143,18 +136,13 @@ export class Main extends Component<any, any> {
|
|||
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h2>Create your own discussion platform</h2>
|
||||
<h2>{i18n.t("create_discussion_platform")}</h2>
|
||||
<p>
|
||||
With Lemmy, you can{" "}
|
||||
<a href="/docs/en/administration/administration.html">
|
||||
easily host your own server
|
||||
</a>
|
||||
, and all these servers are <i>federated</i> (think email), and
|
||||
connected to the same universe, called the{" "}
|
||||
<a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a>.
|
||||
For a link aggregator, this means that someone registered on one
|
||||
server can subscribe to communities elsewhere, and can have
|
||||
discussions with people on a completely different server.
|
||||
<T i18nKey="create_discussion_platform_desc">
|
||||
#<a href="/docs/en/administration/administration.html">#</a>
|
||||
<i>#</i>
|
||||
<a href="https://en.wikipedia.org/wiki/Fediverse">#</a>
|
||||
</T>
|
||||
</p>
|
||||
<p>{this.runServer()}</p>
|
||||
</div>
|
||||
|
@ -167,11 +155,8 @@ export class Main extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4>Live Updates</h4>
|
||||
<p>
|
||||
New comments and posts stream in to your front page and inbox;
|
||||
No more page refreshes required.
|
||||
</p>
|
||||
<h4>{i18n.t("live_updates")}</h4>
|
||||
<p>{i18n.t("live_updates_desc")}</p>
|
||||
</div>
|
||||
<div class="col-6 is-center">
|
||||
<video height={325} autoPlay loop>
|
||||
|
@ -190,48 +175,54 @@ export class Main extends Component<any, any> {
|
|||
<img height={325} src="/static/assets/images/mobile_pic.webp" />
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h4 class="is-marginless">More Features</h4>
|
||||
<h4 class="is-marginless">{i18n.t("more_features")}</h4>
|
||||
<ul class="is-marginless">
|
||||
<li>
|
||||
Self hostable, easy to deploy, via{" "}
|
||||
<a href="/docs/en/administration/install_docker.html">
|
||||
Docker
|
||||
</a>
|
||||
, or{" "}
|
||||
<a href="/docs/en/administration/install_ansible.html">
|
||||
Ansible
|
||||
</a>
|
||||
.
|
||||
<T i18nKey="self_hostable">
|
||||
#<a href="/docs/en/administration/install_docker.html">#</a>
|
||||
<a href="/docs/en/administration/install_ansible.html">#</a>
|
||||
</T>
|
||||
</li>
|
||||
<li>Clean, mobile-friendly interface.</li>
|
||||
<li>User avatar support.</li>
|
||||
<li>{i18n.t("clean_interface")}</li>
|
||||
<li>{i18n.t("avatar_support")}</li>
|
||||
<li>
|
||||
Full vote scores <code>(+/-)</code> like old Reddit.
|
||||
<T i18nKey="full_vote_scores">
|
||||
#<code>#</code>#
|
||||
</T>
|
||||
</li>
|
||||
<li>Themes, including light, dark, and solarized.</li>
|
||||
<li>{i18n.t("themes_including")}</li>
|
||||
<li>
|
||||
Emojis with autocomplete support. Start typing <code>:</code>
|
||||
<T i18nKey="emojis_autocomplete">
|
||||
#<code>#</code>
|
||||
</T>
|
||||
</li>
|
||||
<li>
|
||||
User tagging using <code>@</code>, Community tagging using{" "}
|
||||
<code>!</code>.
|
||||
<T i18nKey="user_tagging">
|
||||
#<code>#</code>
|
||||
<code>#</code>
|
||||
</T>
|
||||
</li>
|
||||
<li>Integrated image uploading in both posts and comments.</li>
|
||||
<li>Notifications, including via email.</li>
|
||||
<li>{i18n.t("integrated_image_uploading")}</li>
|
||||
<li>{i18n.t("notifications_including")}</li>
|
||||
<li>
|
||||
<a href="https://weblate.yerbamate.ml/projects/lemmy/lemmy/">
|
||||
i18n / internationalization support for > 30 languages.
|
||||
</a>
|
||||
<T i18nKey="i18n_support">
|
||||
#
|
||||
<a href="https://weblate.yerbamate.ml/projects/lemmy/lemmy/">
|
||||
#
|
||||
</a>
|
||||
</T>
|
||||
</li>
|
||||
<li>
|
||||
RSS / Atom feeds for <code>All</code>, <code>Subscribed</code>
|
||||
, <code>Inbox</code>, <code>User</code>, and{" "}
|
||||
<code>Community</code>.
|
||||
<T i18nKey="rss_feeds">
|
||||
#<code>#</code>
|
||||
<code>#</code>
|
||||
<code>#</code>
|
||||
<code>#</code>
|
||||
<code>#</code>
|
||||
</T>
|
||||
</li>
|
||||
<li>
|
||||
Can fully erase your data, replacing all posts and comments.
|
||||
</li>
|
||||
<li>NSFW post / community support.</li>
|
||||
<li>{i18n.t("can_fully_erase")}</li>
|
||||
<li>{i18n.t("nsfw_support")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -243,7 +234,7 @@ export class Main extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h2>
|
||||
<a href="/sponsors">Support / Donate</a>
|
||||
<Link to="/sponsors">{i18n.t("support_donate")}</Link>
|
||||
</h2>
|
||||
<DonateLines />
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Component } from "inferno";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { DonateLines } from "./donate-lines";
|
||||
import { i18n } from "../i18next";
|
||||
|
||||
const title = "Lemmy - Sponsors";
|
||||
const title = i18n.t("sponsors_title");
|
||||
|
||||
interface LinkedSponsor {
|
||||
name: string;
|
||||
|
@ -40,15 +41,15 @@ export class Sponsors extends Component<any, any> {
|
|||
</Helmet>
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h1>Donate to Lemmy</h1>
|
||||
<h1>{i18n.t("donate_to_lemmy")}</h1>
|
||||
<DonateLines />
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="text-center">
|
||||
<h2>Sponsors</h2>
|
||||
<p>Silver Sponsors are those that pledged $40 to Lemmy.</p>
|
||||
<h2>{i18n.t("sponsors")}</h2>
|
||||
<p>{i18n.t("silver_sponsors_desc")}</p>
|
||||
<div class="row is-horizontal-align">
|
||||
{silverSponsors.map(s => (
|
||||
<div class="col">
|
||||
|
@ -59,7 +60,7 @@ export class Sponsors extends Component<any, any> {
|
|||
))}
|
||||
</div>
|
||||
<br />
|
||||
<p>General Sponsors are those that pledged $10 to $39 to Lemmy.</p>
|
||||
<p>{i18n.t("general_sponsors_desc")}</p>
|
||||
<div class="row is-horizontal-align">
|
||||
{highlightedSponsors.map(s => (
|
||||
<div class="col">
|
||||
|
|
29
src/shared/i18next.ts
Normal file
29
src/shared/i18next.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import i18next, { i18nTyped } from "i18next";
|
||||
|
||||
// Languages
|
||||
import { en } from "./translations/en";
|
||||
import { ru } from "./translations/ru";
|
||||
|
||||
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
|
||||
const resources = {
|
||||
en,
|
||||
ru,
|
||||
};
|
||||
|
||||
function format(value: any, format: any): any {
|
||||
return format === "uppercase" ? value.toUpperCase() : value;
|
||||
}
|
||||
|
||||
i18next.init({
|
||||
debug: false,
|
||||
// load: 'languageOnly',
|
||||
// initImmediate: false,
|
||||
lng: "en", // This is changed later
|
||||
fallbackLng: "en",
|
||||
resources,
|
||||
interpolation: { format },
|
||||
});
|
||||
|
||||
export const i18n = i18next as i18nTyped;
|
||||
|
||||
export { resources };
|
38
yarn.lock
38
yarn.lock
|
@ -3872,6 +3872,13 @@ html-entities@^1.3.1:
|
|||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
|
||||
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
|
||||
|
||||
html-parse-stringify2@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a"
|
||||
integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=
|
||||
dependencies:
|
||||
void-elements "^2.0.1"
|
||||
|
||||
http-cache-semantics@^3.8.0, http-cache-semantics@^3.8.1:
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
|
||||
|
@ -4089,7 +4096,14 @@ infer-owner@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
|
||||
integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
|
||||
|
||||
inferno-create-element@^7.4.8:
|
||||
inferno-clone-vnode@^7.4.2:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno-clone-vnode/-/inferno-clone-vnode-7.4.8.tgz#fafdb21f86e566f662da7e58ff9be1fc76baff08"
|
||||
integrity sha512-Un9Saio4TJ+1DFehPd7JuNDJV2Fy2aPhK5hD4zmG+JWLUIphi/nuhbwrlsCNmTTDmOsB7GrqyrXFvOUEKX4YpA==
|
||||
dependencies:
|
||||
inferno "7.4.8"
|
||||
|
||||
inferno-create-element@^7.4.2, inferno-create-element@^7.4.8:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno-create-element/-/inferno-create-element-7.4.8.tgz#77bbf24288645c359cf65b4821a3938c6537eb5e"
|
||||
integrity sha512-hCkA+RAiqoeWlmmCrb3VIUDV+4lEeLDCI98RcB4HqzAJwjH8dMR4ZeDQO3f9crygPnmSW7r1L0Ykjf0O2oHYFQ==
|
||||
|
@ -4112,6 +4126,17 @@ inferno-hydrate@^7.4.8:
|
|||
dependencies:
|
||||
inferno "7.4.8"
|
||||
|
||||
"inferno-i18next@github:nimbusec-oss/inferno-i18next#semver:^7.4.2":
|
||||
version "7.4.2"
|
||||
resolved "https://codeload.github.com/nimbusec-oss/inferno-i18next/tar.gz/54b9be591ccd62c53799ad23e35f17144a62f909"
|
||||
dependencies:
|
||||
html-parse-stringify2 "^2.0.1"
|
||||
inferno "^7.4.2"
|
||||
inferno-clone-vnode "^7.4.2"
|
||||
inferno-create-element "^7.4.2"
|
||||
inferno-shared "^7.4.2"
|
||||
inferno-vnode-flags "^7.4.2"
|
||||
|
||||
inferno-router@^7.4.8:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.4.8.tgz#7268f4ad0e5e3f1ca6df272d80d59594c33759ff"
|
||||
|
@ -4129,7 +4154,7 @@ inferno-server@^7.4.8:
|
|||
dependencies:
|
||||
inferno "7.4.8"
|
||||
|
||||
inferno-shared@7.4.8:
|
||||
inferno-shared@7.4.8, inferno-shared@^7.4.2:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.4.8.tgz#2b554a36683b770339008749096d9704846dd337"
|
||||
integrity sha512-I0jnqsBcQvGJ7hqZF3vEzspQ80evViCe8joP3snWkPXPElk9WBVGLBHX5tHwuFuXv6wW4zeVVA4kBRAs47B+NQ==
|
||||
|
@ -4143,12 +4168,12 @@ inferno-side-effect@^1.1.5:
|
|||
npm "^5.8.0"
|
||||
shallowequal "^1.0.1"
|
||||
|
||||
inferno-vnode-flags@7.4.8:
|
||||
inferno-vnode-flags@7.4.8, inferno-vnode-flags@^7.4.2:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.4.8.tgz#275d70e3c8b2b3f4eb56041cc9b8c832ce1fb26d"
|
||||
integrity sha512-wOUeO7Aho8VH+s2V2K/53KwS0DtQFgT7TdzPE/s6P26ZIxQj+vt7oTJqzXn+xjRIjnfkTLm2eQ8qfInOWCu1rw==
|
||||
|
||||
inferno@7.4.8, inferno@^7.4.8:
|
||||
inferno@7.4.8, inferno@^7.4.2, inferno@^7.4.8:
|
||||
version "7.4.8"
|
||||
resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.4.8.tgz#0d5504753e79903b0e4bbeff76fc11fd0b9ffe92"
|
||||
integrity sha512-4XwGe5Kd0QkSaM/jqAQWjM0GfDLv+KujcWm5zbIow80G1tOEnZurQqhyF8u6m/HX3SnrCi+njlVdtPKDJXQiDw==
|
||||
|
@ -8296,6 +8321,11 @@ verror@1.10.0:
|
|||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
void-elements@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
watchpack@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7"
|
||||
|
|
Loading…
Reference in a new issue